|
50866
|
1795
|
54
|
2026-05-18T08:07:32.519070+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091652519_m1.jpg...
|
PhpStorm
|
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Commit and Push…
⌥⌘K
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Commit and Push…","depth":5,"bounds":{"left":0.0,"top":0.0,"width":0.08263889,"height":0.024444444},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"⌥⌘K","depth":5,"bounds":{"left":0.0,"top":0.0,"width":0.04236111,"height":0.024444444},"on_screen":false,"role_description":"text"}]...
|
2535035496832215689
|
306252506011271184
|
visual_change
|
hybrid
|
NULL
|
Commit and Push…
⌥⌘K
iTerm2ShellEditViewSessionS Commit and Push…
⌥⌘K
iTerm2ShellEditViewSessionScriptsProfilesWindowHelp> 0.Preparation for Refi…. in 3h 53 m100% <78• Mon 18 May 11:07:32DOCKER (docker-compose)О ₴2181DOCKER81DEV (-zsh)X T1 DOCKER (docker-compose)docker_lamp_12026-05-18 08:05:55 Running ['artisan'conference:pre-meeting-reminder] in backgrounddocker_lamp_1l ('/usr/local/bin/php' 'artisan' conference:pre-meeting-reminder ›'/proc/1/fd/1''/usr/local/bin/php' 'artisan' schedule:finish"framework/schedule-805efb160ee8d9da02e60364ace7970eb2b35f31" "$?") › '/dev/null' 2>&1 &docker_lamp_12026-05-18 08:05:55 Running ['artisan' hubspot: journal-poll --start]in background3.21ms DONEdocker_lamp_1" ('/usr/local/bin/php' 'artisan' hubspot:journal-poll--start > '/proc/1/fd/1' 2>&1 ; '/usr/local/bin/php'schedule:finish "framework/schedule-e26d77f915d2c55fe91ca4148a230e32eaa1865e" "$?") ›'/dev/null' 2>&1 &docker_lamp_12026-05-18 08:05:55 Running['artisan' crm:bullhorn:ping --heartbeat]account(s) to be processeddocker_lamp_1docker_1amp_1docker_lamp_1docker_lamp_11 Done!4s DONE* Starting HubSpot journal polling service...docker_lamp_11 '/usr/local/bin/php' 'artisan' crm:bullhorn:ping--heartbeat › '/proc/1/fd/1' 2>&1docker_lamp_1docker_lamp_1run_artisan_schedule: Done waiting for schedule:rundocker_lamp_1docker_lamp_12026-05-18 08:07:05 Running ['artisan'meeting-bot:schedule-bot]...6s DONEdocker_1amp_1, '/usr/local/bin/php' 'artisan' meeting-bot:schedule-bot › */proc/1/fd/1'2>&1docker_lamp_12026-05-18 08:07:12 Running ['artisan' dialers:monitor-activities].3S DONEdocker_lamp_1l '/usr/local/bin/php' 'artisan' dialers:monitor-activities > "/proc/1/fd/1'2>&1docker_lamp_12026-05-18 08:07:15 Running ['artisan' jiminny:monitor-social-accountSJ3s DONEdocker_lamp_1l '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > '/proc/1/fd/1'2>&1docker_1amp_12026-05-18 08:07:19 Running ['artisan'mailbox:skip-lists:refresh]3sDONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'mailbox:skip-lists:refresh > */proc/1/fd/1'docker_lamp_12026-05-18 08:07:22 Running ['artisan' mailbox:batch:process --max-batches=15]6sDONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' mailbox:batch:process --max-batches=15 > */proc/1/fd/1' 2>&1APP (-zsh)*3screenpipe"Y2PROD (ssh)Run'do-release-upgrade'to upgrade to it.*** System restart required ***Last login: Thu May 14 07:41:362026 from 212.5.153.87lukas@jiminny-prod-bastion:~$X L3 EU (-zsh)Last login: Sat May 16 18:04:33on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ I|T4STAGE (ssh)See [URL_WITH_CREDENTIALS] ~ $ I17 Ext(-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsEXTENSIONPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|View in Docker Desktop@ View ConfigEnable Watch...
|
50864
|
NULL
|
NULL
|
NULL
|
|
50867
|
1796
|
41
|
2026-05-18T08:07:36.974545+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091656974_m2.jpg...
|
PhpStorm
|
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Commit and Push…
⌥⌘K
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Commit and Push…","depth":5,"bounds":{"left":0.27027926,"top":1.0,"width":0.03956117,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"⌥⌘K","depth":5,"bounds":{"left":0.27027926,"top":1.0,"width":0.020279255,"height":0.0},"on_screen":false,"role_description":"text"}]...
|
2535035496832215689
|
306252506011271184
|
click
|
hybrid
|
NULL
|
Commit and Push…
⌥⌘K
PhostormINavicarecodeLaravelK Commit and Push…
⌥⌘K
PhostormINavicarecodeLaravelKeractorrTavsco.sroledeyC ActivityController.ong© WhichWorkerlsWorkingOnWhichJob.phpconverLeadAcuvites.ong> @ Scheduling© kernel.ong› D Contractsc createrlaybookcreatedevent.ongW Domairv ODTO0 Activityc) IntearationApp/Service.php— customerApi0 ExternalServicelntegrationsC) PlanhatactivityListener.php(C)AutomatedReportsCommand.ohvphp api_y2.ohd) ImportcalliInvitationC) UserinvitationdTo.ohpRolesaSCIM117 ScorecardC) AutomatedReportResult.oho(C) AutomatedReportCreateTeamRequest.phpC) UserinvitationD10.olorganizaServiceM UsersT UserEmail.phpM SmailsSrouter->aroundf'middleware'Srouter->arouodf 'orefix'Srouter->aroundf'orefix'333→ M SnumeD EventsM ActivitiosSrouter->group(['prefi>@ ActivityProviderTM AiAutomation>D AudioTm RoteH):>D Coaching// Integrations$router->group(['middleware'_conterencesm connectionsmadnlowanolmialallewaroeaermLocal ChangesLOg xchandes % tlles= env.local apoT.OA21c62021comncomooser.ison apocomooser.lock aoo(C) Kernel.oho apo/ConsolePurgelookupcache.onp© SyncToPlanhat.php• / M Commands/Crm 1 file(C)TestPioedriveOfficialSdkCommand.ohvV (C) Kernel.oho• contia 1 filel3 added 3 modifiedCommit Messagepipedrive offical SDK v2 POCv Diftside-oy-slde viewerDo notignoreHighlight worasCurrent version app/con:declare(strict types=1)namespace Jaminnv console commanos Crm:use Carbon Carbon:use ILuminate Console Commandiuse Jiminny Comoonent Encrvotion EncrvotedTokenManager:use Jiminny Models Socialaccount."nunomaduro/collision": "^8.6",php loading.oho confidMI PIPEDRIVE VO MIGRATION PLAN.md and"phpstan/phpstan": "^2.1""phpunit/phpunit": "^11.5.50".M+ PIPEDRIVE_V2_MIGRATION_TICKETS.md app(C) TectPinedriveOfficialSdkCommand.nhnann/Console/Commands/Crm"symfony/phpunit-bridge": "^7.0","vimeo/psalm": "^6.5.0"Unversioned Files 9 filesi= .env.nikilocal appenv.other ann(8 CanAcceccAiDonorteTact nhn toctell Init/Doliciod"autoload":{© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Rf fovicon icn nubli=ids.txt appTraw sall querv.sal app© SimulateWebhooksCommand.php app/Console/Commands/Crm/HubspotM+ WEBHOOK_FILTERING_IMPLEMENTATION.md appPreparation Tor kell.. In 3h o3 m100% 1Mon 18 May 11:07:36+O •=custom.loglaravel.log4 SF [jiminny@localhost]& console [STAGING]A HS_local [jiminny@localhost]# console (PROD1fii users (PROD]TestPipedriveOfficialSdkCommand.php© Kernel.php x M+ PIPEDRIVE_V2_MIGRATION_PLAN.mdM+ PIPEDRIVE_VZ_MIGRATION_TICKEIS.mQCommit ChanaesChangelist: ChangesAuthor:Amend commitSian-oft commitCommit checksUpdate copvriahiPoformat codeRearrange codeOptimize importsMigrating to Pipedriveint for team 191 in official SDK v2 via $config->setAccessToken($tokeCancelCommitCommit and Push..The cRerectAsk anvthina (*4L)+ & codeSWE-1ACurrent vercion"nunomaduro/collision": "^8.6","phpstan/phpstan": "^2.1","phpunit/phpunit": "^11.5.50","pipedrive/pipedrive":"symfony/phounit-bridge": "л7.0"."vimeo/osalm": "^6.5.0"Reject allAccept all1ditterencePA enasd....
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50868
|
1795
|
55
|
2026-05-18T08:07:44.623885+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091664623_m1.jpg...
|
PhpStorm
|
Code Analysis
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Code Analysis
text/html
text/html
text/html
File ~ Code Analysis
text/html
text/html
text/html
File ~/jiminny/app/app/Console/Commands/Crm/TestPipedriveOfficialSdkCommand.php contains problems.<br/>No errors and 4 warnings found.<br/>Would you like to review them?
Cancel
Commit and Push
Review Code Analysis...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Code Analysis","depth":1,"on_screen":true,"value":"Code Analysis","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":2,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":2,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":2,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"File ~/jiminny/app/app/Console/Commands/Crm/TestPipedriveOfficialSdkCommand.php contains problems.<br/>No errors and 4 warnings found.<br/>Would you like to review them?","depth":1,"on_screen":true,"help_text":"text/html","role_description":"text"},{"role":"AXButton","text":"Cancel","depth":1,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Commit and Push","depth":1,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Review Code Analysis","depth":1,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8921968001908854141
|
6805301586817245902
|
visual_change
|
accessibility
|
NULL
|
Code Analysis
text/html
text/html
text/html
File ~ Code Analysis
text/html
text/html
text/html
File ~/jiminny/app/app/Console/Commands/Crm/TestPipedriveOfficialSdkCommand.php contains problems.<br/>No errors and 4 warnings found.<br/>Would you like to review them?
Cancel
Commit and Push
Review Code Analysis...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50869
|
1796
|
42
|
2026-05-18T08:07:46.566271+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091666566_m2.jpg...
|
PhpStorm
|
Code Analysis
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Code Analysis
text/html
text/html
text/html
File ~ Code Analysis
text/html
text/html
text/html
File ~/jiminny/app/app/Console/Commands/Crm/TestPipedriveOfficialSdkCommand.php contains problems.<br/>No errors and 4 warnings found.<br/>Would you like to review them?
Cancel
Commit and Push
Review Code Analysis...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Code Analysis","depth":1,"bounds":{"left":0.35006648,"top":0.28411812,"width":0.14926861,"height":0.014365523},"on_screen":true,"value":"Code Analysis","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":2,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":2,"bounds":{"left":0.35006648,"top":0.28411812,"width":0.032247342,"height":0.014365523},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":2,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"File ~/jiminny/app/app/Console/Commands/Crm/TestPipedriveOfficialSdkCommand.php contains problems.<br/>No errors and 4 warnings found.<br/>Would you like to review them?","depth":1,"bounds":{"left":0.35006648,"top":0.3048683,"width":0.14926861,"height":0.06783719},"on_screen":true,"help_text":"text/html","role_description":"text"},{"role":"AXButton","text":"Cancel","depth":1,"bounds":{"left":0.35006648,"top":0.38786912,"width":0.028590426,"height":0.027134877},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Commit and Push","depth":1,"bounds":{"left":0.38264626,"top":0.38786912,"width":0.051529255,"height":0.027134877},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Review Code Analysis","depth":1,"bounds":{"left":0.4381649,"top":0.38786912,"width":0.061170213,"height":0.027134877},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8921968001908854141
|
6805301586817245902
|
click
|
accessibility
|
NULL
|
Code Analysis
text/html
text/html
text/html
File ~ Code Analysis
text/html
text/html
text/html
File ~/jiminny/app/app/Console/Commands/Crm/TestPipedriveOfficialSdkCommand.php contains problems.<br/>No errors and 4 warnings found.<br/>Would you like to review them?
Cancel
Commit and Push
Review Code Analysis...
|
50867
|
NULL
|
NULL
|
NULL
|
|
50870
|
1796
|
43
|
2026-05-18T08:07:48.733808+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091668733_m2.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Committing…
Project: faVsco.js, menu
pipedrive-sdk Committing…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post('trim', [KioskActivityController::class, 'trimActivity']);
$router->get('troubleshoot', [KioskActivityController::class, 'troubleshootA...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Committing…","depth":2,"bounds":{"left":0.5325798,"top":0.54269755,"width":0.06948138,"height":0.011173184},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"","depth":2,"bounds":{"left":0.5325798,"top":0.5746209,"width":0.06948138,"height":0.011173184},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.14228724,"top":0.30327216,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.1549202,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"bounds":{"left":0.16589096,"top":0.30247405,"width":0.043882977,"height":0.015961692},"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.21875,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.2287234,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.23736702,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.24601063,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"bounds":{"left":0.25964096,"top":0.30167598,"width":0.025598405,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.28523937,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.29388297,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.3025266,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.31117022,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.35904256,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chrome","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Firefox","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Safari","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.31648937,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.32646278,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.33643618,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3464096,"top":0.3320032,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.35771278,"top":0.33040702,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.3650266,"top":0.33040702,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00930851,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-6012843911384008434
|
2696331441572408523
|
visual_change
|
accessibility
|
NULL
|
Committing…
Project: faVsco.js, menu
pipedrive-sdk Committing…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post('trim', [KioskActivityController::class, 'trimActivity']);
$router->get('troubleshoot', [KioskActivityController::class, 'troubleshootA...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50871
|
1795
|
56
|
2026-05-18T08:07:50.684046+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091670684_m1.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Committing…
Project: faVsco.js, menu
pipedrive-sdk Committing…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post('trim', [KioskActivityController::class, 'trimActivity']);
$router->get('troubleshoot', [KioskActivityController::class, 'troubleshootA...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Committing…","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chrome","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Firefox","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Safari","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.019444445,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-6012843911384008434
|
2696331441572408523
|
visual_change
|
accessibility
|
NULL
|
Committing…
Project: faVsco.js, menu
pipedrive-sdk Committing…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post('trim', [KioskActivityController::class, 'trimActivity']);
$router->get('troubleshoot', [KioskActivityController::class, 'troubleshootA...
|
50868
|
NULL
|
NULL
|
NULL
|
|
50872
|
1796
|
44
|
2026-05-18T08:07:51.764018+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091671764_m2.jpg...
|
PhpStorm
|
Push Commits to app
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
pipedrive-sdk-poc → origin : pipedrive-sdk-poc
pip pipedrive-sdk-poc → origin : pipedrive-sdk-poc
pipedrive offical SDK v2 POC
Show Diff
Group By
Jump to Source
Show Details
Expand All
Collapse All
6 files, folder
app/Console 2 files, folder
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
composer.json, JSON file
composer.lock, JSON file
PIPEDRIVE_V2_MIGRATION_PLAN.md
PIPEDRIVE_V2_MIGRATION_TICKETS.md
app/Console 2 files, folder
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
composer.json, JSON file
composer.lock, JSON file
PIPEDRIVE_V2_MIGRATION_PLAN.md
PIPEDRIVE_V2_MIGRATION_TICKETS.md
For Commit and Push to non-protected branches, preview commits before push
Help
Push tags:
All
Current Branch
Cancel
Push
Push
Push Commits to app...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"pipedrive-sdk-poc → origin : pipedrive-sdk-poc","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.12699468,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"pipedrive offical SDK v2 POC","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.12699468,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Show Diff","depth":2,"bounds":{"left":0.61269945,"top":0.31524342,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Group By","depth":2,"bounds":{"left":0.6236702,"top":0.31524342,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Jump to Source","depth":2,"bounds":{"left":0.63231385,"top":0.31524342,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Details","depth":2,"bounds":{"left":0.64328456,"top":0.31524342,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand All","depth":2,"bounds":{"left":0.72539896,"top":0.31524342,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":2,"bounds":{"left":0.7340425,"top":0.31524342,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files, folder","depth":4,"bounds":{"left":0.6203458,"top":0.33838788,"width":0.022606382,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"app/Console 2 files, folder","depth":5,"bounds":{"left":0.62666225,"top":0.35594574,"width":0.048204787,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Commands/Crm 1 file, folder","depth":6,"bounds":{"left":0.63297874,"top":0.3735036,"width":0.052526597,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"TestPipedriveOfficialSdkCommand.php, class","depth":7,"bounds":{"left":0.6392952,"top":0.39106146,"width":0.08843085,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Kernel.php, class","depth":6,"bounds":{"left":0.63297874,"top":0.4086193,"width":0.03025266,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"composer.json, JSON file","depth":5,"bounds":{"left":0.62666225,"top":0.42617717,"width":0.038231384,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"composer.lock, JSON file","depth":5,"bounds":{"left":0.62666225,"top":0.44373503,"width":0.037898935,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"PIPEDRIVE_V2_MIGRATION_PLAN.md","depth":5,"bounds":{"left":0.62666225,"top":0.4612929,"width":0.08444149,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"PIPEDRIVE_V2_MIGRATION_TICKETS.md","depth":5,"bounds":{"left":0.62666225,"top":0.47885075,"width":0.091090426,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"app/Console 2 files, folder","depth":4,"bounds":{"left":0.62666225,"top":0.35594574,"width":0.048204787,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Commands/Crm 1 file, folder","depth":5,"bounds":{"left":0.63297874,"top":0.3735036,"width":0.052526597,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"TestPipedriveOfficialSdkCommand.php, class","depth":6,"bounds":{"left":0.6392952,"top":0.39106146,"width":0.08843085,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Kernel.php, class","depth":5,"bounds":{"left":0.63297874,"top":0.4086193,"width":0.03025266,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Commands/Crm 1 file, folder","depth":4,"bounds":{"left":0.63297874,"top":0.3735036,"width":0.052526597,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"TestPipedriveOfficialSdkCommand.php, class","depth":5,"bounds":{"left":0.6392952,"top":0.39106146,"width":0.08843085,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"TestPipedriveOfficialSdkCommand.php, class","depth":4,"bounds":{"left":0.6392952,"top":0.39106146,"width":0.08843085,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Kernel.php, class","depth":4,"bounds":{"left":0.63297874,"top":0.4086193,"width":0.03025266,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"composer.json, JSON file","depth":4,"bounds":{"left":0.62666225,"top":0.42617717,"width":0.038231384,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"composer.lock, JSON file","depth":4,"bounds":{"left":0.62666225,"top":0.44373503,"width":0.037898935,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"PIPEDRIVE_V2_MIGRATION_PLAN.md","depth":4,"bounds":{"left":0.62666225,"top":0.4612929,"width":0.08444149,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"PIPEDRIVE_V2_MIGRATION_TICKETS.md","depth":4,"bounds":{"left":0.62666225,"top":0.47885075,"width":0.091090426,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXCheckBox","text":"For Commit and Push to non-protected branches, preview commits before push","depth":1,"bounds":{"left":0.48304522,"top":0.6783719,"width":0.17287233,"height":0.019952115},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Help","depth":1,"bounds":{"left":0.4820479,"top":0.7047087,"width":0.00930851,"height":0.027134877},"on_screen":true,"help_text":"Show help contents","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Push tags:","depth":1,"bounds":{"left":0.49800533,"top":0.70790106,"width":0.031914894,"height":0.019952115},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"All","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Current Branch","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Cancel","depth":1,"bounds":{"left":0.67586434,"top":0.7047087,"width":0.025930852,"height":0.027134877},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Push","depth":1,"bounds":{"left":0.7037899,"top":0.7047087,"width":0.036236703,"height":0.027134877},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Push","depth":2,"bounds":{"left":0.7037899,"top":0.7047087,"width":0.025930852,"height":0.027134877},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Push Commits to app","depth":1,"bounds":{"left":0.5874335,"top":0.29449323,"width":0.04720745,"height":0.012769354},"on_screen":true,"role_description":"text"}]...
|
5675497303449496590
|
-934619444468731938
|
visual_change
|
accessibility
|
NULL
|
pipedrive-sdk-poc → origin : pipedrive-sdk-poc
pip pipedrive-sdk-poc → origin : pipedrive-sdk-poc
pipedrive offical SDK v2 POC
Show Diff
Group By
Jump to Source
Show Details
Expand All
Collapse All
6 files, folder
app/Console 2 files, folder
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
composer.json, JSON file
composer.lock, JSON file
PIPEDRIVE_V2_MIGRATION_PLAN.md
PIPEDRIVE_V2_MIGRATION_TICKETS.md
app/Console 2 files, folder
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
composer.json, JSON file
composer.lock, JSON file
PIPEDRIVE_V2_MIGRATION_PLAN.md
PIPEDRIVE_V2_MIGRATION_TICKETS.md
For Commit and Push to non-protected branches, preview commits before push
Help
Push tags:
All
Current Branch
Cancel
Push
Push
Push Commits to app...
|
50870
|
NULL
|
NULL
|
NULL
|
|
50873
|
1795
|
57
|
2026-05-18T08:07:55.257280+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091675257_m1.jpg...
|
PhpStorm
|
Push Commits to app
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
pipedrive-sdk-poc → origin : pipedrive-sdk-poc
pip pipedrive-sdk-poc → origin : pipedrive-sdk-poc
pipedrive offical SDK v2 POC
Show Diff
Group By
Jump to Source
Show Details
Expand All
Collapse All
6 files, folder
app/Console 2 files, folder
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
composer.json, JSON file
composer.lock, JSON file
PIPEDRIVE_V2_MIGRATION_PLAN.md
PIPEDRIVE_V2_MIGRATION_TICKETS.md
app/Console 2 files, folder
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
composer.json, JSON file
composer.lock, JSON file
PIPEDRIVE_V2_MIGRATION_PLAN.md
PIPEDRIVE_V2_MIGRATION_TICKETS.md
For Commit and Push to non-protected branches, preview commits before push
Help
Push tags:
All
Current Branch
Cancel
Push
Push
Push Commits to app...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"pipedrive-sdk-poc → origin : pipedrive-sdk-poc","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.26527777,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"pipedrive offical SDK v2 POC","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.26527777,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Show Diff","depth":2,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Group By","depth":2,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Jump to Source","depth":2,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Details","depth":2,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand All","depth":2,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":2,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files, folder","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"app/Console 2 files, folder","depth":5,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Commands/Crm 1 file, folder","depth":6,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"TestPipedriveOfficialSdkCommand.php, class","depth":7,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Kernel.php, class","depth":6,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"composer.json, JSON file","depth":5,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"composer.lock, JSON file","depth":5,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"PIPEDRIVE_V2_MIGRATION_PLAN.md","depth":5,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"PIPEDRIVE_V2_MIGRATION_TICKETS.md","depth":5,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"app/Console 2 files, folder","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Commands/Crm 1 file, folder","depth":5,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"TestPipedriveOfficialSdkCommand.php, class","depth":6,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Kernel.php, class","depth":5,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Commands/Crm 1 file, folder","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"TestPipedriveOfficialSdkCommand.php, class","depth":5,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"TestPipedriveOfficialSdkCommand.php, class","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Kernel.php, class","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"composer.json, JSON file","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"composer.lock, JSON file","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"PIPEDRIVE_V2_MIGRATION_PLAN.md","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"PIPEDRIVE_V2_MIGRATION_TICKETS.md","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXCheckBox","text":"For Commit and Push to non-protected branches, preview commits before push","depth":1,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Help","depth":1,"on_screen":true,"help_text":"Show help contents","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Push tags:","depth":1,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"All","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Current Branch","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Cancel","depth":1,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Push","depth":1,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Push","depth":2,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Push Commits to app","depth":1,"on_screen":true,"role_description":"text"}]...
|
5675497303449496590
|
-934619444468731938
|
click
|
accessibility
|
NULL
|
pipedrive-sdk-poc → origin : pipedrive-sdk-poc
pip pipedrive-sdk-poc → origin : pipedrive-sdk-poc
pipedrive offical SDK v2 POC
Show Diff
Group By
Jump to Source
Show Details
Expand All
Collapse All
6 files, folder
app/Console 2 files, folder
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
composer.json, JSON file
composer.lock, JSON file
PIPEDRIVE_V2_MIGRATION_PLAN.md
PIPEDRIVE_V2_MIGRATION_TICKETS.md
app/Console 2 files, folder
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
Commands/Crm 1 file, folder
TestPipedriveOfficialSdkCommand.php, class
TestPipedriveOfficialSdkCommand.php, class
Kernel.php, class
composer.json, JSON file
composer.lock, JSON file
PIPEDRIVE_V2_MIGRATION_PLAN.md
PIPEDRIVE_V2_MIGRATION_TICKETS.md
For Commit and Push to non-protected branches, preview commits before push
Help
Push tags:
All
Current Branch
Cancel
Push
Push
Push Commits to app...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50874
|
1795
|
58
|
2026-05-18T08:07:56.716519+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091676716_m1.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
6 files committed
pipedrive offical SDK v2 POC
tex 6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"6 files committed","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-3131966368763910826
|
-1360726897342263882
|
visual_change
|
hybrid
|
NULL
|
6 files committed
pipedrive offical SDK v2 POC
tex 6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
iTerm2ShellEditViewSessionScriptsProfilesWindowHelpPreparation for Refi…. in 3h 53 m100% <78• Mon 18 May 11:07:56DOCKER (docker-compose)• $2T81DOCKER81DEV (-zsh)11DOCKER (docker-compose)docker_lamp_1• '/usr/local/bin/php''artisan'crm:bullhorn:ping --heartbeat > '/proc/1/fd/1'2>&1docker_lamp_1docker_1amp_1run_artisan_schedule: Done waiting for schedule:rundocker_lamp_1docker_1amp_12026-05-18 08:07:05 Running ['artisan'meeting-bot: schedule-bot]6sDONEdocker_lamp_1• '/usr/local/bin/php' 'artisan'meeting-bot:schedule-bot > */proc/1/docker_lamp_12026-05-18 08:07:12 Running ['artisan'dialers:monitor-activities].3s DONEdocker_lamp_11/fd/1', '/usr/local/bin/php' 'artisan' dialers:monitor-activities > '/proc/2>81docker_1amp_12026-05-1808:07:15 Running ['artisan' jiminny:monitor-social-accountSJ35 DONEdocker_lamp_1l '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > '/proc/1/fd/1'2>&1docker_lamp_12026-05-18 08:07:19 Running ['artisan' mailbox:skip-lists:refresh]3S DONEdocker_lamp_1" '/usr/local/bin/php' 'artisan'mailbox:skip-lists:refresh > '/proc/1/fd/1'2>&1docker_lamp_12026-05-18 08:07:22 Running ['artisan'mailbox:batch:process--max-batches=15J6s DONEdocker_1amp_1• '/usr/local/bin/php' 'artisan' mailbox:batch:process --max-batches=15 > '/proc/1/fd/1'docker_lamp_12026-05-18 08:07:29 Running ['artisan'mailbox:batch: createl4S DONEdocker_lamp_11 l '/usr/local/bin/php' 'artisan'mailbox:batch:create › */proc/1/fd/12>&1docker_lamp_12026-05-18 08:07:33 Running ['artisan'activity:sync 'ringcentral' 'avaya' 'telus' 'talkdesk' --from='2026-05-18 07:51:00' --t0='2026-05-18 08:07:00']1s DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'activity: sync'ringcentral' 'avaya''telus'"talkdesk'--from='2026-05-18 07:51:00'--to='2026-05-18 08:07:00' > */proc/1/fd/1'2>81docker_lamp_1docker_1amp_1docker_lamp_1run_artisan_schedule: Done waiting forschedule: run2026-05-18 08:07:36 Jiminny\Jobs\Mailbox\CreateBatchesRUNNINGdocker_lamp_12026-05-18 08:07:36 Jiminny \Jobs \Mailbox\CreateBatches92.34msDONEView in Docker Desktop@ View ConfigEnable WatchAPP (-zsh)*3screenpipe"PROD (ssh)Run'do-release-upgrade'to upgrade to it.*** System restart required ***Last login: Thu May 14 07:41:362026 from 212.5.153.87lukas@jiminny-prod-bastion:~$X L3 EU (-zsh)Last login: Sat May 16 18:04:33on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ I|T4STAGE (ssh)See [URL_WITH_CREDENTIALS] ~ $ I17 EXT (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsEXTENSIONPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|...
|
50873
|
NULL
|
NULL
|
NULL
|
|
50875
|
1796
|
45
|
2026-05-18T08:07:57.831337+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091677831_m2.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Writing objects: 100% (526/526), 186.68 KiB | 12.4 Writing objects: 100% (526/526), 186.68 KiB | 12.45 MiB/s, done.
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post(...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"","depth":2,"bounds":{"left":0.5325798,"top":0.54269755,"width":0.06948138,"height":0.011173184},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"Writing objects: 100% (526/526), 186.68 KiB | 12.45 MiB/s, done.","depth":2,"bounds":{"left":0.5325798,"top":0.5746209,"width":0.06948138,"height":0.011173184},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"6 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.91300875,"width":0.100398935,"height":0.013567438},"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.11037234,"height":0.013567438},"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.059507977,"height":0.013567438},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.14228724,"top":0.30327216,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.1549202,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"bounds":{"left":0.16589096,"top":0.30247405,"width":0.043882977,"height":0.015961692},"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.21875,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.2287234,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.23736702,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.24601063,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"bounds":{"left":0.25964096,"top":0.30167598,"width":0.025598405,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.28523937,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.29388297,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.3025266,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.31117022,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.35904256,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chrome","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Firefox","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Safari","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.31648937,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.32646278,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.33643618,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3464096,"top":0.3320032,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.35771278,"top":0.33040702,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.3650266,"top":0.33040702,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00930851,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7656431361802673909
|
2696331441838739147
|
visual_change
|
accessibility
|
NULL
|
Writing objects: 100% (526/526), 186.68 KiB | 12.4 Writing objects: 100% (526/526), 186.68 KiB | 12.45 MiB/s, done.
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post(...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50876
|
1795
|
59
|
2026-05-18T08:07:59.183950+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091679183_m1.jpg...
|
Firefox
|
Jy 20846 mcp enable the ai to know details about t Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12075#pullrequestrevie github.com/jiminny/app/pull/12075#pullrequestreview-4308236584...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-5722649417727870763
|
-1053402666066115154
|
app_switch
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50877
|
1796
|
46
|
2026-05-18T08:07:59.221601+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091679221_m2.jpg...
|
Firefox
|
Jy 20846 mcp enable the ai to know details about t Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12075#pullrequestrevie github.com/jiminny/app/pull/12075#pullrequestreview-4308236584...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.19847074,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-3288695287744557847
|
-1048828695547083378
|
app_switch
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50878
|
1795
|
60
|
2026-05-18T08:07:59.748940+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091679748_m1.jpg...
|
Firefox
|
Jy 20846 mcp enable the ai to know details about t Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12075#pullrequestrevie github.com/jiminny/app/pull/12075#pullrequestreview-4308236584...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-2272923623399904777
|
-5516469759369149018
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(...
|
50876
|
NULL
|
NULL
|
NULL
|
|
50880
|
1796
|
47
|
2026-05-18T08:08:01.294954+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091681294_m2.jpg...
|
Firefox
|
Usage | Windsurf — Work
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.18994413,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.19847074,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-9104469335941488713
|
8175598870322873742
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:...
|
50877
|
NULL
|
NULL
|
NULL
|
|
50879
|
1795
|
61
|
2026-05-18T08:08:01.297610+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091681297_m1.jpg...
|
Firefox
|
Usage | Windsurf — Work
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-9098122172628172870
|
-327127954771540594
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50881
|
1796
|
48
|
2026-05-18T08:08:03.910045+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091683910_m2.jpg...
|
Firefox
|
Usage | Windsurf — Work
|
1
|
windsurf.com/subscription/usage
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.18994413,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.19847074,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"bounds":{"left":0.1100399,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"bounds":{"left":0.120678194,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"bounds":{"left":0.13131648,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"bounds":{"left":0.1419548,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"bounds":{"left":0.15259309,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"bounds":{"left":0.2711104,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"bounds":{"left":0.28573802,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"bounds":{"left":0.30568483,"top":0.035514764,"width":0.05036569,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"bounds":{"left":0.079288565,"top":0.03631285,"width":0.019946808,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"bounds":{"left":0.30568483,"top":0.037110932,"width":0.05036569,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
6446423769231908192
|
8895119416979490222
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50882
|
1795
|
62
|
2026-05-18T08:08:05.805757+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091685805_m1.jpg...
|
Firefox
|
Usage | Windsurf — Work
|
1
|
windsurf.com/subscription/usage
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-1905008479784333079
|
-7470031523923955250
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app...
|
50879
|
NULL
|
NULL
|
NULL
|
|
50883
|
1795
|
63
|
2026-05-18T08:08:08.826113+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091688826_m1.jpg...
|
Firefox
|
Usage | Windsurf — Work
|
1
|
windsurf.com/subscription/usage
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects->map(...)->toArray()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", Laravel collections sometimes preserve their underlying associative keys.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Instead of generating a clean JSON array (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[\"en_GB\", \"bg_BG\"]","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"), it might generate a JSON object with numeric string keys (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"{\"0\": \"en_GB\", \"1\": \"bg_BG\"}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). This would violate your schema's strict definition of","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
3334981859828863275
|
8318657556641613222
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
Close tab
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50884
|
NULL
|
0
|
2026-05-18T08:08:19.647360+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091699647_m1.jpg...
|
Firefox
|
Jy 20846 mcp enable the ai to know details about t Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12075#pullrequestrevie github.com/jiminny/app/pull/12075#pullrequestreview-4308236584...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response....
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
7213731898870991526
|
8751004228886857134
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response....
|
50883
|
NULL
|
NULL
|
NULL
|
|
50885
|
NULL
|
0
|
2026-05-18T08:08:19.647446+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091699647_m2.jpg...
|
Firefox
|
Jy 20846 mcp enable the ai to know details about t Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12075#pullrequestrevie github.com/jiminny/app/pull/12075#pullrequestreview-4308236584...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.19847074,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"bounds":{"left":0.1100399,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"bounds":{"left":0.120678194,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
1203269787658666205
|
8751004228886857102
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response...
|
50881
|
NULL
|
NULL
|
NULL
|
|
50886
|
1798
|
0
|
2026-05-18T08:08:22.031432+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091702031_m2.jpg...
|
Firefox
|
Jy 20846 mcp enable the ai to know details about t Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12075#pullrequestrevie github.com/jiminny/app/pull/12075#pullrequestreview-4308236584...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.19847074,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"bounds":{"left":0.1100399,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"bounds":{"left":0.120678194,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"bounds":{"left":0.13131648,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"bounds":{"left":0.1419548,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"bounds":{"left":0.15259309,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"bounds":{"left":0.2711104,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"bounds":{"left":0.28573802,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"bounds":{"left":0.30568483,"top":0.035514764,"width":0.05036569,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"bounds":{"left":0.079288565,"top":0.03631285,"width":0.019946808,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"bounds":{"left":0.30568483,"top":0.037110932,"width":0.05036569,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"bounds":{"left":0.3480718,"top":0.086592175,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"bounds":{"left":0.12666224,"top":0.088986434,"width":0.030917553,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"bounds":{"left":0.1243351,"top":0.13048683,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"bounds":{"left":0.1243351,"top":0.13248204,"width":0.04105718,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"bounds":{"left":0.11336436,"top":0.13447726,"width":0.21991356,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"bounds":{"left":0.11336436,"top":0.17597765,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"bounds":{"left":0.11336436,"top":0.17757383,"width":0.03656915,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"bounds":{"left":0.15192819,"top":0.17757383,"width":0.025598405,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"bounds":{"left":0.17952128,"top":0.17757383,"width":0.036070477,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"bounds":{"left":0.14960106,"top":0.2047087,"width":0.041223403,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"bounds":{"left":0.19281915,"top":0.20590582,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.23254654,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"bounds":{"left":0.2059508,"top":0.22825219,"width":0.041888297,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"bounds":{"left":0.24983378,"top":0.22705507,"width":0.04055851,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"bounds":{"left":0.29238698,"top":0.22825219,"width":0.019448139,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.31382978,"top":0.22705507,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.22955452,"height":0.061053474},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"bounds":{"left":0.13730054,"top":0.30407023,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"bounds":{"left":0.17004654,"top":0.3028731,"width":0.00880984,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.18085106,"top":0.30407023,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.1939827,"top":0.3028731,"width":0.0014960107,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"bounds":{"left":0.12599733,"top":0.3339984,"width":0.020279255,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"bounds":{"left":0.1462766,"top":0.3339984,"width":0.10854388,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"bounds":{"left":0.13131648,"top":0.37789306,"width":0.009142287,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.32945478,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.34275267,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0056515955,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"bounds":{"left":0.13696809,"top":0.4197925,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"bounds":{"left":0.14527926,"top":0.4197925,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"bounds":{"left":0.16206782,"top":0.4197925,"width":0.027925532,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0809508,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"bounds":{"left":0.14245346,"top":0.4365523,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"bounds":{"left":0.15924202,"top":0.4365523,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"bounds":{"left":0.16755319,"top":0.4365523,"width":0.06981383,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"bounds":{"left":0.23736702,"top":0.4365523,"width":0.08111702,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"bounds":{"left":0.13131648,"top":0.4365523,"width":0.19265293,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"bounds":{"left":0.11336436,"top":0.5039904,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"bounds":{"left":0.11336436,"top":0.50558656,"width":0.09823803,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.18999335,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"bounds":{"left":0.3053524,"top":0.5339186,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.24185506,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"bounds":{"left":0.15442154,"top":0.55626494,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"bounds":{"left":0.11336436,"top":0.55506784,"width":0.24451463,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.036402926,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"bounds":{"left":0.16589096,"top":0.6097366,"width":0.055851065,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"bounds":{"left":0.2237367,"top":0.6085395,"width":0.054853722,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"bounds":{"left":0.2805851,"top":0.6097366,"width":0.044714097,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.23005319,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.04737367,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"bounds":{"left":0.17669548,"top":0.6632083,"width":0.0866024,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"bounds":{"left":0.26529256,"top":0.66201115,"width":0.023936171,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"bounds":{"left":0.2912234,"top":0.6632083,"width":0.036236703,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.22240691,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.20728059,"top":0.6855547,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"bounds":{"left":0.22041224,"top":0.6843575,"width":0.024601065,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"bounds":{"left":0.24700798,"top":0.6855547,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"bounds":{"left":0.27975398,"top":0.6843575,"width":0.025099734,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.050199468,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"bounds":{"left":0.17619681,"top":0.71548283,"width":0.005984043,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"bounds":{"left":0.18417554,"top":0.71668,"width":0.04737367,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.22490026,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"bounds":{"left":0.14095744,"top":0.7390263,"width":0.1171875,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"bounds":{"left":0.2601396,"top":0.7378292,"width":0.04089096,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"bounds":{"left":0.30302528,"top":0.7390263,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"bounds":{"left":0.31898272,"top":0.7378292,"width":0.006482713,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.3274601,"top":0.7390263,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.34075797,"top":0.7378292,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"bounds":{"left":0.11336436,"top":0.7793296,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"bounds":{"left":0.11336436,"top":0.78092575,"width":0.08809841,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.80806065,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"bounds":{"left":0.14960106,"top":0.80806065,"width":0.030751329,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects->map(...)->toArray()","depth":27,"bounds":{"left":0.18234707,"top":0.8092578,"width":0.10605053,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", Laravel collections sometimes preserve their underlying associative keys.","depth":26,"bounds":{"left":0.12599733,"top":0.80806065,"width":0.21343085,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.86153233,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Instead of generating a clean JSON array (e.g.,","depth":26,"bounds":{"left":0.15708111,"top":0.86153233,"width":0.115359046,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[\"en_GB\", \"bg_BG\"]","depth":27,"bounds":{"left":0.27443483,"top":0.86272943,"width":0.05036569,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"), it might generate a JSON object with numeric string keys (e.g.,","depth":26,"bounds":{"left":0.12599733,"top":0.86153233,"width":0.22490026,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"{\"0\": \"en_GB\", \"1\": \"bg_BG\"}","depth":27,"bounds":{"left":0.2601396,"top":0.8850758,"width":0.07829122,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). This would violate your schema's strict definition of","depth":26,"bounds":{"left":0.12599733,"top":0.8838787,"width":0.23005319,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->items($schema->string())","depth":27,"bounds":{"left":0.24152261,"top":0.9074222,"width":0.07247341,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.3159907,"top":0.9062251,"width":0.0014960107,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"bounds":{"left":0.12599733,"top":0.93735033,"width":0.020279255,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Append","depth":26,"bounds":{"left":0.1462766,"top":0.93735033,"width":0.021609042,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->values()","depth":27,"bounds":{"left":0.16988032,"top":0.9385475,"width":0.027925532,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"before","depth":26,"bounds":{"left":0.19980054,"top":0.93735033,"width":0.01861702,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->toArray()","depth":27,"bounds":{"left":0.22041224,"top":0.9385475,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to guarantee a sequentially indexed array:","depth":26,"bounds":{"left":0.25299203,"top":0.93735033,"width":0.10255984,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"bounds":{"left":0.13131648,"top":0.98124504,"width":0.009142287,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.32945478,"top":0.9724661,"width":0.013297873,"height":0.027533889},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.34275267,"top":0.9724661,"width":0.013297873,"height":0.027533889},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"$languageDialects","depth":27,"bounds":{"left":0.09923537,"top":1.0,"width":0.047539894,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->map(","depth":27,"bounds":{"left":0.14677526,"top":1.0,"width":0.01662234,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"fn","depth":27,"bounds":{"left":0.16339761,"top":1.0,"width":0.0056515955,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.1690492,"top":1.0,"width":0.0056515955,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$dialect","depth":27,"bounds":{"left":0.1747008,"top":1.0,"width":0.022273935,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") => $","depth":27,"bounds":{"left":0.19697474,"top":1.0,"width":0.016788565,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"dialect","depth":27,"bounds":{"left":0.2137633,"top":1.0,"width":0.019448139,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->","depth":27,"bounds":{"left":0.23321144,"top":1.0,"width":0.0056515955,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getLanguageLocale","depth":27,"bounds":{"left":0.23886304,"top":1.0,"width":0.047539894,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.2864029,"top":1.0,"width":0.0026595744,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"))->","depth":27,"bounds":{"left":0.2890625,"top":1.0,"width":0.011303191,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"values","depth":27,"bounds":{"left":0.3003657,"top":1.0,"width":0.01662234,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.31698802,"top":1.0,"width":0.0028257978,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")->","depth":27,"bounds":{"left":0.31981382,"top":1.0,"width":0.008477394,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"toArray","depth":27,"bounds":{"left":0.32829124,"top":1.0,"width":0.019448139,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.34773937,"top":1.0,"width":0.0028257978,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":27,"bounds":{"left":0.35056517,"top":1.0,"width":0.0028257978,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-6758569247925836612
|
8895154592728090022
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50887
|
1797
|
0
|
2026-05-18T08:08:23.898312+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091703898_m1.jpg...
|
Firefox
|
Jy 20846 mcp enable the ai to know details about t Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12075#pullrequestrevie github.com/jiminny/app/pull/12075#pullrequestreview-4308236584...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects->map(...)->toArray()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", Laravel collections sometimes preserve their underlying associative keys.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Instead of generating a clean JSON array (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[\"en_GB\", \"bg_BG\"]","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"), it might generate a JSON object with numeric string keys (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"{\"0\": \"en_GB\", \"1\": \"bg_BG\"}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). This would violate your schema's strict definition of","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->items($schema->string())","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Append","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->values()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"before","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->toArray()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to guarantee a sequentially indexed array:","depth":26,"bounds":{"left":0.0,"top":0.0,"width":0.21423611,"height":0.022777777},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.12361111,"top":0.0,"width":0.027777778,"height":0.044444446},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.15138888,"top":0.0,"width":0.027777778,"height":0.044444446},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->map(","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"fn","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$dialect","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") => $","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"dialect","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->","depth":27,"bounds":{"left":0.0,"top":0.032222223,"width":0.011805556,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getLanguageLocale","depth":27,"bounds":{"left":0.0,"top":0.032222223,"width":0.099305555,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.033680554,"top":0.032222223,"width":0.0055555557,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"))->","depth":27,"bounds":{"left":0.03923611,"top":0.032222223,"width":0.023611112,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"values","depth":27,"bounds":{"left":0.06284722,"top":0.032222223,"width":0.034722224,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.09756944,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")->","depth":27,"bounds":{"left":0.103472225,"top":0.032222223,"width":0.017708333,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"toArray","depth":27,"bounds":{"left":0.12118056,"top":0.032222223,"width":0.040625,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.16180556,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":27,"bounds":{"left":0.16770834,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Are any of these properties (like","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$timezone","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Redo","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Share & export","depth":20,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":20,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXTextArea","text":"Enter a prompt for Gemini\nencrypted","depth":20,"on_screen":true,"value":"Enter a prompt for Gemini\nencrypted","help_text":"","role_description":"text entry area","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enter a prompt for Gemini","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"encrypted","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open upload file menu","depth":20,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tools","depth":18,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open mode picker","depth":20,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
3306571386637414302
|
8894873117784933798
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50888
|
1798
|
1
|
2026-05-18T08:08:24.122472+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091704122_m2.jpg...
|
Firefox
|
Jy 20846 mcp enable the ai to know details about t Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app — Work...
|
1
|
github.com/jiminny/app/pull/12075
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.19847074,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"bounds":{"left":0.1100399,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"bounds":{"left":0.120678194,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"bounds":{"left":0.13131648,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"bounds":{"left":0.1419548,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"bounds":{"left":0.15259309,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"bounds":{"left":0.2711104,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"bounds":{"left":0.28573802,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"bounds":{"left":0.30568483,"top":0.035514764,"width":0.05036569,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"bounds":{"left":0.079288565,"top":0.03631285,"width":0.019946808,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"bounds":{"left":0.30568483,"top":0.037110932,"width":0.05036569,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"bounds":{"left":0.3480718,"top":0.086592175,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"bounds":{"left":0.12666224,"top":0.088986434,"width":0.030917553,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"bounds":{"left":0.1243351,"top":0.13048683,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"bounds":{"left":0.1243351,"top":0.13248204,"width":0.04105718,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"bounds":{"left":0.11336436,"top":0.13447726,"width":0.21991356,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"bounds":{"left":0.11336436,"top":0.17597765,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"bounds":{"left":0.11336436,"top":0.17757383,"width":0.03656915,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"bounds":{"left":0.15192819,"top":0.17757383,"width":0.025598405,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"bounds":{"left":0.17952128,"top":0.17757383,"width":0.036070477,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"bounds":{"left":0.14960106,"top":0.2047087,"width":0.041223403,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"bounds":{"left":0.19281915,"top":0.20590582,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.23254654,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"bounds":{"left":0.2059508,"top":0.22825219,"width":0.041888297,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"bounds":{"left":0.24983378,"top":0.22705507,"width":0.04055851,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"bounds":{"left":0.29238698,"top":0.22825219,"width":0.019448139,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
1995096021832191992
|
8318658664122418606
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Jy 20846 mcp enable the ai to know details about the user by nikolaybiaivanov · Pull Request #12075 · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini...
|
50886
|
NULL
|
NULL
|
NULL
|
|
50889
|
1798
|
2
|
2026-05-18T08:08:28.087864+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091708087_m2.jpg...
|
Firefox
|
Pull requests · jiminny/app — Work
|
1
|
github.com/jiminny/app/pulls
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pull requests · jiminny/app
Pull requests · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Send message
Your Jiminny chats aren’t used to improve our models. Gemini is AI. It can make mistakes, so double check it.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to content
Skip to content
Open menu
Homepage (g then d)
jiminny
jiminny
app
app
Search or jump to…
Type
/
to search
Chat with Copilot
Open Copilot…
Create new...
All issues(g then i)
All pull requests
All repositories
You have unread notifications(g then n)...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"bounds":{"left":0.1100399,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"bounds":{"left":0.120678194,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"bounds":{"left":0.13131648,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"bounds":{"left":0.1419548,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"bounds":{"left":0.15259309,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"bounds":{"left":0.2711104,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"bounds":{"left":0.28573802,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"bounds":{"left":0.30568483,"top":0.035514764,"width":0.05036569,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"bounds":{"left":0.079288565,"top":0.03631285,"width":0.019946808,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"bounds":{"left":0.30568483,"top":0.037110932,"width":0.05036569,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"bounds":{"left":0.3480718,"top":0.086592175,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"bounds":{"left":0.12666224,"top":0.088986434,"width":0.030917553,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"bounds":{"left":0.1243351,"top":0.13048683,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"bounds":{"left":0.1243351,"top":0.13248204,"width":0.04105718,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"bounds":{"left":0.11336436,"top":0.13447726,"width":0.21991356,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"bounds":{"left":0.11336436,"top":0.17597765,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"bounds":{"left":0.11336436,"top":0.17757383,"width":0.03656915,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"bounds":{"left":0.15192819,"top":0.17757383,"width":0.025598405,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"bounds":{"left":0.17952128,"top":0.17757383,"width":0.036070477,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"bounds":{"left":0.14960106,"top":0.2047087,"width":0.041223403,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"bounds":{"left":0.19281915,"top":0.20590582,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.23254654,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"bounds":{"left":0.2059508,"top":0.22825219,"width":0.041888297,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"bounds":{"left":0.24983378,"top":0.22705507,"width":0.04055851,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"bounds":{"left":0.29238698,"top":0.22825219,"width":0.019448139,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.31382978,"top":0.22705507,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.22955452,"height":0.061053474},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"bounds":{"left":0.13730054,"top":0.30407023,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"bounds":{"left":0.17004654,"top":0.3028731,"width":0.00880984,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.18085106,"top":0.30407023,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.1939827,"top":0.3028731,"width":0.0014960107,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"bounds":{"left":0.12599733,"top":0.3339984,"width":0.020279255,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"bounds":{"left":0.1462766,"top":0.3339984,"width":0.10854388,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"bounds":{"left":0.13131648,"top":0.37789306,"width":0.009142287,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.32945478,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.34275267,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0056515955,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"bounds":{"left":0.13696809,"top":0.4197925,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"bounds":{"left":0.14527926,"top":0.4197925,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"bounds":{"left":0.16206782,"top":0.4197925,"width":0.027925532,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0809508,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"bounds":{"left":0.14245346,"top":0.4365523,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"bounds":{"left":0.15924202,"top":0.4365523,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"bounds":{"left":0.16755319,"top":0.4365523,"width":0.06981383,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"bounds":{"left":0.23736702,"top":0.4365523,"width":0.08111702,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"bounds":{"left":0.13131648,"top":0.4365523,"width":0.19265293,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"bounds":{"left":0.11336436,"top":0.5039904,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"bounds":{"left":0.11336436,"top":0.50558656,"width":0.09823803,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.18999335,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"bounds":{"left":0.3053524,"top":0.5339186,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.24185506,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"bounds":{"left":0.15442154,"top":0.55626494,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"bounds":{"left":0.11336436,"top":0.55506784,"width":0.24451463,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.036402926,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"bounds":{"left":0.16589096,"top":0.6097366,"width":0.055851065,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"bounds":{"left":0.2237367,"top":0.6085395,"width":0.054853722,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"bounds":{"left":0.2805851,"top":0.6097366,"width":0.044714097,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.23005319,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.04737367,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"bounds":{"left":0.17669548,"top":0.6632083,"width":0.0866024,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"bounds":{"left":0.26529256,"top":0.66201115,"width":0.023936171,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"bounds":{"left":0.2912234,"top":0.6632083,"width":0.036236703,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.22240691,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.20728059,"top":0.6855547,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"bounds":{"left":0.22041224,"top":0.6843575,"width":0.024601065,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"bounds":{"left":0.24700798,"top":0.6855547,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"bounds":{"left":0.27975398,"top":0.6843575,"width":0.025099734,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.050199468,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"bounds":{"left":0.17619681,"top":0.71548283,"width":0.005984043,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"bounds":{"left":0.18417554,"top":0.71668,"width":0.04737367,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.22490026,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"bounds":{"left":0.14095744,"top":0.7390263,"width":0.1171875,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"bounds":{"left":0.2601396,"top":0.7378292,"width":0.04089096,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"bounds":{"left":0.30302528,"top":0.7390263,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"bounds":{"left":0.31898272,"top":0.7378292,"width":0.006482713,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.3274601,"top":0.7390263,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.34075797,"top":0.7378292,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"bounds":{"left":0.11336436,"top":0.7793296,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"bounds":{"left":0.11336436,"top":0.78092575,"width":0.08809841,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.80806065,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"bounds":{"left":0.14960106,"top":0.80806065,"width":0.030751329,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects->map(...)->toArray()","depth":27,"bounds":{"left":0.18234707,"top":0.8092578,"width":0.10605053,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", Laravel collections sometimes preserve their underlying associative keys.","depth":26,"bounds":{"left":0.12599733,"top":0.80806065,"width":0.21343085,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.86153233,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Instead of generating a clean JSON array (e.g.,","depth":26,"bounds":{"left":0.15708111,"top":0.86153233,"width":0.115359046,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[\"en_GB\", \"bg_BG\"]","depth":27,"bounds":{"left":0.27443483,"top":0.86272943,"width":0.05036569,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"), it might generate a JSON object with numeric string keys (e.g.,","depth":26,"bounds":{"left":0.12599733,"top":0.86153233,"width":0.22490026,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"{\"0\": \"en_GB\", \"1\": \"bg_BG\"}","depth":27,"bounds":{"left":0.2601396,"top":0.8850758,"width":0.07829122,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). This would violate your schema's strict definition of","depth":26,"bounds":{"left":0.12599733,"top":0.8838787,"width":0.23005319,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->items($schema->string())","depth":27,"bounds":{"left":0.24152261,"top":0.9074222,"width":0.07247341,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.3159907,"top":0.9062251,"width":0.0014960107,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"bounds":{"left":0.12599733,"top":0.93735033,"width":0.020279255,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Append","depth":26,"bounds":{"left":0.1462766,"top":0.93735033,"width":0.021609042,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->values()","depth":27,"bounds":{"left":0.16988032,"top":0.9385475,"width":0.027925532,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"before","depth":26,"bounds":{"left":0.19980054,"top":0.93735033,"width":0.01861702,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->toArray()","depth":27,"bounds":{"left":0.22041224,"top":0.9385475,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to guarantee a sequentially indexed array:","depth":26,"bounds":{"left":0.25299203,"top":0.93735033,"width":0.10255984,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"bounds":{"left":0.13131648,"top":0.98124504,"width":0.009142287,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.32945478,"top":0.9724661,"width":0.013297873,"height":0.027533889},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.34275267,"top":0.9724661,"width":0.013297873,"height":0.027533889},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"$languageDialects","depth":27,"bounds":{"left":0.09923537,"top":1.0,"width":0.047539894,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->map(","depth":27,"bounds":{"left":0.14677526,"top":1.0,"width":0.01662234,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"fn","depth":27,"bounds":{"left":0.16339761,"top":1.0,"width":0.0056515955,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.1690492,"top":1.0,"width":0.0056515955,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$dialect","depth":27,"bounds":{"left":0.1747008,"top":1.0,"width":0.022273935,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") => $","depth":27,"bounds":{"left":0.19697474,"top":1.0,"width":0.016788565,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"dialect","depth":27,"bounds":{"left":0.2137633,"top":1.0,"width":0.019448139,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->","depth":27,"bounds":{"left":0.23321144,"top":1.0,"width":0.0056515955,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getLanguageLocale","depth":27,"bounds":{"left":0.23886304,"top":1.0,"width":0.047539894,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.2864029,"top":1.0,"width":0.0026595744,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"))->","depth":27,"bounds":{"left":0.2890625,"top":1.0,"width":0.011303191,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"values","depth":27,"bounds":{"left":0.3003657,"top":1.0,"width":0.01662234,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.31698802,"top":1.0,"width":0.0028257978,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")->","depth":27,"bounds":{"left":0.31981382,"top":1.0,"width":0.008477394,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"toArray","depth":27,"bounds":{"left":0.32829124,"top":1.0,"width":0.019448139,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.34773937,"top":1.0,"width":0.0028257978,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":27,"bounds":{"left":0.35056517,"top":1.0,"width":0.0028257978,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Are any of these properties (like","depth":24,"bounds":{"left":0.11336436,"top":1.0,"width":0.0787899,"height":-0.07462096},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":25,"bounds":{"left":0.19414894,"top":1.0,"width":0.013962766,"height":-0.07581806},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or","depth":24,"bounds":{"left":0.21010639,"top":1.0,"width":0.0078125,"height":-0.07462096},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$timezone","depth":25,"bounds":{"left":0.21991356,"top":1.0,"width":0.025099734,"height":-0.07581806},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?","depth":24,"bounds":{"left":0.11336436,"top":1.0,"width":0.24069148,"height":-0.07462096},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Redo","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Share & export","depth":20,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":20,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXTextArea","text":"Enter a prompt for Gemini\nencrypted","depth":20,"bounds":{"left":0.11469415,"top":0.83439744,"width":0.23670213,"height":0.01915403},"on_screen":true,"value":"Enter a prompt for Gemini\nencrypted","help_text":"","role_description":"text entry area","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enter a prompt for Gemini","depth":21,"bounds":{"left":0.12134308,"top":0.8347965,"width":0.069980055,"height":0.018355945},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"encrypted","depth":21,"bounds":{"left":0.113696806,"top":0.83439744,"width":0.0066489363,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open upload file menu","depth":20,"bounds":{"left":0.11070479,"top":0.87031126,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tools","depth":18,"bounds":{"left":0.12666224,"top":0.87031126,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open mode picker","depth":20,"bounds":{"left":0.31399602,"top":0.867917,"width":0.026097074,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pro","depth":23,"bounds":{"left":0.31931517,"top":0.87669593,"width":0.007480053,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Microphone","depth":19,"bounds":{"left":0.34208778,"top":0.867917,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Send message","depth":19,"bounds":{"left":0.34840426,"top":0.8671189,"width":0.013962766,"height":0.033519555},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your Jiminny chats aren’t used to improve our models. Gemini is AI. It can make mistakes, so double check it.","depth":17,"bounds":{"left":0.11303192,"top":0.92178774,"width":0.19996676,"height":0.012370312},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Your privacy & Gemini Opens in a new window","depth":17,"bounds":{"left":0.31299868,"top":0.92178774,"width":0.040059842,"height":0.012370312},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your privacy & Gemini","depth":18,"bounds":{"left":0.31299868,"top":0.92178774,"width":0.040059842,"height":0.012370312},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Opens in a new window","depth":19,"bounds":{"left":0.079288565,"top":0.92098963,"width":0.043218084,"height":0.012370312},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Summarize page","depth":7,"bounds":{"left":0.08494016,"top":0.95730245,"width":0.053523935,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Summarize page","depth":9,"bounds":{"left":0.09059176,"top":0.96249,"width":0.042220745,"height":0.015163607},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Skip to content","depth":7,"bounds":{"left":0.38929522,"top":0.0518755,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Skip to content","depth":8,"bounds":{"left":0.38929522,"top":0.05347167,"width":0.0029920214,"height":0.21468475},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open menu","depth":11,"bounds":{"left":0.39461437,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Homepage (g then d)","depth":10,"bounds":{"left":0.40924203,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"jiminny","depth":13,"bounds":{"left":0.4225399,"top":0.06464485,"width":0.018949468,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"jiminny","depth":15,"bounds":{"left":0.4245346,"top":0.07063048,"width":0.014960106,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"app","depth":13,"bounds":{"left":0.44647607,"top":0.06464485,"width":0.017785905,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app","depth":15,"bounds":{"left":0.44847074,"top":0.07063048,"width":0.008477394,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Search or jump to…","depth":10,"bounds":{"left":0.8171542,"top":0.06464485,"width":0.06565824,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Type","depth":13,"bounds":{"left":0.8294548,"top":0.07063048,"width":0.011801862,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":13,"bounds":{"left":0.84258646,"top":0.07222666,"width":0.002493351,"height":0.011572227},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to search","depth":13,"bounds":{"left":0.8465758,"top":0.07063048,"width":0.021276595,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Chat with Copilot","depth":11,"bounds":{"left":0.88480717,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"Open Copilot…","depth":10,"bounds":{"left":0.89511305,"top":0.06464485,"width":0.008643617,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXMenuButton","text":"Create new...","depth":10,"bounds":{"left":0.91173536,"top":0.06464485,"width":0.01662234,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"All issues(g then i)","depth":10,"bounds":{"left":0.9310173,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"All pull requests","depth":10,"bounds":{"left":0.94431514,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"All repositories","depth":10,"bounds":{"left":0.95761305,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"You have unread notifications(g then n)","depth":10,"bounds":{"left":0.9709109,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
5633169623325896719
|
4283468582789973422
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pull requests · jiminny/app
Pull requests · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Send message
Your Jiminny chats aren’t used to improve our models. Gemini is AI. It can make mistakes, so double check it.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to content
Skip to content
Open menu
Homepage (g then d)
jiminny
jiminny
app
app
Search or jump to…
Type
/
to search
Chat with Copilot
Open Copilot…
Create new...
All issues(g then i)
All pull requests
All repositories
You have unread notifications(g then n)...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50890
|
1797
|
1
|
2026-05-18T08:08:28.359200+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091708359_m1.jpg...
|
Firefox
|
Pull requests · jiminny/app — Work
|
1
|
github.com/jiminny/app/pulls
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pull requests · jiminny/app
Pull requests · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Send message
Your Jiminny chats aren’t used to improve our models. Gemini is AI. It can make mistakes, so double check it.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects->map(...)->toArray()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", Laravel collections sometimes preserve their underlying associative keys.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Instead of generating a clean JSON array (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[\"en_GB\", \"bg_BG\"]","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"), it might generate a JSON object with numeric string keys (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"{\"0\": \"en_GB\", \"1\": \"bg_BG\"}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). This would violate your schema's strict definition of","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->items($schema->string())","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Append","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->values()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"before","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->toArray()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to guarantee a sequentially indexed array:","depth":26,"bounds":{"left":0.0,"top":0.0,"width":0.21423611,"height":0.022777777},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.12361111,"top":0.0,"width":0.027777778,"height":0.044444446},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.15138888,"top":0.0,"width":0.027777778,"height":0.044444446},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->map(","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"fn","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$dialect","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") => $","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"dialect","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->","depth":27,"bounds":{"left":0.0,"top":0.032222223,"width":0.011805556,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getLanguageLocale","depth":27,"bounds":{"left":0.0,"top":0.032222223,"width":0.099305555,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.033680554,"top":0.032222223,"width":0.0055555557,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"))->","depth":27,"bounds":{"left":0.03923611,"top":0.032222223,"width":0.023611112,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"values","depth":27,"bounds":{"left":0.06284722,"top":0.032222223,"width":0.034722224,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.09756944,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")->","depth":27,"bounds":{"left":0.103472225,"top":0.032222223,"width":0.017708333,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"toArray","depth":27,"bounds":{"left":0.12118056,"top":0.032222223,"width":0.040625,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.16180556,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":27,"bounds":{"left":0.16770834,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Are any of these properties (like","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$timezone","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Redo","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Share & export","depth":20,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":20,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXTextArea","text":"Enter a prompt for Gemini\nencrypted","depth":20,"on_screen":true,"value":"Enter a prompt for Gemini\nencrypted","help_text":"","role_description":"text entry area","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enter a prompt for Gemini","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"encrypted","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open upload file menu","depth":20,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tools","depth":18,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open mode picker","depth":20,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pro","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Microphone","depth":19,"on_screen":true,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Send message","depth":19,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your Jiminny chats aren’t used to improve our models. Gemini is AI. It can make mistakes, so double check it.","depth":17,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Your privacy & Gemini Opens in a new window","depth":17,"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your privacy & Gemini","depth":18,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Opens in a new window","depth":19,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
1559363224108685768
|
8751039404752897454
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pull requests · jiminny/app
Pull requests · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Send message
Your Jiminny chats aren’t used to improve our models. Gemini is AI. It can make mistakes, so double check it.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window...
|
50887
|
NULL
|
NULL
|
NULL
|
|
50891
|
1798
|
3
|
2026-05-18T08:08:31.123450+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091711123_m2.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.090259306,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"bounds":{"left":0.1100399,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"bounds":{"left":0.120678194,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"bounds":{"left":0.13131648,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"bounds":{"left":0.1419548,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"bounds":{"left":0.15259309,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"bounds":{"left":0.2711104,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"bounds":{"left":0.28573802,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"bounds":{"left":0.30568483,"top":0.035514764,"width":0.05036569,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"bounds":{"left":0.079288565,"top":0.03631285,"width":0.019946808,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"bounds":{"left":0.30568483,"top":0.037110932,"width":0.05036569,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"bounds":{"left":0.3480718,"top":0.086592175,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"bounds":{"left":0.12666224,"top":0.088986434,"width":0.030917553,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"bounds":{"left":0.1243351,"top":0.13048683,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"bounds":{"left":0.1243351,"top":0.13248204,"width":0.04105718,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"bounds":{"left":0.11336436,"top":0.13447726,"width":0.21991356,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"bounds":{"left":0.11336436,"top":0.17597765,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"bounds":{"left":0.11336436,"top":0.17757383,"width":0.03656915,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"bounds":{"left":0.15192819,"top":0.17757383,"width":0.025598405,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"bounds":{"left":0.17952128,"top":0.17757383,"width":0.036070477,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"bounds":{"left":0.14960106,"top":0.2047087,"width":0.041223403,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"bounds":{"left":0.19281915,"top":0.20590582,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.23254654,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"bounds":{"left":0.2059508,"top":0.22825219,"width":0.041888297,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"bounds":{"left":0.24983378,"top":0.22705507,"width":0.04055851,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"bounds":{"left":0.29238698,"top":0.22825219,"width":0.019448139,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.31382978,"top":0.22705507,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.22955452,"height":0.061053474},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"bounds":{"left":0.13730054,"top":0.30407023,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"bounds":{"left":0.17004654,"top":0.3028731,"width":0.00880984,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.18085106,"top":0.30407023,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.1939827,"top":0.3028731,"width":0.0014960107,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"bounds":{"left":0.12599733,"top":0.3339984,"width":0.020279255,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"bounds":{"left":0.1462766,"top":0.3339984,"width":0.10854388,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"bounds":{"left":0.13131648,"top":0.37789306,"width":0.009142287,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.32945478,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.34275267,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0056515955,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"bounds":{"left":0.13696809,"top":0.4197925,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"bounds":{"left":0.14527926,"top":0.4197925,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"bounds":{"left":0.16206782,"top":0.4197925,"width":0.027925532,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0809508,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"bounds":{"left":0.14245346,"top":0.4365523,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"bounds":{"left":0.15924202,"top":0.4365523,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"bounds":{"left":0.16755319,"top":0.4365523,"width":0.06981383,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"bounds":{"left":0.23736702,"top":0.4365523,"width":0.08111702,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"bounds":{"left":0.13131648,"top":0.4365523,"width":0.19265293,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"bounds":{"left":0.11336436,"top":0.5039904,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"bounds":{"left":0.11336436,"top":0.50558656,"width":0.09823803,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.18999335,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"bounds":{"left":0.3053524,"top":0.5339186,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.24185506,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"bounds":{"left":0.15442154,"top":0.55626494,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"bounds":{"left":0.11336436,"top":0.55506784,"width":0.24451463,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.036402926,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"bounds":{"left":0.16589096,"top":0.6097366,"width":0.055851065,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"bounds":{"left":0.2237367,"top":0.6085395,"width":0.054853722,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"bounds":{"left":0.2805851,"top":0.6097366,"width":0.044714097,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.23005319,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.04737367,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"bounds":{"left":0.17669548,"top":0.6632083,"width":0.0866024,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"bounds":{"left":0.26529256,"top":0.66201115,"width":0.023936171,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"bounds":{"left":0.2912234,"top":0.6632083,"width":0.036236703,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.22240691,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.20728059,"top":0.6855547,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"bounds":{"left":0.22041224,"top":0.6843575,"width":0.024601065,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"bounds":{"left":0.24700798,"top":0.6855547,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"bounds":{"left":0.27975398,"top":0.6843575,"width":0.025099734,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.050199468,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"bounds":{"left":0.17619681,"top":0.71548283,"width":0.005984043,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"bounds":{"left":0.18417554,"top":0.71668,"width":0.04737367,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.22490026,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"bounds":{"left":0.14095744,"top":0.7390263,"width":0.1171875,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"bounds":{"left":0.2601396,"top":0.7378292,"width":0.04089096,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"bounds":{"left":0.30302528,"top":0.7390263,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"bounds":{"left":0.31898272,"top":0.7378292,"width":0.006482713,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.3274601,"top":0.7390263,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.34075797,"top":0.7378292,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"bounds":{"left":0.11336436,"top":0.7793296,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"bounds":{"left":0.11336436,"top":0.78092575,"width":0.08809841,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.80806065,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"bounds":{"left":0.14960106,"top":0.80806065,"width":0.030751329,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-7497451843669592982
|
-903657849538869850
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using...
|
50889
|
NULL
|
NULL
|
NULL
|
|
50892
|
1797
|
2
|
2026-05-18T08:08:33.166247+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091713166_m1.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
2635364400906536738
|
-903657841158650458
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50893
|
1798
|
4
|
2026-05-18T08:08:33.193472+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091713193_m2.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.090259306,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"bounds":{"left":0.1100399,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"bounds":{"left":0.120678194,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"bounds":{"left":0.13131648,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"bounds":{"left":0.1419548,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"bounds":{"left":0.15259309,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"bounds":{"left":0.2711104,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"bounds":{"left":0.28573802,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"bounds":{"left":0.30568483,"top":0.035514764,"width":0.05036569,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"bounds":{"left":0.079288565,"top":0.03631285,"width":0.019946808,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"bounds":{"left":0.30568483,"top":0.037110932,"width":0.05036569,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"bounds":{"left":0.3480718,"top":0.086592175,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"bounds":{"left":0.12666224,"top":0.088986434,"width":0.030917553,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"bounds":{"left":0.1243351,"top":0.13048683,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"bounds":{"left":0.1243351,"top":0.13248204,"width":0.04105718,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"bounds":{"left":0.11336436,"top":0.13447726,"width":0.21991356,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"bounds":{"left":0.11336436,"top":0.17597765,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"bounds":{"left":0.11336436,"top":0.17757383,"width":0.03656915,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"bounds":{"left":0.15192819,"top":0.17757383,"width":0.025598405,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"bounds":{"left":0.17952128,"top":0.17757383,"width":0.036070477,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"bounds":{"left":0.14960106,"top":0.2047087,"width":0.041223403,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"bounds":{"left":0.19281915,"top":0.20590582,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.23254654,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"bounds":{"left":0.2059508,"top":0.22825219,"width":0.041888297,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"bounds":{"left":0.24983378,"top":0.22705507,"width":0.04055851,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"bounds":{"left":0.29238698,"top":0.22825219,"width":0.019448139,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.31382978,"top":0.22705507,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.22955452,"height":0.061053474},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"bounds":{"left":0.13730054,"top":0.30407023,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"bounds":{"left":0.17004654,"top":0.3028731,"width":0.00880984,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.18085106,"top":0.30407023,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.1939827,"top":0.3028731,"width":0.0014960107,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"bounds":{"left":0.12599733,"top":0.3339984,"width":0.020279255,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"bounds":{"left":0.1462766,"top":0.3339984,"width":0.10854388,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"bounds":{"left":0.13131648,"top":0.37789306,"width":0.009142287,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.32945478,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.34275267,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0056515955,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"bounds":{"left":0.13696809,"top":0.4197925,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"bounds":{"left":0.14527926,"top":0.4197925,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"bounds":{"left":0.16206782,"top":0.4197925,"width":0.027925532,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0809508,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"bounds":{"left":0.14245346,"top":0.4365523,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"bounds":{"left":0.15924202,"top":0.4365523,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"bounds":{"left":0.16755319,"top":0.4365523,"width":0.06981383,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"bounds":{"left":0.23736702,"top":0.4365523,"width":0.08111702,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"bounds":{"left":0.13131648,"top":0.4365523,"width":0.19265293,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"bounds":{"left":0.11336436,"top":0.5039904,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"bounds":{"left":0.11336436,"top":0.50558656,"width":0.09823803,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.18999335,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"bounds":{"left":0.3053524,"top":0.5339186,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.24185506,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"bounds":{"left":0.15442154,"top":0.55626494,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"bounds":{"left":0.11336436,"top":0.55506784,"width":0.24451463,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.036402926,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"bounds":{"left":0.16589096,"top":0.6097366,"width":0.055851065,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"bounds":{"left":0.2237367,"top":0.6085395,"width":0.054853722,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"bounds":{"left":0.2805851,"top":0.6097366,"width":0.044714097,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.23005319,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.04737367,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"bounds":{"left":0.17669548,"top":0.6632083,"width":0.0866024,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"bounds":{"left":0.26529256,"top":0.66201115,"width":0.023936171,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"bounds":{"left":0.2912234,"top":0.6632083,"width":0.036236703,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.22240691,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.20728059,"top":0.6855547,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"bounds":{"left":0.22041224,"top":0.6843575,"width":0.024601065,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"bounds":{"left":0.24700798,"top":0.6855547,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"bounds":{"left":0.27975398,"top":0.6843575,"width":0.025099734,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.050199468,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"bounds":{"left":0.17619681,"top":0.71548283,"width":0.005984043,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"bounds":{"left":0.18417554,"top":0.71668,"width":0.04737367,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.22490026,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"bounds":{"left":0.14095744,"top":0.7390263,"width":0.1171875,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"bounds":{"left":0.2601396,"top":0.7378292,"width":0.04089096,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"bounds":{"left":0.30302528,"top":0.7390263,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"bounds":{"left":0.31898272,"top":0.7378292,"width":0.006482713,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.3274601,"top":0.7390263,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.34075797,"top":0.7378292,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"bounds":{"left":0.11336436,"top":0.7793296,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"bounds":{"left":0.11336436,"top":0.78092575,"width":0.08809841,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.80806065,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"bounds":{"left":0.14960106,"top":0.80806065,"width":0.030751329,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects->map(...)->toArray()","depth":27,"bounds":{"left":0.18234707,"top":0.8092578,"width":0.10605053,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", Laravel collections sometimes preserve their underlying associative keys.","depth":26,"bounds":{"left":0.12599733,"top":0.80806065,"width":0.21343085,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.86153233,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Instead of generating a clean JSON array (e.g.,","depth":26,"bounds":{"left":0.15708111,"top":0.86153233,"width":0.115359046,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
4681045508978671930
|
-904679295874628186
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50894
|
1797
|
3
|
2026-05-18T08:08:34.221268+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091714221_m1.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-4899050126974704341
|
-903657841158650458
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:...
|
50892
|
NULL
|
NULL
|
NULL
|
|
50895
|
1798
|
5
|
2026-05-18T08:08:34.231136+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091714231_m2.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.090259306,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"bounds":{"left":0.1100399,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"bounds":{"left":0.120678194,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"bounds":{"left":0.13131648,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"bounds":{"left":0.1419548,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"bounds":{"left":0.15259309,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"bounds":{"left":0.2711104,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"bounds":{"left":0.28573802,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"bounds":{"left":0.30568483,"top":0.035514764,"width":0.05036569,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"bounds":{"left":0.079288565,"top":0.03631285,"width":0.019946808,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"bounds":{"left":0.30568483,"top":0.037110932,"width":0.05036569,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"bounds":{"left":0.3480718,"top":0.086592175,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"bounds":{"left":0.12666224,"top":0.088986434,"width":0.030917553,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"bounds":{"left":0.1243351,"top":0.13048683,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"bounds":{"left":0.1243351,"top":0.13248204,"width":0.04105718,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"bounds":{"left":0.11336436,"top":0.13447726,"width":0.21991356,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"bounds":{"left":0.11336436,"top":0.17597765,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"bounds":{"left":0.11336436,"top":0.17757383,"width":0.03656915,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"bounds":{"left":0.15192819,"top":0.17757383,"width":0.025598405,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"bounds":{"left":0.17952128,"top":0.17757383,"width":0.036070477,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"bounds":{"left":0.14960106,"top":0.2047087,"width":0.041223403,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"bounds":{"left":0.19281915,"top":0.20590582,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.23254654,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"bounds":{"left":0.2059508,"top":0.22825219,"width":0.041888297,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"bounds":{"left":0.24983378,"top":0.22705507,"width":0.04055851,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"bounds":{"left":0.29238698,"top":0.22825219,"width":0.019448139,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.31382978,"top":0.22705507,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.22955452,"height":0.061053474},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"bounds":{"left":0.13730054,"top":0.30407023,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"bounds":{"left":0.17004654,"top":0.3028731,"width":0.00880984,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.18085106,"top":0.30407023,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.1939827,"top":0.3028731,"width":0.0014960107,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"bounds":{"left":0.12599733,"top":0.3339984,"width":0.020279255,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"bounds":{"left":0.1462766,"top":0.3339984,"width":0.10854388,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"bounds":{"left":0.13131648,"top":0.37789306,"width":0.009142287,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.32945478,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.34275267,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0056515955,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"bounds":{"left":0.13696809,"top":0.4197925,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"bounds":{"left":0.14527926,"top":0.4197925,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"bounds":{"left":0.16206782,"top":0.4197925,"width":0.027925532,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0809508,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"bounds":{"left":0.14245346,"top":0.4365523,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"bounds":{"left":0.15924202,"top":0.4365523,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"bounds":{"left":0.16755319,"top":0.4365523,"width":0.06981383,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"bounds":{"left":0.23736702,"top":0.4365523,"width":0.08111702,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"bounds":{"left":0.13131648,"top":0.4365523,"width":0.19265293,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"bounds":{"left":0.11336436,"top":0.5039904,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"bounds":{"left":0.11336436,"top":0.50558656,"width":0.09823803,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.18999335,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"bounds":{"left":0.3053524,"top":0.5339186,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.24185506,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"bounds":{"left":0.15442154,"top":0.55626494,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"bounds":{"left":0.11336436,"top":0.55506784,"width":0.24451463,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.036402926,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"bounds":{"left":0.16589096,"top":0.6097366,"width":0.055851065,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"bounds":{"left":0.2237367,"top":0.6085395,"width":0.054853722,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"bounds":{"left":0.2805851,"top":0.6097366,"width":0.044714097,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.23005319,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.04737367,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"bounds":{"left":0.17669548,"top":0.6632083,"width":0.0866024,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"bounds":{"left":0.26529256,"top":0.66201115,"width":0.023936171,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"bounds":{"left":0.2912234,"top":0.6632083,"width":0.036236703,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
8997394970643195274
|
-904679287249042010
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()...
|
50893
|
NULL
|
NULL
|
NULL
|
|
50896
|
1797
|
4
|
2026-05-18T08:08:38.972675+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091718972_m1.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects->map(...)->toArray()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", Laravel collections sometimes preserve their underlying associative keys.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Instead of generating a clean JSON array (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[\"en_GB\", \"bg_BG\"]","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"), it might generate a JSON object with numeric string keys (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"{\"0\": \"en_GB\", \"1\": \"bg_BG\"}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). This would violate your schema's strict definition of","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->items($schema->string())","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Append","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->values()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"before","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->toArray()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to guarantee a sequentially indexed array:","depth":26,"bounds":{"left":0.0,"top":0.0,"width":0.21423611,"height":0.022777777},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.12361111,"top":0.0,"width":0.027777778,"height":0.044444446},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.15138888,"top":0.0,"width":0.027777778,"height":0.044444446},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->map(","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"fn","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$dialect","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") => $","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"dialect","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->","depth":27,"bounds":{"left":0.0,"top":0.032222223,"width":0.011805556,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getLanguageLocale","depth":27,"bounds":{"left":0.0,"top":0.032222223,"width":0.099305555,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.033680554,"top":0.032222223,"width":0.0055555557,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"))->","depth":27,"bounds":{"left":0.03923611,"top":0.032222223,"width":0.023611112,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"values","depth":27,"bounds":{"left":0.06284722,"top":0.032222223,"width":0.034722224,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.09756944,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")->","depth":27,"bounds":{"left":0.103472225,"top":0.032222223,"width":0.017708333,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"toArray","depth":27,"bounds":{"left":0.12118056,"top":0.032222223,"width":0.040625,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.16180556,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":27,"bounds":{"left":0.16770834,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Are any of these properties (like","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$timezone","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Redo","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Share & export","depth":20,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":20,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false}]...
|
-5026101708527775443
|
8174577552895903142
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50897
|
1798
|
6
|
2026-05-18T08:08:39.363360+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091719363_m2.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.090259306,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-8600596612156681088
|
-1047703895151868530
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50898
|
1797
|
5
|
2026-05-18T08:08:42.025067+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091722025_m1.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","depth":3,"on_screen":true,"value":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5193179490567620149
|
3986944911298471426
|
visual_change
|
hybrid
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
iTerm2ShellEditViewSessionScriptsProfilesWindowHelpPreparation for Refi... in 3 h 52 m100% (78• Mon 18 May 11:08:41DOCKER (docker-compose)O ₴2T81DOCKER81DEV (-zsh)11DOCKER (docker-compose)docker_lamp_1"talkdesk''/usr/local/bin/php'activity:sync 'ringcentral'--from='2026-05-18 07:51:00'--to='2026-05-18 08:07:00' > */proc/1/fddocker_1amp_1docker_lamp_1docker_lamp_1run_artisan_schedule:Done waiting forschedule: run2026-05-18 08:07:36 Jiminny\Jobs\Mailbox\CreateBatchesFRdocker_lamp_12026-05-1808:07:36 Jiminny\Jobs\Mailbox\CreateBatches92.34msDONEdocker_lamp_1docker_lamp_12026-05-1808:08:08 Running['artisan' meeting-bot:schedule-bot]...2S DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan'meeting-bot:schedule-bot > */proc/1/fd/1'2>&1docker_lamp_12026-05-18 08:08:11 Running ['artisan' dialers:monitor-activities] .1S DONEdocker_lamp_1, '/usr/local/bin/php' 'artisan' dialers:monitor-activities > '/proc/1/fd/1' 2>&1docker_lamp_1 |2026-05-18 08:08:13 Running ['artisan' jiminny:monitor-social-accountdocker_lamp_1proc/1/fd/1'docker_lamp_1" '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > '/2026-05-18 08:08:17 Running ['artisan'mailbox:skip-lists:refresh]docker_lamp_11/fd/1' 2>&1docker_lamp_11 '/usr/local/bin/php' 'artisan'mailbox:skip-lists:refresh> */proc/2026-05-18 08:08:21 Running ['artisan'mailbox:batch:process --max-badocker_1amp_1• '/usr/local/bin/php' 'artisan'mailbox:batch:process --max-batches=*/proc/1/fd/1' 2>&1docker_lamp_1 |2026-05-18 08:08:24 Running ['artisan' conference:monitor:count] ….docker_lamp_11 l '/usr/local/bin/php' 'artisan' conference:monitor:count › */proc/1/fd/1'2>&1docker_lamp_12026-05-18 08:08:27Running ['artisan' mailbox:batch:retry-failed --max-batches=15] in background1.98ms DONEdocker_lamp_11• ('/usr/local/bin/php' 'artisan'mailbox:batch:retry-failed--max-batches=15 >'/proc/1/fd/1' 2>&1 ; '/usr/local/bin/php''artisan'schedule: finish"framework/schedule-390defd641effba0f73a895e426ded4cf2ba7f11" "$?") > '/dev/null' 2>&1 &docker_lamp_1docker_lamp_1I run_artisan_schedule: Done waiting for schedule:runAPP (-zsh)*3screenpipe"Y2PROD (ssh)Run'do-release-upgrade'to upgrade to it.*** System restart required ***Last login: Thu May 14 07:41:362026 from 212.5.153.87lukas@jiminny-prod-bastion:~$X L3 EU (-zsh)Last login: Sat May 16 18:04:33on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ I|T4STAGE (ssh)See [URL_WITH_CREDENTIALS] ~ $ IX W7 ExT(-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsEXTENSIONPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|View in Docker Desktop@ View ConfigEnable Watch...
|
50896
|
NULL
|
NULL
|
NULL
|
|
50899
|
1798
|
7
|
2026-05-18T08:08:43.139957+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091723139_m2.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","depth":3,"bounds":{"left":0.8753325,"top":0.8387869,"width":0.11037234,"height":0.027134877},"on_screen":true,"value":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.8387869,"width":0.08809841,"height":0.027134877},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Create pull request","depth":2,"bounds":{"left":0.8753325,"top":0.87150836,"width":0.038896278,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.91300875,"width":0.100398935,"height":0.013567438},"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.11037234,"height":0.013567438},"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.059507977,"height":0.013567438},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-1946226836277469217
|
-1415053868601834186
|
click
|
accessibility
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50900
|
1797
|
6
|
2026-05-18T08:08:45.011924+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091725011_m1.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, '...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","depth":3,"on_screen":true,"value":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Create pull request","depth":2,"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files committed","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chrome","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Firefox","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Safari","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.019444445,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-2869658411287101143
|
2696331441845038283
|
visual_change
|
accessibility
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, '...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50902
|
1798
|
8
|
2026-05-18T08:08:46.365443+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091726365_m2.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","depth":3,"bounds":{"left":0.8753325,"top":0.8387869,"width":0.11037234,"height":0.027134877},"on_screen":true,"value":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.8387869,"width":0.08809841,"height":0.027134877},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Create pull request","depth":2,"bounds":{"left":0.8753325,"top":0.87150836,"width":0.038896278,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.91300875,"width":0.100398935,"height":0.013567438},"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.11037234,"height":0.013567438},"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.059507977,"height":0.013567438},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.14228724,"top":0.30327216,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.1549202,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"bounds":{"left":0.16589096,"top":0.30247405,"width":0.043882977,"height":0.015961692},"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.21875,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.2287234,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.23736702,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.24601063,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"bounds":{"left":0.25964096,"top":0.30167598,"width":0.025598405,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.28523937,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.29388297,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.3025266,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.31117022,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.35904256,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8545317049828955656
|
-3673730028314454138
|
click
|
accessibility
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes...
|
50899
|
NULL
|
NULL
|
NULL
|
|
50901
|
1797
|
7
|
2026-05-18T08:08:46.398154+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091726398_m1.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","depth":3,"on_screen":true,"value":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Create pull request","depth":2,"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files committed","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1270974553645142004
|
-3673730028314454138
|
click
|
accessibility
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close...
|
50900
|
NULL
|
NULL
|
NULL
|
|
50904
|
1798
|
9
|
2026-05-18T08:08:47.030740+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091727030_m2.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","depth":3,"bounds":{"left":0.8753325,"top":0.8387869,"width":0.11037234,"height":0.027134877},"on_screen":true,"value":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.8387869,"width":0.08809841,"height":0.027134877},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Create pull request","depth":2,"bounds":{"left":0.8753325,"top":0.87150836,"width":0.038896278,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.91300875,"width":0.100398935,"height":0.013567438},"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.11037234,"height":0.013567438},"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.059507977,"height":0.013567438},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.14228724,"top":0.30327216,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.1549202,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"bounds":{"left":0.16589096,"top":0.30247405,"width":0.043882977,"height":0.015961692},"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.21875,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.2287234,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.23736702,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.24601063,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"bounds":{"left":0.25964096,"top":0.30167598,"width":0.025598405,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.28523937,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.29388297,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.3025266,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.31117022,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.35904256,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8526035053389563478
|
-3673809192984928378
|
click
|
accessibility
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50903
|
1797
|
8
|
2026-05-18T08:08:47.038040+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091727038_m1.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","depth":3,"on_screen":true,"value":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Create pull request","depth":2,"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files committed","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chrome","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-2517250343956987527
|
-3673800396889809018
|
click
|
accessibility
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50905
|
1798
|
10
|
2026-05-18T08:08:50.007761+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091730007_m2.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, '...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","depth":3,"bounds":{"left":0.8753325,"top":0.8387869,"width":0.11037234,"height":0.027134877},"on_screen":true,"value":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.8387869,"width":0.08809841,"height":0.027134877},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Create pull request","depth":2,"bounds":{"left":0.8753325,"top":0.87150836,"width":0.038896278,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.91300875,"width":0.100398935,"height":0.013567438},"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.11037234,"height":0.013567438},"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.059507977,"height":0.013567438},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.14228724,"top":0.30327216,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.1549202,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"bounds":{"left":0.16589096,"top":0.30247405,"width":0.043882977,"height":0.015961692},"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.21875,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.2287234,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.23736702,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.24601063,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"bounds":{"left":0.25964096,"top":0.30167598,"width":0.025598405,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.28523937,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.29388297,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.3025266,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.31117022,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.35904256,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chrome","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Firefox","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Safari","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.31648937,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.32646278,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.33643618,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3464096,"top":0.3320032,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.35771278,"top":0.33040702,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.3650266,"top":0.33040702,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00930851,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-2869658411287101143
|
2696331441845038283
|
visual_change
|
accessibility
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, '...
|
50904
|
NULL
|
NULL
|
NULL
|
|
50906
|
1797
|
9
|
2026-05-18T08:08:50.877817+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091730877_m1.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, '...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","depth":3,"on_screen":true,"value":"Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Create pull request","depth":2,"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6 files committed","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chrome","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Firefox","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Safari","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.019444445,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-2869658411287101143
|
2696331441845038283
|
click
|
accessibility
|
NULL
|
Pushed pipedrive-sdk-poc to new branch origin/pipe Pushed pipedrive-sdk-poc to new branch origin/pipedrive-sdk-poc
text/html
text/html
text/html
Create pull request
6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, '...
|
50903
|
NULL
|
NULL
|
NULL
|
|
50908
|
1798
|
11
|
2026-05-18T08:08:53.644835+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091733644_m2.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
6 files committed
pipedrive offical SDK v2 POC
tex 6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post('trim', [KioskActivityController::class, 'trimActivity']);
...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"6 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.91300875,"width":0.100398935,"height":0.013567438},"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.11037234,"height":0.013567438},"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.92897046,"width":0.059507977,"height":0.013567438},"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.05618351,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.14228724,"top":0.30327216,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.1549202,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"bounds":{"left":0.16589096,"top":0.30247405,"width":0.043882977,"height":0.015961692},"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.21875,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.2287234,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.23736702,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.24601063,"top":0.30247405,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"bounds":{"left":0.25964096,"top":0.30167598,"width":0.025598405,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.28523937,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.29388297,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.3025266,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.31117022,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.35904256,"top":0.3008779,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chrome","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Firefox","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Safari","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.31648937,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.32646278,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.33643618,"top":0.3320032,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3464096,"top":0.3320032,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.35771278,"top":0.33040702,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.3650266,"top":0.33040702,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00930851,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7874037434345827966
|
2696331441572408523
|
click
|
accessibility
|
NULL
|
6 files committed
pipedrive offical SDK v2 POC
tex 6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post('trim', [KioskActivityController::class, 'trimActivity']);
...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50907
|
1797
|
10
|
2026-05-18T08:08:53.652959+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091733652_m1.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
6 files committed
pipedrive offical SDK v2 POC
tex 6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post('trim', [KioskActivityController::class, 'trimActivity']);
...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"6 files committed","depth":2,"on_screen":true,"role_description":"text"},{"role":"AXTextField","text":"pipedrive offical SDK v2 POC","depth":3,"on_screen":true,"value":"pipedrive offical SDK v2 POC","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":true,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"on_screen":false,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"on_screen":true,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"pipedrive-sdk-poc, menu","depth":5,"on_screen":true,"help_text":"Git Branch: pipedrive-sdk-poc","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"organiza","depth":4,"on_screen":true,"value":"organiza","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/10","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Built-in Preview","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Chrome","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Firefox","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Safari","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\n/**\n * API routes.\n *\n * @see \\Jiminny\\Providers\\RouteServiceProvider\n *\n * @var Router $router\n */\n\nuse Illuminate\\Routing\\Router;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\DealInsights\\Forecast\\Forecast;\nuse Jiminny\\Component\\Router\\Routes;\nuse Jiminny\\Contracts\\Acl\\PermissionEnum;\nuse Jiminny\\Http\\Controllers;\nuse Jiminny\\Http\\Controllers\\API\\ActivityController;\nuse Jiminny\\Http\\Controllers\\API\\AiCrmNotesController;\nuse Jiminny\\Http\\Controllers\\API\\ClientTokenController;\nuse Jiminny\\Http\\Controllers\\API\\CrmController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAiCallScoringController;\nuse Jiminny\\Http\\Controllers\\ConferencesOptInOutController;\nuse Jiminny\\Http\\Controllers\\API\\DealRiskController;\nuse Jiminny\\Http\\Controllers\\API\\InstantMeetingController;\nuse Jiminny\\Http\\Controllers\\API\\LanguageController;\nuse Jiminny\\Http\\Controllers\\API\\LiveFeedController;\nuse Jiminny\\Http\\Controllers\\API\\MeetingsController;\nuse Jiminny\\Http\\Controllers\\API\\MessageController;\nuse Jiminny\\Http\\Controllers\\API\\MetadataController;\nuse Jiminny\\Http\\Controllers\\API\\MobileSettingsController;\nuse Jiminny\\Http\\Controllers\\API\\MomentController;\nuse Jiminny\\Http\\Controllers\\API\\NudgeController;\nuse Jiminny\\Http\\Controllers\\API\\NumberAllocatorController;\nuse Jiminny\\Http\\Controllers\\API\\Opportunity\\CommentsController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationLicensesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationMembersController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRetentionPolicyController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationRolesController;\nuse Jiminny\\Http\\Controllers\\API\\OrganizationSyncController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\OnDemandController;\nuse Jiminny\\Http\\Controllers\\API\\Page\\PlaybackController;\nuse Jiminny\\Http\\Controllers\\API\\PartnerController;\nuse Jiminny\\Http\\Controllers\\API\\PhoneNumberController;\nuse Jiminny\\Http\\Controllers\\API\\PlaylistController;\nuse Jiminny\\Http\\Controllers\\API\\Settings\\EmailSyncController;\nuse Jiminny\\Http\\Controllers\\API\\SidekickController;\nuse Jiminny\\Http\\Controllers\\API\\SoftphoneController;\nuse Jiminny\\Http\\Controllers\\API\\SubscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiAutomationController;\nuse Jiminny\\Http\\Controllers\\API\\TeamAiContextController;\nuse Jiminny\\Http\\Controllers\\API\\TeamController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ActivityStatsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\CoachingFeedbacksController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\DashboardController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\EngagementController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TeamInsightsAutomatedCallScoresController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\ThemeTopicsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsights\\TopicsInDealsController;\nuse Jiminny\\Http\\Controllers\\API\\TeamInsightsController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\ThemeController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicController;\nuse Jiminny\\Http\\Controllers\\API\\Themes\\TopicTriggerController;\nuse Jiminny\\Http\\Controllers\\API\\TranscriptionController;\nuse Jiminny\\Http\\Controllers\\API\\TranslationController;\nuse Jiminny\\Http\\Controllers\\API\\UserAutomatedReports\\UserAutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\API\\UserController;\nuse Jiminny\\Http\\Controllers\\API\\VocabularyController;\nuse Jiminny\\Http\\Controllers\\Auth\\ExtensionController;\nuse Jiminny\\Http\\Controllers\\Auth\\SocialController;\nuse Jiminny\\Http\\Controllers\\ExportController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\ActivityController as KioskActivityController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\AutomatedReportsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\MediaPipelineController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\OrganizationsController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\PartnersController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\SearchController;\nuse Jiminny\\Http\\Controllers\\Kiosk\\Teams\\OnboardController;\nuse Jiminny\\Http\\Controllers\\NotificationController;\nuse Jiminny\\Http\\Controllers\\Settings\\GroupController;\nuse Jiminny\\Http\\Controllers\\Settings\\JobTitleController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookCategoryController;\nuse Jiminny\\Http\\Controllers\\Settings\\PlaybookController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\IntegrationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\InvitationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamActivityController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamCoachingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamConferenceSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamController as OrganizationController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamDealInsightsSettingController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamMemberController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamPhotoController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamRecordingSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSettingsController;\nuse Jiminny\\Http\\Controllers\\Settings\\Teams\\TeamSoftphoneSettingsController;\nuse Jiminny\\Http\\Controllers\\TeamSetupController;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\PlaybackTheme;\nuse Jiminny\\Models\\SocialAccount;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models\\Vocabulary;\nuse Jiminny\\Repositories;\nuse Jiminny\\Mcp\\Servers\\JiminnyServer;\nuse Laravel\\Mcp\\Facades\\Mcp;\n\n// mcp.audit MUST stay outermost so its $next($request) call wraps the auth\n// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections\n// short-circuit before McpAuditMiddleware::handle ever runs and we lose\n// audit rows for exactly the requests the security log most needs to capture.\n// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing\n// pre-auth is safe.\nMcp::web('/mcp', JiminnyServer::class)\n ->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/metadata/extension-app', [MetadataController::class, 'extension']);\n\n $router->get('/', [NumberAllocatorController::class, 'generate']);\n $router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);\n\n $router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('instant-meeting.start');\n\n // Meeting creation endpoint for Outlook add-in\n $router->post('/meetings', [MeetingsController::class, 'create'])\n ->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])\n ->name('meetings.create');\n\n // Number provisioning and search.\n $router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);\n $router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);\n\n $router->group(['prefix' => 'deal-insights'], static function (Router $router): void {\n $router->get('/forecast', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'getForecast',\n ])->defaults('period', Forecast::PERIOD_QUARTER);\n\n $router->get('/deals/{stage?}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'list',\n ])->defaults('stage', \\Jiminny\\Component\\DealInsights\\CriteriaInterface::STAGE_ALL);\n\n $router->get('/details/details-daily/{opportunityId}/{date}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'detailsDaily',\n ]);\n\n $router->put('/deals/{opportunity}/edit-fields', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'updateFields',\n ]);\n\n $router->get('/externalId/{dealId}', [\n Controllers\\API\\DealInsights\\DealsController::class,\n 'externalDealId',\n ]);\n\n $router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);\n });\n\n $router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])\n ->name('team_insights.users');\n\n $router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])\n ->name('team_insights.dashboard');\n\n // Team Insights - Coaching Feedbacks\n $router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])\n ->name('team_insights.coaching_feedbacks_over_time');\n\n $router\n ->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])\n ->name('team_insights.coaching_feedbacks_over_time.download');\n\n $router->get(\n '/team-insights/coaching-feedbacks-over-time/drill-down',\n [CoachingFeedbacksController::class, 'drillDown'],\n )->name('team_insights.coaching_feedbacks_over_time.drill_down');\n\n // Team Insights - Automated Call Scores\n $router->get(\n '/team-insights/automated-call-scores-over-time',\n [TeamInsightsAutomatedCallScoresController::class, 'index'],\n )->name('team_insights.automated_call_scores_over_time.index');\n\n $router->get(\n '/team-insights/automated-call-scores-over-time/drill-down',\n [TeamInsightsAutomatedCallScoresController::class, 'show'],\n )->name('team_insights.automated_call_scores_over_time.show');\n\n // Team Insights - AI Call Scoring\n $router->get(\n '/team-insights/ai-call-scoring-over-time',\n [TeamInsightsAiCallScoringController::class, 'index'],\n )->name('team_insights.ai_call_scoring_over_time.index');\n\n $router->get(\n '/team-insights/ai-call-scoring-over-time/drill-down',\n [TeamInsightsAiCallScoringController::class, 'show'],\n )->name('team_insights.ai_call_scoring_over_time.show');\n\n $router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])\n ->name('team_insights.engagement');\n\n $router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])\n ->name('team_insights.engagement.drill_down');\n\n $router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])\n ->name('team_insights.topics.index');\n\n $router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])\n ->name('team_insights.topics.show');\n\n $router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])\n ->name('team_insights.topics.drill_down');\n\n $router->group(['prefix' => 'team-insights'], static function (Router $router): void {\n $router->group(['prefix' => 'conversations'], static function (Router $router): void {\n $router->get('/', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'fetch',\n ]);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{activityChannel}/{drillDownType}', [\n Controllers\\API\\TeamInsights\\ConversationsController::class,\n 'drillDown',\n ])\n ->where(\n 'activityChannel',\n Collection::make(Models\\Activity::CHANNELS)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(Repositories\\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)\n ->join('|'),\n );\n });\n });\n\n $router->group(['prefix' => 'coaching'], static function (Router $router): void {\n $router->get('/', [EngagementController::class, 'fetch']);\n\n $router->group(['prefix' => 'drill-down'], static function (Router $router): void {\n $router\n ->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])\n ->where(\n 'coachingType',\n Collection::make(EngagementController::COACHING_TYPES)->join('|'),\n )\n ->where(\n 'drillDownType',\n Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),\n );\n });\n });\n });\n\n $router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])\n ->name('topics_in_deals.topics');\n $router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])\n ->name('topics_in_deals.topic_triggers');\n $router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])\n ->name('topics_in_deals.comparison');\n\n // CRM actions.\n $router->group(['prefix' => 'crm'], static function (Router $router): void {\n $router->get('/search', [CrmController::class, 'search']);\n $router->get('/opportunity', [CrmController::class, 'opportunities']);\n $router->get('/customers', [CrmController::class, 'customers']);\n $router->get('/accounts', [CrmController::class, 'accounts']);\n $router->get('/contacts', [CrmController::class, 'contacts']);\n $router->get('/leads', [CrmController::class, 'leads']);\n $router->get('/tasks', [CrmController::class, 'activities']);\n $router->get('/layouts', [CrmController::class, 'layouts']);\n });\n\n // AI CRM notes.\n $router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {\n $router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);\n $router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);\n $router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);\n\n $router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);\n $router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);\n $router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);\n });\n\n // Automated Reports\n $router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);\n\n $router->group(\n [\n 'prefix' => 'automated-reports',\n 'middleware' => 'can:canAccessAiReports,' . User::class,\n ],\n static function (Router $router): void {\n $router->get('/', [UserAutomatedReportsController::class, 'list']);\n $router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);\n }\n );\n\n // Setup New Team / Trial\n $router->get('/features', [TeamSetupController::class, 'features']);\n $router->get('/tiers', [TeamSetupController::class, 'tiers']);\n $router->get('/calendars', [TeamSetupController::class, 'calendars']);\n $router->get('/crm-services', [TeamSetupController::class, 'crmServices']);\n $router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);\n $router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);\n $router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);\n\n // Notifications\n $router->get('/notifications/recent', [NotificationController::class, 'notifications']);\n $router->put('/notifications/read', [NotificationController::class, 'markAsRead']);\n $router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);\n $router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);\n\n // Live feed\n $router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);\n\n // Languages\n $router->get('/languages', [LanguageController::class, 'list']);\n\n // The whole settings section will be moved out in a separate file\n $router->group(['prefix' => '/settings'], static function (Router $router): void {\n $router->group(['prefix' => '/organizations'], static function (Router $router): void {\n $router\n ->middleware(['can:kiosk,' . User::class])\n ->post('/', [OrganizationController::class, 'store'])\n ->name('kiosk.organizations.store');\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {\n // Sync fields and team metadata\n $router->post('/fields/sync', [OrganizationSyncController::class, 'index'])\n ->name('api.sync.fields');\n\n // Conference Preferences.\n $router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])\n ->name('update.bot.avatar');\n\n // Roles.\n $router->get('/roles', [OrganizationRolesController::class, 'index'])\n ->name('api.roles.index');\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],\n static function (Router $router): void {\n $router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])\n ->name('api.retention_policy.index');\n\n $router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])\n ->name('api.retention_policy.update');\n }\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],\n static function (Router $router): void {\n // Invitations.\n $router->get('/invitations', [InvitationController::class, 'index'])\n ->name('api.invitations.index');\n $router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])\n ->name('api.invitations.resend');\n $router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])\n ->name('api.invitations.delete');\n $router->post('/invitations', [InvitationController::class, 'store'])\n ->name('api.invitations.store');\n },\n );\n\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],\n static function (Router $router): void {\n // Groups.\n $router->post('/groups', [GroupController::class, 'store']);\n $router->get('/groups/{group}', [GroupController::class, 'show']);\n $router->put('/groups/{group}', [GroupController::class, 'update']);\n\n $router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);\n\n $router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);\n\n // Sidekick settings\n $router->group(\n ['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],\n static function (Router $router): void {\n $router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);\n $router\n ->post(\n '/group/{group}/sidekick',\n [SidekickController::class, 'setSidekickSettings'],\n )\n ->middleware(['can:updateSidekickSettings,group'])\n ->name('api.sidekick_settings.update');\n $router\n ->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])\n ->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])\n ->name('api.sidekick_settings.update_all');\n },\n );\n\n $router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);\n $router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);\n\n // CRM Layout Management\n $router->group(['prefix' => 'layouts'], static function (Router $router): void {\n $router->get(\n '/{type}',\n [Controllers\\API\\LayoutManagementController::class, 'list'],\n )->name('layouts.list');\n\n $router->put(\n '/{layout}',\n [Controllers\\API\\LayoutManagementController::class, 'update'],\n )->name('layouts.update');\n });\n\n // Users.\n $router->put('/users/{user}', [TeamMemberController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.update');\n $router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])\n ->name('api.users.deactivate');\n\n $router->group(\n [\n 'prefix' => 'vocabulary',\n 'middleware' => 'can:manage,' . Vocabulary::class,\n ],\n static function (Router $router): void {\n $router\n ->get('/', [VocabularyController::class, 'list'])\n ->name('api.vocabulary.index');\n $router\n ->post('/', [VocabularyController::class, 'update'])\n ->name('api.vocabulary.create');\n\n $router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {\n $router\n ->put('/', [VocabularyController::class, 'update'])\n ->middleware('can:update,vocabulary')\n ->name('api.vocabulary.update');\n $router\n ->delete('/', [VocabularyController::class, 'delete'])\n ->middleware('can:delete,vocabulary')\n ->name('api.vocabulary.delete');\n });\n },\n );\n\n $router->group(['prefix' => 'ai-context'], static function (Router $router): void {\n $router->get('/', [TeamAiContextController::class, 'index'])\n ->name('api.ai_context.get');\n $router->post('/', [TeamAiContextController::class, 'store'])\n ->name('api.ai_context.store');\n });\n\n $router->group(['prefix' => 'ai-automation'], static function (Router $router): void {\n $router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])\n ->name('api.automation.templates.fields.test-prompt');\n // List CRM fields per object type\n $router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])\n ->name('api.automation.fields');\n\n // List DealStages fields per object type\n $router->get('/stages', [TeamAiAutomationController::class, 'stages'])\n ->name('api.automation.stages');\n // Create CRM AI template\n $router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])\n ->name('api.automation.templates.create');\n\n // Export CRM updates\n $router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])\n ->name('api.automation.templates.export-crm-updates');\n\n // Update CRM AI template\n $router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])\n ->name('api.automation.templates.update');\n // Delete CRM AI template\n $router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])\n ->name('api.automation.templates.delete');\n // List all CRM AI templates\n $router->get('/templates', [TeamAiAutomationController::class, 'templates'])\n ->name('api.automation.templates.list');\n // Create CRM AI template field\n $router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])\n ->name('api.automation.templates.fields.create');\n // Update CRM AI template field\n $router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])\n ->name('api.automation.templates.fields.update');\n // Delete CRM AI template field\n $router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])\n ->name('api.automation.templates.fields.delete');\n });\n\n $router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {\n // Create AI scorecard\n $router->post('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'createAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.create');\n // Update AI scorecard\n $router->put('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'updateAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.update');\n // Delete AI scorecard\n $router->delete('/ai-scorecards/{aiScorecard}', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'deleteAiScorecard'])\n ->name('api.ai-call-scoring.ai-scorecards.delete');\n // List all AI scorecards\n $router->get('/ai-scorecards', [Controllers\\API\\AiCallScoring\\AiScorecardController::class, 'aiScorecards'])\n ->name('api.ai-call-scoring.ai-scorecards.list');\n // Test AI scorecard prompt\n $router->post(\n '/ai-scorecards/{aiScorecard}/test-prompt',\n [\n Controllers\\API\\AiCallScoring\\AiScorecardController::class,\n 'testAiScorecardPrompt',\n ]\n )\n ->name('api.ai-call-scoring.ai-scorecards.test-prompt');\n\n // Create AI Scorecard rule\n $router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'createRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');\n // Update AI Scorecard rule\n $router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'updateAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');\n // Delete AI Scorecard rule\n $router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\\API\\AiCallScoring\\AiScorecardRuleController::class, 'deleteAiScorecardRule'])\n ->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');\n });\n\n // Theme, topics, triggers\n $router->get('/themes', [ThemeController::class, 'list']);\n $router\n ->post('/themes', [ThemeController::class, 'updateTheme'])\n ->middleware('can:manage,' . PlaybackTheme::class)\n ->name('api.theme.create');\n\n $router->group(\n [\n 'prefix' => 'theme/{theme}',\n 'middleware' => 'can:update,theme',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [ThemeController::class, 'updateTheme'])\n ->name('api.theme.update');\n $router\n ->delete('/', [ThemeController::class, 'deleteTheme'])\n ->middleware('can:delete,theme')\n ->name('api.theme.delete');\n\n $router\n ->post('/topics', [TopicController::class, 'updateTopic'])\n ->middleware('can:createTopic,theme')\n ->name('api.topic.create');\n\n $router->group(\n [\n 'prefix' => 'topic/{topic}',\n 'middleware' => 'can:update,topic',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicController::class, 'updateTopic'])\n ->name('api.topic.update');\n $router\n ->delete('/', [TopicController::class, 'deleteTopic'])\n ->middleware('can:delete,topic')\n ->name('api.topic.delete');\n\n $router\n ->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])\n ->middleware('can:createTrigger,topic')\n ->name('api.topic_trigger.create');\n\n $router->group(\n [\n 'prefix' => 'trigger/{topicTrigger}',\n 'middleware' => 'can:update,topicTrigger',\n ],\n static function (Router $router): void {\n $router\n ->put('/', [TopicTriggerController::class, 'updateTrigger'])\n ->name('api.topic_trigger.update');\n $router\n ->delete('/', [TopicTriggerController::class, 'deleteTrigger'])\n ->middleware('can:delete,topicTrigger')\n ->name('api.topic_trigger.delete');\n },\n );\n },\n );\n },\n );\n\n $router->post('/themes/import', [Controllers\\API\\Themes\\ImportTopicTriggerController::class, 'importThemes']);\n $router->get('/themes/export', [Controllers\\API\\Themes\\ExportTopicTriggerController::class, 'exportThemes']);\n\n // Auto-scoring\n $router->group(['prefix' => '/scorecards'], static function (Router $router) {\n $router->get('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'list']);\n $router->post('/', [Controllers\\API\\Scorecards\\ScorecardController::class, 'create']);\n $router->delete('/{scorecard}', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n $router->post('/validate-name', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'validateNameExists',\n ]);\n\n $router->get('/enabled-scorecard', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getEnabledScorecard',\n ]);\n\n $router->get('/affected-scorecards', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'getAffectedScorecards',\n ]);\n\n $router->group(['prefix' => '/{scorecard}'], static function (Router $router) {\n $router->put('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'update',\n ]);\n $router->delete('/', [\n Controllers\\API\\Scorecards\\ScorecardController::class,\n 'delete',\n ]);\n\n $router->post('/rules', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'create',\n ]);\n\n $router->post('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'update',\n ]);\n\n $router->delete('/rules/{scorecardRule}', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'delete',\n ]);\n\n $router->post('/rules/{scorecardRule}/update-order', [\n Controllers\\API\\Scorecards\\ScorecardRuleController::class,\n 'updateOrder',\n ]);\n });\n });\n\n // Coaching Playbook.\n Route::get('/playbooks', [PlaybookController::class, 'all']);\n Route::get('/playbooksTree', [PlaybookController::class, 'tree']);\n Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);\n Route::post('/playbooks', [PlaybookController::class, 'store']);\n Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);\n\n Route::prefix('/playbooks/{playbook}')->group(static function () {\n // Playbook Categories.\n Route::get('/categories', [PlaybookCategoryController::class, 'all']);\n Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.\n Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);\n Route::post('/categories', [PlaybookCategoryController::class, 'store']);\n Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);\n Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);\n Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);\n\n Route::prefix('/categories/{category}')->group(static function () {\n // Coaching Sections\n Route::get('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'all']);\n Route::put('/coaching-section/sequence', [Controllers\\Settings\\Coaching\\SectionsController::class, 'sequence']);\n Route::put('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'update']);\n Route::post('/coaching-section', [Controllers\\Settings\\Coaching\\SectionsController::class, 'store']);\n Route::delete('/coaching-section/{coachingSection}', [Controllers\\Settings\\Coaching\\SectionsController::class, 'destroy']);\n\n Route::prefix('coaching-section/{coachingSection}')->group(static function () {\n // Coaching Section Criteria\n Route::get('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'all']);\n Route::put('/coaching-section-criterion/sequence', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'sequence']);\n Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'update']);\n Route::post('/coaching-section-criterion', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'store']);\n Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\\Settings\\Coaching\\SectionCriteriaController::class, 'destroy']);\n });\n });\n });\n },\n );\n\n $router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])\n ->group(static function (Router $router): void {\n // Job Titles.\n $router->get('/job-titles', [JobTitleController::class, 'all']);\n $router->put('/job-titles/{job}', [JobTitleController::class, 'update']);\n $router->post('/job-titles', [JobTitleController::class, 'store']);\n $router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);\n\n // Team Settings.\n $router->put('/', [TeamSettingsController::class, 'update']);\n $router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);\n $router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);\n $router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);\n $router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);\n $router->put('/owner', [Controllers\\Settings\\Teams\\OrganizationSettingsController::class, 'updateOwner']);\n\n $router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);\n\n // Key Moments.\n $router->get('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'show']);\n $router->put('/moments/{moment}', [Controllers\\Settings\\MomentController::class, 'update']);\n $router->post('/moments', [Controllers\\Settings\\MomentController::class, 'store']);\n $router->put('/activity', [TeamActivityController::class, 'store']);\n\n // Team Domains.\n $router->get('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'all']);\n $router->post('/domains', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'create']);\n $router->delete('/domains/{teamDomain}', [Controllers\\Settings\\Teams\\TeamDomainsController::class, 'destroy']);\n });\n });\n });\n });\n\n // Integrations\n $router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {\n $router->post('/integrations', [IntegrationController::class, 'internal'])\n ->name('api.integrations.internal');\n $router->put('/integrations', [IntegrationController::class, 'toggleStatus'])\n ->name('api.integrations.toggle_status');\n $router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])\n ->name('api.integrations.delete');\n });\n\n $router->get('/integrations', [IntegrationController::class, 'all'])\n ->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)\n ->name('api.integrations.index');\n\n // Slack API for getting slack channels list\n $router->get('{notificationProvider}/channels', [Controllers\\NotificationProviderController::class, 'channels']);\n\n\n // Team actions. XXX: These all need moving out to their own controllers.\n $router->group(['prefix' => 'organizations'], static function (Router $router): void {\n $router->get('current', [TeamController::class, 'current']);\n\n $router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {\n $router->get('/', [TeamController::class, 'show']);\n\n $router->get('/categories', [TeamController::class, 'categories']);\n $router->get('/stages', [TeamController::class, 'stages']);\n $router->get('/users', [OrganizationMembersController::class, 'index'])\n ->name('organization.members.index');\n $router\n ->get('/users/download', [OrganizationMembersController::class, 'download'])\n ->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)\n ->name('organization.members.download');\n $router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])\n ->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)\n ->name('organization.licensed-roles.index');\n $router->get('/invitations', [TeamController::class, 'invitations']);\n $router->get('/groups', [TeamController::class, 'groups']);\n $router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])\n ->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])\n ->name('api.groups.delete');\n $router->get('/job-titles', [TeamController::class, 'jobTitles']);\n $router->get('/slugs', [TeamController::class, 'slugs']);\n $router->put('/api-token', [TeamController::class, 'generateApiToken'])\n ->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);\n $router->get('/key-moments', [MomentController::class, 'all']);\n });\n });\n\n // Internal Kiosk. This whole section will be moved out to a separate file\n $router\n ->prefix('kiosk')\n ->middleware('can:kiosk,' . User::class)\n ->group(static function (Router $router): void {\n // Partner actions.\n $router->get('/partners', [PartnersController::class, 'index']);\n\n // User actions.\n $router->post('/users/search', [SearchController::class, 'performBasicSearch']);\n\n // Team actions.\n $router->prefix('organizations')->group(static function (Router $router): void {\n $router->get('/', [OrganizationsController::class, 'show']);\n $router->put('/{team}', [OrganizationController::class, 'edit'])\n ->name('kiosk.organizations.edit');\n $router->get('/{team}/users', [OrganizationMembersController::class, 'index'])\n ->name('kiosk.organization.members.index');\n $router->get('onboardable', [OnboardController::class, 'available']);\n $router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);\n });\n\n // Automated reports\n // api/v1/kiosk/automated-reports\n $router->prefix('automated-reports')->group(static function (Router $router): void {\n $router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);\n $router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);\n $router->post('/filters', [AutomatedReportsController::class, 'getFilters']);\n $router->post('/', [AutomatedReportsController::class, 'create']);\n $router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);\n $router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);\n $router->get('/', [AutomatedReportsController::class, 'list']);\n $router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);\n $router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);\n $router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);\n $router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);\n });\n\n // Activity actions.\n $router->post('/activity/search', [SearchController::class, 'performActivitySearch']);\n $router->prefix('activity/{activity}')->group(static function (Router $router): void {\n $router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);\n $router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);\n $router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);\n $router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);\n $router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);\n $router->post('language', [KioskActivityController::class, 'updateLanguage']);\n $router->post('trim', [KioskActivityController::class, 'trimActivity']);\n $router->get('troubleshoot', [KioskActivityController::class, 'troubleshootActivity']);\n $router->get('transcription', [KioskActivityController::class, 'getTranscriptions']);\n $router->post('speakers', [KioskActivityController::class, 'addSpeakers']);\n $router->post('crm-fields-fill', [KioskActivityController::class, 'crmFieldsFill']);\n $router->post('summary-highlights', [KioskActivityController::class, 'summaryHighlights']);\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->group(['prefix' => 'events'], static function (Router $router): void {\n $router->post('authenticate', [Controllers\\PusherController::class, 'auth'])\n ->name(Routes::WEBHOOK_PUSHER_AUTH);\n });\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n $router->get('/extensions/auth', [ExtensionController::class, 'authenticate']);\n $router->get('/call-token/{team}/{participant?}', [ClientTokenController::class, 'generateToken']);\n});\n\n$router->group(['prefix' => 'user'], static function (Router $router): void {\n $router->get('chrome-extension-authentication', [ExtensionController::class, 'authenticate']);\n});\n\n$router->group(['middleware' => ['auth:api'], 'prefix' => 'sms'], static function (Router $router): void {\n $router->get('/{phoneNumber}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messages']);\n $router->get('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'messagesList']);\n $router->post('/', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'send']);\n $router->delete('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'redact']);\n $router->put('/{activity}', [Controllers\\Telephony\\TextMessaging\\MessageController::class, 'resend']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/users/current', [UserController::class, 'current']);\n\n $router->get('/users/slug/{slug?}', [UserController::class, 'validateSlug']);\n\n // Profile Contact Information.\n $router->put(\n '/users/{user}/settings/profile',\n [Controllers\\Settings\\Profile\\ContactInformationController::class, 'update'],\n );\n\n $router->get('/users/{user}/email-sync-settings', [EmailSyncController::class, 'index']);\n $router->put('/users/{user}/email-sync-settings', [EmailSyncController::class, 'update']);\n\n // SMS Settings.\n $router->put('/users/{user}/settings/sms', [Controllers\\Settings\\Profile\\SmsController::class, 'update']);\n\n $router->get('/settings/timezones', [Controllers\\API\\Settings\\TimeZoneController::class, 'index'])\n ->name('settings.timezones.index');\n\n $router->put('/settings/user/deal-insights', [Controllers\\Settings\\Users\\UserSettingsController::class, 'update']);\n});\n\n$router->group(['prefix' => 'page', 'middleware' => ['api', 'auth:api']], static function () use ($router): void {\n $router->get('/playback/{activity}', [PlaybackController::class, 'show'])\n ->name('api.playback');\n $router->get('/on-demand', [OnDemandController::class, 'show'])\n ->name('api.activity.search');\n});\n\n$router->group(['prefix' => 'partners', 'middleware' => 'auth:partner-api'], static function () use ($router): void {\n $router->get('/', [PartnerController::class, 'me']);\n\n $router->group(['prefix' => 'organizations'], static function () use ($router): void {\n $router->get('/{team}', [PartnerController::class, 'fetchOrganization']);\n $router->post('/', [PartnerController::class, 'createOrganization']);\n });\n\n $router->group(['prefix' => 'groups'], static function () use ($router): void {\n $router->get('/{group}', [PartnerController::class, 'fetchGroup']);\n $router->post('/', [PartnerController::class, 'createGroup']);\n });\n\n $router->group(['prefix' => 'users'], static function () use ($router): void {\n $router->get('/{user}', [PartnerController::class, 'fetchUser']);\n $router->post('/', [PartnerController::class, 'createUser']);\n $router->delete('/{user}', [PartnerController::class, 'deactivateUser']);\n });\n\n $router->group(['prefix' => 'activities'], static function () use ($router): void {\n $router->get('/{activity}', [PartnerController::class, 'fetchActivity']);\n $router->get('/', [PartnerController::class, 'searchActivity']);\n });\n});\n\n$router->group(['prefix' => 'activity', 'middleware' => 'api'], static function () use ($router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function () use ($router): void {\n // Bulk delete\n $router->delete('/', [ActivityController::class, 'delete']);\n\n // Search.\n $router->get('/search', [ActivityController::class, 'search']);\n\n // All comments.\n $router->get('/comments', [ActivityController::class, 'fetchComments']);\n\n // Transcription AI\n $router->get('/{activity}/action-items', [Controllers\\API\\ActionItemsController::class, 'index']);\n $router->get('/{activity}/ai-call-scoring', [Controllers\\API\\AiCallScoring\\AiCallScoringController::class, 'index']);\n\n $router->get('/saved-search', [ActivityController::class, 'listActivitySearch'])->name('api.saved_search.index');\n $router->get('/saved-search/{search}', [ActivityController::class, 'fetchActivitySearch'])->name('api.saved_search.show');\n $router->post('/saved-search', [ActivityController::class, 'createActivitySearch'])->name('api.saved_search.create');\n $router->put('/saved-search/{search}', [ActivityController::class, 'updateActivitySearch'])->name('api.saved_search.update');\n $router->delete('/saved-search/{search}', [ActivityController::class, 'deleteActivitySearch'])->name('api.saved_search.delete');\n\n $router->post('/saved-search/{search}/nudges', [NudgeController::class, 'createAction'])->name('api.nudges.create');\n $router->put('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'updateAction'])->name('api.nudges.update');\n $router->delete('/saved-search/{search}/nudges/{nudge}', [NudgeController::class, 'deleteAction'])->name('api.nudges.delete');\n\n // Live (coaching).\n $router->get('/live', [ActivityController::class, 'live']);\n $router->get('/{activity}/cloudfront-s3-media-keys', [ActivityController::class, 'fetchCloudFrontS3MediaKeys']);\n\n $router->post('/softphone', [SoftphoneController::class, 'create']);\n $router->put('/softphone', [SoftphoneController::class, 'createCoachParticipant']);\n $router->post('/softphone/dial', [SoftphoneController::class, 'dial']);\n $router->get('/softphone/{activity}', [SoftphoneController::class, 'fetch']);\n $router->delete('/softphone/{activity}', [SoftphoneController::class, 'endCall']);\n\n $router->post('softphone/{activity}/message', [SoftphoneController::class, 'message']);\n });\n\n // Activity actions.\n $router->group(['prefix' => '{activity}', 'middleware' => ['auth:api']], static function (Router $router): void {\n // User only.\n $router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n // Messages endpoint.\n $router->post('/message', [MessageController::class, 'message']);\n\n // Organizer actions.\n $router->put('/', [ActivityController::class, 'update']);\n $router->get('/', [ActivityController::class, 'show']);\n $router->delete('/', [ActivityController::class, 'destroy']);\n\n $router->post('/recording', [ActivityController::class, 'createRecording']);\n $router->put('/recording', [ActivityController::class, 'updateRecording']);\n $router->delete('/recording', [ActivityController::class, 'stopRecording']);\n\n $router->post('/summarize', [ActivityController::class, 'summarize']);\n\n // Sales Activity Playback action.\n $router->put('/favorite', [ActivityController::class, 'favorite']);\n $router->delete('/favorite', [ActivityController::class, 'unfavorite']);\n\n $router->put('/private', [ActivityController::class, 'markAsPrivate']);\n $router->delete('/private', [ActivityController::class, 'markAsPublic']);\n\n $router->put('/notification', [ActivityController::class, 'notify']);\n $router->delete('/notification/{notification}', [ActivityController::class, 'unnotify']);\n\n // Activity comments\n $router->put('/comment/{comment}', [ActivityController::class, 'updateComment']);\n $router->post('/comment/{comment}', [ActivityController::class, 'replyComment']);\n $router->post('/comment', [ActivityController::class, 'comment']);\n $router->delete('/comment/{comment}', [ActivityController::class, 'deleteComment']);\n $router->put('/comment/{comment}/visibility', [ActivityController::class, 'updateCommentVisibility']);\n\n $router->get('/coaching-sections', [ActivityController::class, 'coachingSections']);\n\n $router->put('/coach', [ActivityController::class, 'putCoachingFeedback']);\n $router->delete('/coach/{coachingFeedback}', [ActivityController::class, 'deleteCoachingFeedback']);\n\n $router->post('/coach-request', [ActivityController::class, 'coachRequest']);\n $router->post('/share', [ActivityController::class, 'share']);\n\n $router->post('/playlists', [ActivityController::class, 'addToPlaylist'])\n ->name('playlists.add.activity');\n\n $router->post('/key-moment', [MomentController::class, 'store']);\n\n $router->put('/play', [ActivityController::class, 'play']);\n\n $router->get('/stats', [ActivityController::class, 'stats']);\n\n $router->get('/topic-triggers', [ActivityController::class, 'fetchActivityTopicTriggers']);\n\n $router->post('/topic-triggers', [ActivityController::class, 'createActivityTopicTriggers']);\n\n $router->get('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'getAutoScore']);\n $router->post('/auto-score', [Controllers\\API\\Scorecards\\AutoScoreController::class, 'updateAutoScore']);\n\n // Get Download link for an activity\n $router->get('/download', [Controllers\\PlaybackController::class, 'getDownloadUrl'])->name('getDownloadUrl');\n\n $router->post('/note', [ActivityController::class, 'note']);\n\n $router->post('/export', [ExportController::class, 'share'])\n ->middleware(['throttle:activity-export']);\n\n $router->post('/shareable-link', [ExportController::class, 'getShareableLink'])\n ->middleware(['throttle:activity-export-shareable-link']);\n\n $router->group(['prefix' => 'transcription'], static function (Router $router): void {\n $router->get('/', [TranscriptionController::class, 'getTranscriptionByActivity']);\n $router->get('/search', [TranscriptionController::class, 'searchAction']);\n $router->get('/download', [TranscriptionController::class, 'downloadTranscriptionByActivity'])\n ->middleware(['throttle:transcription-download']);\n $router->put('/attribution-flip/{participantA}/{participantB}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionFlip']);\n $router->put('/attribution-change/{participant}', [Controllers\\API\\TranscriptionController::class, 'speakerAttributionChange']);\n $router->get('/translation', [TranslationController::class, 'getTranslation']);\n });\n });\n });\n});\n\n$router->group(['middleware' => ['auth:api']], static function () use ($router) {\n $router->put('/subscription/{morphType}', [SubscriptionController::class, 'subscribe']);\n $router->delete('/subscription/{morphType}', [SubscriptionController::class, 'unsubscribe']);\n});\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlists', [PlaylistController::class, 'all'])->name('api.playlists.all');\n $router->get('/playlists/user', [PlaylistController::class, 'userPlaylists'])\n ->name('api.playlists.userPlaylists');\n $router->post('/playlists', [PlaylistController::class, 'store'])->name('api.playlists.store');\n\n $router->post('/playlists/{playlist}/share', [PlaylistController::class, 'share'])\n ->name('api.playlist.create.share');\n $router->get('/playlists/{playlist}/activities', [PlaylistController::class, 'activities'])\n ->name('api.playlist.activities');\n $router->delete('/playlists/{playlist}/shares/{playlistShare}', [PlaylistController::class, 'unshare'])\n ->name('api.playlist.unshare');\n $router->get('/playlists/{playlist}/shares', [PlaylistController::class, 'shares'])\n ->name('api.playlist.get.shares');\n $router->post('/playlists/{playlist}/lock', [PlaylistController::class, 'lock'])->name('api.playlist.lock');\n $router->post('/playlists/{playlist}/unlock', [PlaylistController::class, 'unlock'])\n ->name('api.playlist.unlock');\n $router->get(\n '/playlists/{playlist}/available-playlists',\n [PlaylistController::class, 'availablePlaylistsToMoveTo'],\n )->name('api.playlist.available');\n $router->put('/playlists/{playlist}', [PlaylistController::class, 'update'])->name('api.playlist.update');\n $router->delete('/playlists/{playlist}', [PlaylistController::class, 'destroy'])\n ->name('api.playlist.destroy');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'updatePlaylistTrack'],\n )->name('api.playlist.updatePlaylistTrack');\n $router->put(\n '/playlists/{playlist}/tracks/{playlistActivity}/move',\n [PlaylistController::class, 'moveToPlaylist'],\n )->name('api.playlist.moveToPlaylist');\n $router->delete(\n '/playlists/{playlist}/tracks/{playlistActivity}',\n [PlaylistController::class, 'removeFromPlaylist'],\n )->name('api.playlist.removeFromPlaylist');\n});\n\n$router->group(\n ['prefix' => '/opportunity/{opportunity}', 'middleware' => ['api']],\n static function (Router $router): void {\n // Opportunity comments\n $router->group(['prefix' => '/comment', 'middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/', [CommentsController::class, 'fetchComments']);\n $router->post('/', [CommentsController::class, 'comment']);\n\n $router->group(['prefix' => '{comment}'], static function (Router $router): void {\n $router->put('/', [CommentsController::class, 'updateComment']);\n $router->post('/', [CommentsController::class, 'replyComment']);\n $router->delete('/', [CommentsController::class, 'deleteComment']);\n $router->put('/visibility', [CommentsController::class, 'updateCommentVisibility']);\n });\n });\n },\n);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->get('/playlist/{activity}.m3u8', [Controllers\\API\\PlaybackController::class, 'playlist']);\n $router->get('/media/{track}.m3u8', [Controllers\\API\\PlaybackController::class, 'media']);\n});\n\n$router->group(['middleware' => ['api']], static function (Router $router): void {\n // SSO email query.\n $router->get('/auth/sso/login', [Controllers\\API\\SsoController::class, 'ssoLogin'])->name('ssoLogin');\n});\n\n$router->get('/mobile-settings', [MobileSettingsController::class, 'getAll']);\n\n$router->put('/mobile-settings', [MobileSettingsController::class, 'updateSettings'])\n ->middleware(['auth:api', 'can:kiosk,' . User::class])\n ->name('api.kiosk.mobile_settings.update');\n\n// Ask Jiminny on deal level\n$router->get('deals/{opportunity}/ask-jiminny', [Controllers\\API\\DealLevelPromptsController::class, 'index'])\n ->middleware(['api', 'auth:api'])\n ->name('api.deals.ask-jiminny');\n\n$router->get('get-access-token/{provider?}', [SocialController::class, 'getAccessToken'])\n ->name('api.get_access_token')\n ->whereIn('provider', [SocialAccount::PROVIDER_HUBSPOT]);\n\n$router->group(['middleware' => ['auth:api']], static function (Router $router): void {\n $router->post('single-claim-token/{provider?}', [SocialController::class, 'getSingleUseClaim'])\n ->name('api.singe-claim-token');\n});\n\n$router->post('deauthorize-zoom-app', [SocialController::class, 'deauthorizeZoomApp'])\n ->name('api.deauthorize-zoom-app.recall-ai');\n\n$router->put('/conferences/{activity}/consent', [ConferencesOptInOutController::class, 'storeConsent'])\n ->middleware(['throttle:conference-consent'])\n ->name('api.conferences.store-consent');","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.019444445,"height":0.02111111},"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Console;\n\nuse Illuminate\\Console\\ConfirmableTrait;\nuse Illuminate\\Console\\Scheduling\\Event;\nuse Illuminate\\Console\\Scheduling\\Schedule;\nuse Illuminate\\Foundation\\Console\\Kernel as ConsoleKernel;\nuse Jiminny\\Component\\Acl\\RemoveExpiredRoleChangeEventsCommand;\nuse Jiminny\\Component\\ActionItems\\Commands\\SendActionItemsCommand;\nuse Jiminny\\Component\\AiActivityType\\Commands\\AutodetectAiActivityTypeCommand;\nuse Jiminny\\Component\\AskJiminnyAi\\Commands\\ProphetAnalyzeClosedDealsCommand;\nuse Jiminny\\Component\\Cache\\Constants;\nuse Jiminny\\Component\\DealInsights\\Commands\\SendDealsUpdateCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\MediaPipelineRestartCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportActivityProcessingTimeToDatadogCommand;\nuse Jiminny\\Component\\MediaPipeline\\Command\\ReportProcessingStatesToDatadogCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\OverrideTranscriptionLocaleCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryFailedTranscriptionsCommand;\nuse Jiminny\\Component\\Transcription\\Commands\\RetryStuckTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ActivitiesMatchCrmCommand;\nuse Jiminny\\Console\\Commands\\Activities\\AutologOldActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DeleteActivitiesForRetentionTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\DownloadMissingTrackCommand;\nuse Jiminny\\Console\\Commands\\Activities\\FixActivitiesOpportunity;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesForChurnedTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\HardDeleteActivitiesTeamsCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReassignTranscriptCommand;\nuse Jiminny\\Console\\Commands\\Activities\\ReindexRecentActivitiesCommand;\nuse Jiminny\\Console\\Commands\\Activities\\RetryProspectSummaryCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeleteCancelledCommand;\nuse Jiminny\\Console\\Commands\\Calendars\\Events\\CalendarEventDeletePastCommand;\nuse Jiminny\\Console\\Commands\\Crm\\BackfillOpportunityUserFromAccountCommand;\nuse Jiminny\\Console\\Commands\\Crm\\CleanDuplicateFieldDataCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ProcessMergedObjectsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\RestoreDealAssociationsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\ProcessHubspotObjectsSyncBatches;\nuse Jiminny\\Console\\Commands\\Crm\\PurgeDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\ListJournalWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\Hubspot\\SetupJournalDealWebhookSubscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Crm\\SyncHubspotActiveDeals;\nuse Jiminny\\Console\\Commands\\Crm\\SyncOpportunitiesMissingFieldDataCommand;\nuse Jiminny\\Console\\Commands\\DeleteOldAiCrmNotesCommand;\nuse Jiminny\\Console\\Commands\\DeleteS3LeftoversCommand;\nuse Jiminny\\Console\\Commands\\DiarizeViaAiParticipantIdentificationCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\DeleteEmailDocumentsCommand;\nuse Jiminny\\Console\\Commands\\Elasticsearch\\RemoveGhostParticipantsCommand;\nuse Jiminny\\Console\\Commands\\FlushRolesPermissionsCache;\nuse Jiminny\\Console\\Commands\\GenerateInternalWebhookToken;\nuse Jiminny\\Console\\Commands\\IssueMcpTokenCommand;\nuse Jiminny\\Console\\Commands\\HubspotJournalPollingCommand;\nuse Jiminny\\Console\\Commands\\HubspotWebhookServiceCommand;\nuse Jiminny\\Console\\Commands\\Livestream\\StopHangingLivestreamsCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteEmailMessagesWithoutActivityCommand;\nuse Jiminny\\Console\\Commands\\Mailboxes\\DeleteInboxEmailsCommand;\nuse Jiminny\\Console\\Commands\\PurgeSoftDeletedOpportunitiesCommand;\nuse Jiminny\\Console\\Commands\\PurgeSyncBatchesCommand;\nuse Jiminny\\Console\\Commands\\RemoveDeleteMarkersCommand;\nuse Jiminny\\Console\\Commands\\RemoveExpiredNudgesCommand;\nuse Jiminny\\Console\\Commands\\RemoveUnusedParticipantSpeechesCommand;\nuse Jiminny\\Console\\Commands\\Reports\\AutomatedReportsRetentionPolicyCommand;\nuse Jiminny\\Console\\Commands\\Reports\\DeleteReportCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityCrmProviderIdCommand;\nuse Jiminny\\Console\\Commands\\RestoreActivityTypeCommand;\nuse Jiminny\\Console\\Commands\\SendNudgeExpirationWarningsCommand;\nuse Jiminny\\Console\\Commands\\Slack\\SyncSlackUserCommand;\nuse Jiminny\\Console\\Commands\\Teams\\SyncTeamUsersCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamDeleteCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteDeactivatedCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamsDeleteRetentionCommand;\nuse Jiminny\\Console\\Commands\\Teams\\TeamSettingPutCommand;\nuse Jiminny\\Console\\Commands\\Teams\\UpdateTeamsCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\CleanupActivityTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\DeleteUnusedTracksCommand;\nuse Jiminny\\Console\\Commands\\Tracks\\RestoreTracksCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\DeleteOldTranscriptionsCommand;\nuse Jiminny\\Console\\Commands\\Transcription\\UpdateOldTranscriptionModelLocalesCommand;\nuse Jiminny\\Console\\Commands\\Twilio\\DeleteChurnedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\DeletePredefinedSubAccounts;\nuse Jiminny\\Console\\Commands\\Twilio\\ReleaseNumbersCommand;\nuse Jiminny\\Jobs\\Activity\\SyncActivity;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\InboxEmail;\nuse Jiminny\\Services\\RecallAI\\Commands\\ImportRegionMeetingCommand;\nuse Jiminny\\Services\\RecallAI\\Commands\\ScheduleBotCommand;\n\nclass Kernel extends ConsoleKernel\n{\n use ConfirmableTrait;\n\n /**\n * The Artisan commands provided by your application.\n *\n * @var string[]\n */\n protected $commands = [\n Commands\\GeckoExport\\GeckoExportTranscriptCommand::class,\n Commands\\GeckoExport\\GeckoExportTranscriptionCommand::class,\n Commands\\GeckoExport\\GeckoExportParticipantSpeechesCommand::class,\n Commands\\Activities\\DeleteForCoachesCommand::class,\n ReindexRecentActivitiesCommand::class,\n Commands\\Crm\\BullhornPingCommand::class,\n Commands\\Crm\\BullhornSessionCommand::class,\n Commands\\Crm\\BullhornSearchCommand::class,\n Commands\\PlaybackThemes\\TopicsConsolidateCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesCopyCommand::class,\n Commands\\PlaybackThemes\\AssignTopicsUsedBySingleTeamCommand::class,\n Commands\\PlaybackThemes\\PlaybackThemesMigrateToVersionsCommand::class,\n Commands\\Vocabulary\\VocabularyCopyCommand::class,\n Commands\\Transcription\\TranscriptionPrintRaw::class,\n Commands\\Migrate\\JiminnyMigratePopulateActivitySourceCommand::class,\n Commands\\EngagementStats\\JiminnyEngagementStatsExplainCommand::class,\n Commands\\EngagementStatsRegenerateCommand::class,\n Commands\\Analytics\\NumberOfActivitiesPerActivityTypeCommand::class,\n Commands\\Elasticsearch\\MappingRunCommand::class,\n Commands\\Elasticsearch\\MappingInstallCommand::class,\n Commands\\Elasticsearch\\UpdateEsMappingSettingsCommand::class,\n Commands\\Analytics\\TranscriptionWordMatchCommand::class,\n Commands\\JiminnyCacheClearCommand::class,\n Commands\\Transcription\\TranscriptionSearchCommand::class,\n RetryStuckTranscriptionsCommand::class,\n RetryFailedTranscriptionsCommand::class,\n Commands\\JiminnyDebugCommand::class,\n Commands\\RunAiCallScoringForUntypedActivitiesCommand::class,\n Commands\\Calendars\\SyncCalendars::class,\n Commands\\Calendars\\SyncDeletedEvents::class,\n Commands\\Twilio\\FetchMetrics::class,\n Commands\\Twilio\\FetchEvents::class,\n Commands\\Twilio\\FetchSummary::class,\n Commands\\Twilio\\SyncZoneAccess::class,\n Commands\\DatabaseTableCount::class,\n Commands\\PurgeConferences::class,\n Commands\\ResetElasticSearch::class,\n Commands\\CreateDatabaseUsers::class,\n Commands\\Activities\\NotifyNotLogged::class,\n Commands\\Crm\\SyncTeamMetadata::class,\n Commands\\Crm\\SyncProfileMetadata::class,\n Commands\\Crm\\SyncContact::class,\n Commands\\Crm\\SyncObjects::class,\n Commands\\Crm\\SyncHubspotObjects::class,\n Commands\\Crm\\SyncAccount::class,\n Commands\\Crm\\ResetGovernorLimits::class,\n Commands\\Crm\\ManageSyncStrategyCommand::class,\n Commands\\ImportRecording::class,\n Commands\\TrackImported::class,\n Commands\\Twilio\\RecoverTwilioTracksCommand::class,\n Commands\\Crm\\SetupLayouts::class,\n Commands\\Tracks\\SyncTwilioTracks::class,\n Commands\\Activities\\StatusCount::class,\n\n Commands\\Mailboxes\\TextRelay\\WatchMailboxEvents::class,\n Commands\\Mailboxes\\InboxCreate::class,\n Commands\\Mailboxes\\InboxSync::class,\n Commands\\Mailboxes\\BatchCreate::class,\n Commands\\Mailboxes\\BatchProcess::class,\n Commands\\Mailboxes\\InboxPurge::class,\n Commands\\Mailboxes\\BatchRetryFailed::class,\n Commands\\Mailboxes\\BatchFailStalled::class,\n Commands\\Mailboxes\\SkipListsRefresh::class,\n Commands\\Mailboxes\\SkipListsDump::class,\n Commands\\Mailboxes\\TextRelay\\SyncMailbox::class,\n Commands\\Mailboxes\\DeleteInboxEmailsCommand::class,\n Commands\\Mailboxes\\DeleteEmailMessagesCommand::class,\n DeleteEmailMessagesWithoutActivityCommand::class,\n\n Commands\\Tracks\\CheckIntegrity::class,\n Commands\\Twilio\\RemoteLifecycle::class,\n Commands\\Twilio\\SyncNumbers::class,\n Commands\\Crm\\SetupActivityTypeForFollowUp::class,\n Commands\\Activities\\CheckPlayable::class,\n Commands\\Activities\\ActivityDeleteCommand::class,\n Commands\\Activities\\Copy::class,\n Commands\\Activities\\ActivityHardDeleteCommand::class,\n Commands\\Reports\\Team::class,\n Commands\\Reports\\GenerateMarketingReport::class,\n Commands\\Reports\\AutomatedReportsCommand::class,\n Commands\\Reports\\AutomatedReportsSendCommand::class,\n Commands\\MuteOrganizerChannel::class,\n Commands\\Tracks\\DeleteTracks::class,\n Commands\\Tracks\\RetryDownload::class,\n Commands\\Tracks\\RetryFailedDownloads::class,\n Commands\\Twilio\\SyncAddresses::class,\n Commands\\Activities\\UpdateElasticSearch::class,\n Commands\\MakeSlackLiveCoachingChatNotesOn::class,\n Commands\\Activities\\PreMeetingNotification::class,\n ScheduleBotCommand::class,\n ImportRegionMeetingCommand::class,\n Commands\\Activities\\MonitorMeetingCountCommand::class,\n Commands\\Activities\\MonitorMeetingStartCommand::class,\n Commands\\Activities\\MonitorMeetingEndCommand::class,\n Commands\\SyncActivity::class,\n Commands\\PhpApm::class,\n Commands\\Crm\\SyncOpportunity::class,\n Commands\\Crm\\SyncLead::class,\n Commands\\Users\\SyncLicenceDataToSalesforce::class,\n Commands\\Crm\\UpdateOpportunitySpecifications::class,\n Commands\\Users\\SyncToIntercom::class,\n Commands\\Users\\SyncToUserPilot::class,\n Commands\\Teams\\SyncToPlanhat::class,\n Commands\\Twilio\\SetZoneAccess::class,\n Commands\\Users\\CreateDefaultSavedSearchesCommand::class,\n Commands\\Crm\\SendNotLogged::class,\n Commands\\Teams\\DeactivateTeamCommand::class,\n Commands\\Crm\\SyncFieldMetadata::class,\n Commands\\Postmark\\SyncEmailTemplatesCommand::class,\n Commands\\PlaybackThemes\\ImportTriggersFromTranslatedCsvCommand::class,\n Commands\\Activities\\PreMeetingReminder::class,\n Commands\\Activities\\CustomerActivitiesExport::class,\n Commands\\Users\\RefreshAccessToken::class,\n Commands\\Calendars\\SetupCalendarSubscription::class,\n Commands\\Activities\\InviteMeetingBot::class,\n Commands\\Activities\\ChangeActivitiesPlaybookCategoryOnPlaybookChange::class,\n Commands\\Crm\\MigrateProvider::class,\n Commands\\Activities\\MigrateLocationFromCalendarEventToActivities::class,\n Commands\\HelperTruncateCoachingTables::class,\n Commands\\FixCrossTenantIssues::class,\n Commands\\Activities\\CloudCall\\SetupIntegration::class,\n Commands\\Activities\\CloudTalk\\FixTimeZone::class,\n Commands\\Activities\\Orum\\SetupIntegration::class,\n Commands\\Activities\\JustCall\\SetupIntegration::class,\n Commands\\Activities\\RingCentral\\AddInboundPromptSupport::class,\n Commands\\Dialers\\Dialpad\\SubscribeToWebhooks::class,\n Commands\\RecalculateDealRisksCommand::class,\n SendDealsUpdateCommand::class,\n Commands\\Activities\\SetProviderCapabilitiesField::class,\n Commands\\Teams\\InitiallySetNotificationProviderTeamsTable::class,\n Commands\\Crm\\AddLayoutEntities::class,\n Commands\\PropagateCoachingFeedbackCreatedAtToSectionFeedbacks::class,\n Commands\\JiminnyTokenInfoCommand::class,\n Commands\\JiminnySetEncryptedTokenManagerModeCommand::class,\n Commands\\EncryptTokensCommand::class,\n Commands\\Dialers\\Aircall\\CheckAndRenewWebhooks::class,\n Commands\\Migrate\\MigrateTeamRegionCommand::class,\n Commands\\ManageScimForTeam::class,\n Commands\\Dialers\\SyncUsersCommand::class,\n Commands\\WhichWorkerIsWorkingOnWhichJob::class,\n Commands\\GroupSetDefaultLanguageCommand::class,\n Commands\\Dev\\AddRateLimitCommand::class,\n Commands\\Dev\\ImportCallsCommand::class,\n Commands\\DealInsights\\BuildDealInsightsLayoutCommand::class,\n Commands\\DealInsights\\DeleteAskJiminnyDealPrompts::class,\n Commands\\Crm\\MatchCrmObjectsCommand::class,\n Commands\\Activities\\SetupIntegration\\EightByEight::class,\n Commands\\Calendars\\RemoveCalendarEventActivitiesCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromGongCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromChorusCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromLeexiCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromAvomaCommand::class,\n Commands\\Activities\\Migrator\\MigrateFromClariCommand::class,\n Commands\\Activities\\SetupIntegration\\ConnectAndSell::class,\n Commands\\Activities\\SetupIntegration\\CloudTalk::class,\n Commands\\Users\\CreateConferenceSlug::class,\n Commands\\Elasticsearch\\AsyncUpdateEsEntities::class,\n Commands\\Elasticsearch\\ResetAsyncElasticSearchCommand::class,\n Commands\\Playlists\\PlaylistSharesUpdateCommand::class,\n Commands\\Crm\\AutologDelayedCommand::class,\n Commands\\Activities\\HydrateDefaultActivityTypeCommand::class,\n Commands\\Crm\\CheckActivityLoggableCommand::class,\n Commands\\Activities\\MonitorDialerActivitiesCommand::class,\n Commands\\Activities\\SetupIntegration\\Xant::class,\n Commands\\ImportUsersFromCsvFile::class,\n Commands\\DevPostmanCommand::class,\n Commands\\Playlists\\FixTreeStructureCommand::class,\n Commands\\Zoom\\ResolvePmiLinksCommand::class,\n Commands\\MarkBranchForEnvironmentPipelineCommand::class,\n Commands\\Activities\\ProbeMediaSegmentsCommand::class,\n Commands\\Activities\\SetupIntegration\\AmazonConnect::class,\n Commands\\Playbooks\\ChangePlaybookActivityFieldCommand::class,\n MediaPipelineRestartCommand::class,\n Commands\\Dev\\FixHubSpotTokens::class,\n Commands\\Dev\\MonitorSocialAccountsState::class,\n Commands\\Activities\\SetupIntegration\\Vonage::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlex::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexDirect::class,\n Commands\\Activities\\SetupIntegration\\TwilioFlexSetDialerAuthCredentialsCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioSetS3RecordingCredentialsCommand::class,\n SendActionItemsCommand::class,\n Commands\\Users\\ChangeEmail::class,\n Commands\\Calendars\\ListUserGoogleCalendars::class,\n Commands\\Activities\\JustCall\\SyncPlaybackLinkToCrmCommand::class,\n Commands\\Activities\\HydrateCallWithCrmDataCommand::class,\n Commands\\Activities\\UpdateActivityElasticSearchDocumentCommand::class,\n Commands\\Activities\\SetupIntegration\\Talkdesk::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookRegisterCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookListCommand::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookShow::class,\n Commands\\Transcription\\Microsoft\\TranscriptionProviderMicrosoftWebhookDeleteCommand::class,\n Commands\\Activities\\SetupIntegration\\TwilioVideo::class,\n Commands\\Crm\\SetupCloseCrm::class,\n Commands\\Crm\\SetupCopperCrm::class,\n Commands\\Crm\\FullSyncOpportunityCommand::class,\n Commands\\Crm\\IntegrationApp\\CrmEntitiesFullSyncCommand::class,\n Commands\\Crm\\IntegrationApp\\ValidateConnectionCommand::class,\n Commands\\Activities\\Workflow\\RefreshCrmData::class,\n Commands\\Activities\\Migrator\\AnalyseGongCalls::class,\n Commands\\Users\\AddVoiceRoleToRecorderCommand::class,\n Commands\\Activities\\SyncMissingCallDispositions::class,\n Commands\\Calendars\\RemoveFutureCalendarEvents::class,\n FlushRolesPermissionsCache::class,\n Commands\\Activities\\SetupIntegration\\FiveNine::class,\n CalendarEventDeleteCancelledCommand::class,\n CalendarEventDeletePastCommand::class,\n ReportActivityProcessingTimeToDatadogCommand::class,\n ReportProcessingStatesToDatadogCommand::class,\n ReleaseNumbersCommand::class,\n BackfillOpportunityUserFromAccountCommand::class,\n RemoveExpiredRoleChangeEventsCommand::class,\n RemoveExpiredNudgesCommand::class,\n SendNudgeExpirationWarningsCommand::class,\n AutologOldActivitiesCommand::class,\n RemoveUnusedParticipantSpeechesCommand::class,\n DeleteActivitiesForChurnedTeamsCommand::class,\n HardDeleteActivitiesForChurnedTeamsCommand::class,\n TeamDeleteCommand::class,\n TeamsDeleteDeactivatedCommand::class,\n UpdateTeamsCommand::class,\n OverrideTranscriptionLocaleCommand::class,\n SyncSlackUserCommand::class,\n PurgeSoftDeletedOpportunitiesCommand::class,\n PurgeSyncBatchesCommand::class,\n ProphetAnalyzeClosedDealsCommand::class,\n DeleteChurnedSubAccounts::class,\n Commands\\ProphetAi\\DumpContext::class,\n DeletePredefinedSubAccounts::class,\n DeleteActivitiesForRetentionTeamsCommand::class,\n HardDeleteActivitiesTeamsCommand::class,\n TeamsDeleteRetentionCommand::class,\n TeamSettingPutCommand::class,\n StopHangingLivestreamsCommand::class,\n FixActivitiesOpportunity::class,\n Commands\\Activities\\SetupIntegration\\Salesforce\\SetupSalesforceIntegrationCommand::class,\n UpdateOldTranscriptionModelLocalesCommand::class,\n Commands\\Dev\\FixMissMatchedCrmActivitiesCommand::class,\n DownloadMissingTrackCommand::class,\n ActivitiesMatchCrmCommand::class,\n DeleteEmailDocumentsCommand::class,\n DeleteOldTranscriptionsCommand::class,\n DeleteS3LeftoversCommand::class,\n RemoveDeleteMarkersCommand::class,\n SyncTeamUsersCommand::class,\n ReassignTranscriptCommand::class,\n DiarizeViaAiParticipantIdentificationCommand::class,\n RestoreActivityTypeCommand::class,\n DeleteOldAiCrmNotesCommand::class,\n DeleteReportCommand::class,\n AutomatedReportsRetentionPolicyCommand::class,\n SyncHubspotActiveDeals::class,\n GenerateInternalWebhookToken::class,\n IssueMcpTokenCommand::class,\n RestoreActivityCrmProviderIdCommand::class,\n CleanupActivityTracksCommand::class,\n DeleteUnusedTracksCommand::class,\n RestoreTracksCommand::class,\n HubspotWebhookServiceCommand::class,\n ProcessMergedObjectsCommand::class,\n HubspotJournalPollingCommand::class,\n SetupJournalDealWebhookSubscriptionsCommand::class,\n ListJournalWebhookSubscriptionsCommand::class,\n RemoveGhostParticipantsCommand::class,\n AutodetectAiActivityTypeCommand::class,\n Commands\\Crm\\LogActivitiesCommand::class,\n Commands\\Crm\\MatchOpportunityActivitiesCommand::class,\n PurgeDeletedOpportunitiesCommand::class,\n CleanDuplicateFieldDataCommand::class,\n RetryProspectSummaryCommand::class,\n ProcessHubspotObjectsSyncBatches::class,\n SyncOpportunitiesMissingFieldDataCommand::class,\n RestoreDealAssociationsCommand::class,\n Commands\\Crm\\TestPipedriveOfficialSdkCommand::class,\n ];\n\n private Schedule $schedule;\n private string $output;\n\n protected function schedule(Schedule $schedule): void\n {\n $this->schedule = $schedule;\n $this->output = config('jiminny.scheduler_log');\n\n $schedule->useCache('redis');\n\n $currentMinute = (int) date('i');\n $currentDay = (int) date('w');\n\n $this->scheduleEveryMinute();\n $this->scheduleEveryTwoMinutes();\n $this->scheduleEveryFiveMinutes();\n $this->scheduleEveryTenMinutes();\n $this->scheduleEveryFifteenMinutes();\n $this->scheduleEveryThirtyMinutes();\n $this->scheduleHourly();\n $this->scheduleDaily();\n $this->scheduleWeekly($currentDay);\n $this->scheduleSpecificTimes();\n $this->scheduleDynamic($currentMinute);\n }\n\n protected function scheduleEveryMinute(): void\n {\n $this->scheduleCommand('meeting-bot:schedule-bot', expiresAt: 1)->everyMinute();\n $this->scheduleCommand('dialers:monitor-activities')->everyMinute();\n $this->scheduleCommand('jiminny:monitor-social-accounts')->everyMinute();\n $this->scheduleCommand('mailbox:skip-lists:refresh')->everyMinute();\n\n $this->schedule->command('mailbox:batch:process', ['--max-batches=15'])\n ->everyMinute()\n ->sendOutputTo($this->output);\n }\n\n protected function scheduleEveryTwoMinutes(): void\n {\n $this->scheduleCommand('conference:monitor:count', [], 2)->everyTwoMinutes();\n }\n\n protected function scheduleEveryFiveMinutes(): void\n {\n $this->scheduleCommand('activity:purge-stale', [], 4)->everyFiveMinutes();\n // Offset by 1 minute to avoid overlap with crm:sync-objects (runs at :14 and :44)\n $this->scheduleCommand('crm:sync-hubspot-objects', [], 4)\n ->cron('1,6,11,16,21,26,31,36,41,46,51,56 * * * *');\n $this->scheduleCommand('mailbox:text-relay:sync')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-notification', [], 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:start', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('conference:monitor:end', expiresAt: 3)->everyFiveMinutes();\n $this->scheduleCommand('jiminny:fix-hubspot-tokens')->everyFiveMinutes();\n $this->scheduleCommand('conference:pre-meeting-reminder')->everyFiveMinutes()->runInBackground();\n\n $this->schedule->command('mailbox:batch:create')\n ->cron('2-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output);\n\n $this->schedule->command('mailbox:batch:retry-failed', ['--max-batches=15'])\n ->cron('3-59/5 * * * *')\n ->withoutOverlapping(180)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ->runInBackground();\n\n $this->schedule->command('hubspot:journal-poll', ['--start'])\n ->everyFiveMinutes()\n ->sendOutputTo($this->output)\n ->runInBackground();\n }\n\n protected function scheduleEveryTenMinutes(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-failed')->everyTenMinutes();\n $this->scheduleCommand('activity:notify-not-logged')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('activity:status-count')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('mailbox:sync')->cron('6,16,26,36,46,56 * * * *');\n $this->scheduleCommand('crm:reset-governor')->everyTenMinutes();\n }\n\n protected function scheduleEveryFifteenMinutes(): void\n {\n $this->scheduleCommand('datadog:report:processing-sla-activities')->everyFifteenMinutes();\n $this->scheduleCommand('calendar:sync', ['--dateMode=daily'], 14)->cron('13,28,43,58 * * * *');\n $this->scheduleCommand('activity:aircall:check-and-renew')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('track:retry-failed-downloads')->cron('9,24,39,54 * * * *');\n $this->scheduleCommand('crm:autolog-delayed')->cron('3,18,33,48 * * * *');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n ],\n ])->everyFifteenMinutes();\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_RINGCENTRAL,\n Activity::PROVIDER_AVAYA,\n Activity::PROVIDER_TELUS,\n Activity::PROVIDER_TALKDESK,\n '--from' => now()->subMinutes(16)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->cron('7,22,37,52 * * * *');\n }\n\n protected function scheduleEveryThirtyMinutes(): void\n {\n $this->scheduleCommand('crm:sync-objects')->cron('14,44 * * * *');\n $this->scheduleCommand('mailbox:batch:fail-stalled')->everyThirtyMinutes();\n\n $this->scheduleCommand('activities:delete-activities-for-deactivated-teams', expiresAt: 5)\n ->between('02:58', '05:29')\n ->everyThirtyMinutes()\n ->runInBackground();\n\n $this->scheduleActivitiesHardDelete();\n }\n\n protected function scheduleHourly(): void\n {\n $this->scheduleCommand('jiminny:transcription:retry-stuck')->hourly();\n $this->scheduleCommand('twilio:recover-tracks')->cron('22 * * * *');\n $this->scheduleCommand('dialers:sync-users')->cron('22 * * * *');\n $this->scheduleCommand('datadog:report:failed-processing-states')->cron('22 * * * *');\n $this->scheduleCommand('automated-reports:send')->hourly();\n $this->scheduleCommand('deal-insights:send-update')->hourlyAt(0);\n $this->scheduleCommand('crm:integration-app-validate-team-connection')->hourlyAt(23);\n }\n\n protected function scheduleDaily(): void\n {\n $this->scheduleCommand('teams:sync-planhat')->daily();\n $this->scheduleCommand('twilio:sync-addresses')->daily();\n $this->scheduleCommand('twilio:sync-zone-access')->daily();\n $this->scheduleCommand('mailbox:text-relay:watch-text-events')->daily();\n $this->scheduleCommand('users:sync-licence-data')->daily();\n $this->scheduleCommand('users:sync-intercom-data')->daily();\n $this->scheduleCommand('nudges:send-expiration-warnings')->daily();\n $this->scheduleCommand('nudges-data-clean-up', ['--deleteExpiredNudges'])->daily();\n }\n\n protected function scheduleWeekly(int $currentDay): void\n {\n if ($currentDay === 0) {\n $this->scheduleCommand('crm:update-opp-specs')->weeklyOn(0);\n }\n\n if ($currentDay === 6) {\n $this->scheduleCommand('jiminny:acl:remove-expired-role-change-events')->saturdays();\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_AMAZON_CONNECT,\n '--from' => now()->subDays(7)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->saturdays()->at('01:00')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-past', ['--force'], 60)\n ->saturdays()->at('01:07')->runInBackground();\n $this->scheduleCommand('calendar:event:delete-cancelled', ['--force'], 60 * 47 + 52)\n ->saturdays()->at('05:08')->runInBackground();\n $this->scheduleCommand('nudges-data-clean-up --squashNudgeRuns')\n ->weeklyOn(6, '6:00');\n $this->scheduleCommand('nudges-data-clean-up --pruneOldRuns --retentionDays=35')\n ->weeklyOn(6, '7:00');\n }\n }\n\n protected function scheduleSpecificTimes(): void\n {\n $this->scheduleCommand('deal-risks:calculate', ['--cronjob'])->dailyAt('00:00');\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_DISCARDED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:20')->runInBackground();\n\n $this->scheduleCommand(DeleteInboxEmailsCommand::NAME, [\n '--status' => InboxEmail::STATUS_PROCESSED,\n '--to' => now()->subWeeks(2)->format('Y-m-d'),\n ])->saturdays()->at('00:30')->runInBackground();\n\n $this->scheduleCommand('automated-reports')->dailyAt('01:00');\n $this->scheduleCommand('crm:sync-team-metadata')->dailyAt('01:05');\n $this->scheduleCommand('crm:sync-profile-metadata')->dailyAt('01:05');\n $this->scheduleCommand('calendar:sync-deleted-events')->dailyAt('01:10');\n $this->scheduleCommand('teams:delete-retention')->dailyAt('02:55');\n $this->scheduleCommand('teams:delete-deactivated')->dailyAt('02:58');\n $this->scheduleCommand('twilio:remote-lifecycle')->dailyAt('03:00');\n\n $this->scheduleCommand('activity:sync', [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('03:05');\n\n\n $this->scheduleCommand('activities:delete-retention-teams', expiresAt: 240)->dailyAt('03:04');\n $this->scheduleCommand('automated-reports:run-retention-policy', expiresAt: 120)->dailyAt('03:15');\n $this->scheduleCommand('stop:hanging:livestreams')->dailyAt('03:30');\n $this->scheduleCommand('crm:purge-sync-batches')->dailyAt('03:45');\n $this->scheduleCommand('twilio:sync-numbers')->dailyAt('04:00');\n\n if (! $this->app->environment('production')) {\n $this->scheduleCommand('activities:hard-delete', ['--limit' => 1000, '--jobs' => 5], 60)\n ->dailyAt('04:02')->runInBackground();\n }\n\n $this->scheduleCommand('crm:full-sync-opportunity')->dailyAt('05:00');\n\n $this->scheduleCommand('activity:sync', [\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--skipProviders' => [\n Activity::PROVIDER_VONAGE,\n Activity::PROVIDER_FIVE_NINE,\n ],\n ])->dailyAt('05:05');\n\n if (! $this->app->environment('qa')) {\n $this->scheduleCommand('ai-crm-notes:delete-old')->dailyAt('07:00');\n }\n\n $this->scheduleCommand('activity:sync-dispositions', [\n Activity::PROVIDER_HUBSPOT,\n '--from' => now()->subDay()->startOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->subDay()->endOfDay()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->dailyAt('07:05');\n }\n\n protected function scheduleDynamic(int $currentMinute): void\n {\n $this->scheduleHourlyFallbackActivitySyncs($currentMinute);\n $this->scheduleBullhornHeartbeat($currentMinute);\n }\n\n private function scheduleHourlyFallbackActivitySyncs(int $offsetMinute): void\n {\n if ($offsetMinute === 0) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUD_TALK, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_VONAGE, 6, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_CLOUDCALL_US, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_FIVE_NINE, 3, 0);\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_HUBSPOT, 1, 0);\n } elseif ($offsetMinute === 1) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_RINGCENTRAL, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 1);\n } elseif ($offsetMinute === 2) {\n $this->scheduleHourlyFallbackActivitySync(Activity::PROVIDER_AVAYA, Constants::RINGCENTRAL_CALL_LOG_LOOK_BACK_HOURS, 2);\n }\n }\n\n private function scheduleBullhornHeartbeat(int $currentMinute): void\n {\n $bhHeartbeatInterval = config('services.bullhorn.heartbeatInterval', 0);\n if ($bhHeartbeatInterval > 0) {\n $minutes = max((int) floor($bhHeartbeatInterval / 60), 1);\n if ($currentMinute % $minutes === 0) {\n $bhEvent = $this->scheduleCommand('crm:bullhorn:ping', ['--heartbeat']);\n if ($minutes > 30) {\n $bhEvent->hourly();\n } else {\n $bhEvent->cron(sprintf('*/%d * * * *', $minutes));\n }\n }\n }\n }\n\n private function scheduleActivitiesHardDelete(): void\n {\n if (config(key: 'jiminny.deploy_region') === 'eu') {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 1000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n } elseif ($this->app->environment('production')) {\n $this->scheduleCommand(\n name: 'activities:hard-delete',\n options: ['--limit' => 2000, '--jobs' => 20],\n expiresAt: 29\n )\n ->between('02:59', '07:02')->everyThirtyMinutes()\n ->runInBackground();\n }\n }\n\n private function scheduleHourlyFallbackActivitySync(string $provider, int $hours, int $offsetMinute = 0): void\n {\n $this->scheduleCommand('activity:sync', [\n $provider,\n '--from' => now()->subHours($hours)->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n '--to' => now()->startOfMinute()->format(SyncActivity::ALLOWED_DATE_FORMAT),\n ])->hourlyAt($offsetMinute);\n }\n\n /**\n * Register the Closure based commands for the application.\n */\n protected function commands(): void\n {\n require_once base_path('routes/console.php');\n }\n\n private function scheduleCommand(string $name, array $options = [], $expiresAt = 60 * 3): Event\n {\n return $this->schedule\n ->command($name, $options)\n ->withoutOverlapping($expiresAt)\n ->onOneServer()\n ->sendOutputTo($this->output)\n ;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7874037434345827966
|
2696331441572408523
|
click
|
accessibility
|
NULL
|
6 files committed
pipedrive offical SDK v2 POC
tex 6 files committed
pipedrive offical SDK v2 POC
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
pipedrive-sdk-poc, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
organiza
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/10
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
Built-in Preview
Chrome
Firefox
Safari
2
5
3
16
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
/**
* API routes.
*
* @see \Jiminny\Providers\RouteServiceProvider
*
* @var Router $router
*/
use Illuminate\Routing\Router;
use Illuminate\Support\Collection;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Component\Router\Routes;
use Jiminny\Contracts\Acl\PermissionEnum;
use Jiminny\Http\Controllers;
use Jiminny\Http\Controllers\API\ActivityController;
use Jiminny\Http\Controllers\API\AiCrmNotesController;
use Jiminny\Http\Controllers\API\ClientTokenController;
use Jiminny\Http\Controllers\API\CrmController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAiCallScoringController;
use Jiminny\Http\Controllers\ConferencesOptInOutController;
use Jiminny\Http\Controllers\API\DealRiskController;
use Jiminny\Http\Controllers\API\InstantMeetingController;
use Jiminny\Http\Controllers\API\LanguageController;
use Jiminny\Http\Controllers\API\LiveFeedController;
use Jiminny\Http\Controllers\API\MeetingsController;
use Jiminny\Http\Controllers\API\MessageController;
use Jiminny\Http\Controllers\API\MetadataController;
use Jiminny\Http\Controllers\API\MobileSettingsController;
use Jiminny\Http\Controllers\API\MomentController;
use Jiminny\Http\Controllers\API\NudgeController;
use Jiminny\Http\Controllers\API\NumberAllocatorController;
use Jiminny\Http\Controllers\API\Opportunity\CommentsController;
use Jiminny\Http\Controllers\API\OrganizationLicensesController;
use Jiminny\Http\Controllers\API\OrganizationMembersController;
use Jiminny\Http\Controllers\API\OrganizationRetentionPolicyController;
use Jiminny\Http\Controllers\API\OrganizationRolesController;
use Jiminny\Http\Controllers\API\OrganizationSyncController;
use Jiminny\Http\Controllers\API\Page\OnDemandController;
use Jiminny\Http\Controllers\API\Page\PlaybackController;
use Jiminny\Http\Controllers\API\PartnerController;
use Jiminny\Http\Controllers\API\PhoneNumberController;
use Jiminny\Http\Controllers\API\PlaylistController;
use Jiminny\Http\Controllers\API\Settings\EmailSyncController;
use Jiminny\Http\Controllers\API\SidekickController;
use Jiminny\Http\Controllers\API\SoftphoneController;
use Jiminny\Http\Controllers\API\SubscriptionController;
use Jiminny\Http\Controllers\API\TeamAiAutomationController;
use Jiminny\Http\Controllers\API\TeamAiContextController;
use Jiminny\Http\Controllers\API\TeamController;
use Jiminny\Http\Controllers\API\TeamInsights\ActivityStatsController;
use Jiminny\Http\Controllers\API\TeamInsights\CoachingFeedbacksController;
use Jiminny\Http\Controllers\API\TeamInsights\DashboardController;
use Jiminny\Http\Controllers\API\TeamInsights\EngagementController;
use Jiminny\Http\Controllers\API\TeamInsights\TeamInsightsAutomatedCallScoresController;
use Jiminny\Http\Controllers\API\TeamInsights\ThemeTopicsController;
use Jiminny\Http\Controllers\API\TeamInsights\TopicsInDealsController;
use Jiminny\Http\Controllers\API\TeamInsightsController;
use Jiminny\Http\Controllers\API\Themes\ThemeController;
use Jiminny\Http\Controllers\API\Themes\TopicController;
use Jiminny\Http\Controllers\API\Themes\TopicTriggerController;
use Jiminny\Http\Controllers\API\TranscriptionController;
use Jiminny\Http\Controllers\API\TranslationController;
use Jiminny\Http\Controllers\API\UserAutomatedReports\UserAutomatedReportsController;
use Jiminny\Http\Controllers\API\UserController;
use Jiminny\Http\Controllers\API\VocabularyController;
use Jiminny\Http\Controllers\Auth\ExtensionController;
use Jiminny\Http\Controllers\Auth\SocialController;
use Jiminny\Http\Controllers\ExportController;
use Jiminny\Http\Controllers\Kiosk\ActivityController as KioskActivityController;
use Jiminny\Http\Controllers\Kiosk\AutomatedReportsController;
use Jiminny\Http\Controllers\Kiosk\MediaPipelineController;
use Jiminny\Http\Controllers\Kiosk\OrganizationsController;
use Jiminny\Http\Controllers\Kiosk\PartnersController;
use Jiminny\Http\Controllers\Kiosk\SearchController;
use Jiminny\Http\Controllers\Kiosk\Teams\OnboardController;
use Jiminny\Http\Controllers\NotificationController;
use Jiminny\Http\Controllers\Settings\GroupController;
use Jiminny\Http\Controllers\Settings\JobTitleController;
use Jiminny\Http\Controllers\Settings\PlaybookCategoryController;
use Jiminny\Http\Controllers\Settings\PlaybookController;
use Jiminny\Http\Controllers\Settings\Teams\IntegrationController;
use Jiminny\Http\Controllers\Settings\Teams\InvitationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamActivityController;
use Jiminny\Http\Controllers\Settings\Teams\TeamCoachingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamConferenceSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamController as OrganizationController;
use Jiminny\Http\Controllers\Settings\Teams\TeamDealInsightsSettingController;
use Jiminny\Http\Controllers\Settings\Teams\TeamMemberController;
use Jiminny\Http\Controllers\Settings\Teams\TeamPhotoController;
use Jiminny\Http\Controllers\Settings\Teams\TeamRecordingSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSettingsController;
use Jiminny\Http\Controllers\Settings\Teams\TeamSoftphoneSettingsController;
use Jiminny\Http\Controllers\TeamSetupController;
use Jiminny\Models;
use Jiminny\Models\PlaybackTheme;
use Jiminny\Models\SocialAccount;
use Jiminny\Models\User;
use Jiminny\Models\Vocabulary;
use Jiminny\Repositories;
use Jiminny\Mcp\Servers\JiminnyServer;
use Laravel\Mcp\Facades\Mcp;
// mcp.audit MUST stay outermost so its $next($request) call wraps the auth
// and tier guards. Otherwise 401 (auth:api) and 403 (mcp.tier) rejections
// short-circuit before McpAuditMiddleware::handle ever runs and we lose
// audit rows for exactly the requests the security log most needs to capture.
// McpAuditMiddleware::writeAuditRow null-checks $request->user(), so writing
// pre-auth is safe.
Mcp::web('/mcp', JiminnyServer::class)
->middleware(['mcp.audit', 'auth:api', 'mcp.tier']);
$router->group(['middleware' => ['auth:api']], static function (Router $router): void {
$router->get('/metadata/extension-app', [MetadataController::class, 'extension']);
$router->get('/', [NumberAllocatorController::class, 'generate']);
$router->delete('/key-moment/{activityMoment}', [MomentController::class, 'destroy']);
$router->post('/instant-meeting/start', [InstantMeetingController::class, 'postRequestBotAtUrl'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('instant-meeting.start');
// Meeting creation endpoint for Outlook add-in
$router->post('/meetings', [MeetingsController::class, 'create'])
->middleware(['permission:' . PermissionEnum::RECORD_MEETING->value])
->name('meetings.create');
// Number provisioning and search.
$router->get('/phone-numbers', [NumberAllocatorController::class, 'generate']);
$router->get('/phone-numbers/{number}', [PhoneNumberController::class, 'number']);
$router->group(['prefix' => 'deal-insights'], static function (Router $router): void {
$router->get('/forecast', [
Controllers\API\DealInsights\DealsController::class,
'getForecast',
])->defaults('period', Forecast::PERIOD_QUARTER);
$router->get('/deals/{stage?}', [
Controllers\API\DealInsights\DealsController::class,
'list',
])->defaults('stage', \Jiminny\Component\DealInsights\CriteriaInterface::STAGE_ALL);
$router->get('/details/details-daily/{opportunityId}/{date}', [
Controllers\API\DealInsights\DealsController::class,
'detailsDaily',
]);
$router->put('/deals/{opportunity}/edit-fields', [
Controllers\API\DealInsights\DealsController::class,
'updateFields',
]);
$router->get('/externalId/{dealId}', [
Controllers\API\DealInsights\DealsController::class,
'externalDealId',
]);
$router->put('/dealRisk/{dealRisk}', [DealRiskController::class, 'toggleActivity']);
});
$router->get('/team-insights/users', [TeamInsightsController::class, 'fetchUsers'])
->name('team_insights.users');
$router->get('/team-insights/dashboard', [DashboardController::class, 'fetch'])
->name('team_insights.dashboard');
// Team Insights - Coaching Feedbacks
$router->get('/team-insights/coaching-feedbacks-over-time', [CoachingFeedbacksController::class, 'fetch'])
->name('team_insights.coaching_feedbacks_over_time');
$router
->get('/team-insights/coaching-feedbacks-over-time/download', [CoachingFeedbacksController::class, 'download'])
->name('team_insights.coaching_feedbacks_over_time.download');
$router->get(
'/team-insights/coaching-feedbacks-over-time/drill-down',
[CoachingFeedbacksController::class, 'drillDown'],
)->name('team_insights.coaching_feedbacks_over_time.drill_down');
// Team Insights - Automated Call Scores
$router->get(
'/team-insights/automated-call-scores-over-time',
[TeamInsightsAutomatedCallScoresController::class, 'index'],
)->name('team_insights.automated_call_scores_over_time.index');
$router->get(
'/team-insights/automated-call-scores-over-time/drill-down',
[TeamInsightsAutomatedCallScoresController::class, 'show'],
)->name('team_insights.automated_call_scores_over_time.show');
// Team Insights - AI Call Scoring
$router->get(
'/team-insights/ai-call-scoring-over-time',
[TeamInsightsAiCallScoringController::class, 'index'],
)->name('team_insights.ai_call_scoring_over_time.index');
$router->get(
'/team-insights/ai-call-scoring-over-time/drill-down',
[TeamInsightsAiCallScoringController::class, 'show'],
)->name('team_insights.ai_call_scoring_over_time.show');
$router->get('/team-insights/engagement', [ActivityStatsController::class, 'fetch'])
->name('team_insights.engagement');
$router->get('/team-insights/engagement/drill-down/{engagementType}', [ActivityStatsController::class, 'drillDown'])
->name('team_insights.engagement.drill_down');
$router->get('/team-insights/topics', [ThemeTopicsController::class, 'getTopics'])
->name('team_insights.topics.index');
$router->get('/team-insights/topics/{topic}', [ThemeTopicsController::class, 'fetch'])
->name('team_insights.topics.show');
$router->get('/team-insights/topics/{topic}/drill-down', [ThemeTopicsController::class, 'drillDown'])
->name('team_insights.topics.drill_down');
$router->group(['prefix' => 'team-insights'], static function (Router $router): void {
$router->group(['prefix' => 'conversations'], static function (Router $router): void {
$router->get('/', [
Controllers\API\TeamInsights\ConversationsController::class,
'fetch',
]);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{activityChannel}/{drillDownType}', [
Controllers\API\TeamInsights\ConversationsController::class,
'drillDown',
])
->where(
'activityChannel',
Collection::make(Models\Activity::CHANNELS)->join('|'),
)
->where(
'drillDownType',
Collection::make(Repositories\TeamInsightsRepository::CONVERSATION_DRILLDOWNS)
->join('|'),
);
});
});
$router->group(['prefix' => 'coaching'], static function (Router $router): void {
$router->get('/', [EngagementController::class, 'fetch']);
$router->group(['prefix' => 'drill-down'], static function (Router $router): void {
$router
->get('/{coachingType}/{drillDownType?}', [EngagementController::class, 'drillDown'])
->where(
'coachingType',
Collection::make(EngagementController::COACHING_TYPES)->join('|'),
)
->where(
'drillDownType',
Collection::make(EngagementController::COACHING_DRILLDOWNS)->join('|'),
);
});
});
});
$router->get('/topics-in-deals', [TopicsInDealsController::class, 'topics'])
->name('topics_in_deals.topics');
$router->get('/topics-in-deals/topic-triggers', [TopicsInDealsController::class, 'topicTriggers'])
->name('topics_in_deals.topic_triggers');
$router->get('/compare-topics-in-deals', [TopicsInDealsController::class, 'comparison'])
->name('topics_in_deals.comparison');
// CRM actions.
$router->group(['prefix' => 'crm'], static function (Router $router): void {
$router->get('/search', [CrmController::class, 'search']);
$router->get('/opportunity', [CrmController::class, 'opportunities']);
$router->get('/customers', [CrmController::class, 'customers']);
$router->get('/accounts', [CrmController::class, 'accounts']);
$router->get('/contacts', [CrmController::class, 'contacts']);
$router->get('/leads', [CrmController::class, 'leads']);
$router->get('/tasks', [CrmController::class, 'activities']);
$router->get('/layouts', [CrmController::class, 'layouts']);
});
// AI CRM notes.
$router->group(['prefix' => 'ai-crm-notes'], static function (Router $router): void {
$router->get('/activity/{activity}', [AiCrmNotesController::class, 'getByActivity']);
$router->post('/activity/{activity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByActivity']);
$router->post('/activity/{activity}/discard', [AiCrmNotesController::class, 'discardByActivity']);
$router->get('/deal/{opportunity}', [AiCrmNotesController::class, 'getByOpportunity']);
$router->post('/deal/{opportunity}/log-to-crm', [AiCrmNotesController::class, 'logToCrmByOpportunity']);
$router->post('/deal/{opportunity}/discard', [AiCrmNotesController::class, 'discardByOpportunity']);
});
// Automated Reports
$router->post('/automated-reports/interest', [UserAutomatedReportsController::class, 'trackInterest']);
$router->group(
[
'prefix' => 'automated-reports',
'middleware' => 'can:canAccessAiReports,' . User::class,
],
static function (Router $router): void {
$router->get('/', [UserAutomatedReportsController::class, 'list']);
$router->delete('/{uuid}', [UserAutomatedReportsController::class, 'delete']);
}
);
// Setup New Team / Trial
$router->get('/features', [TeamSetupController::class, 'features']);
$router->get('/tiers', [TeamSetupController::class, 'tiers']);
$router->get('/calendars', [TeamSetupController::class, 'calendars']);
$router->get('/crm-services', [TeamSetupController::class, 'crmServices']);
$router->get('/connect-providers', [TeamSetupController::class, 'connectProviders']);
$router->get('/integration-app-token', [TeamSetupController::class, 'integrationAppToken']);
$router->post('/integration-app-connect', [TeamSetupController::class, 'integrationAppConnect']);
// Notifications
$router->get('/notifications/recent', [NotificationController::class, 'notifications']);
$router->put('/notifications/read', [NotificationController::class, 'markAsRead']);
$router->put('/notifications/read-multiple', [NotificationController::class, 'markMultipleAsRead']);
$router->put('/notifications/read-all', [NotificationController::class, 'markAllAsRead']);
// Live feed
$router->get('/live-feed', [LiveFeedController::class, 'liveFeedItems']);
// Languages
$router->get('/languages', [LanguageController::class, 'list']);
// The whole settings section will be moved out in a separate file
$router->group(['prefix' => '/settings'], static function (Router $router): void {
$router->group(['prefix' => '/organizations'], static function (Router $router): void {
$router
->middleware(['can:kiosk,' . User::class])
->post('/', [OrganizationController::class, 'store'])
->name('kiosk.organizations.store');
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router) {
// Sync fields and team metadata
$router->post('/fields/sync', [OrganizationSyncController::class, 'index'])
->name('api.sync.fields');
// Conference Preferences.
$router->post('/bot-avatar', [TeamPhotoController::class, 'updateBotAvatar'])
->name('update.bot.avatar');
// Roles.
$router->get('/roles', [OrganizationRolesController::class, 'index'])
->name('api.roles.index');
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_RETENTION_POLICY->value],
static function (Router $router): void {
$router->get('/retention-policy', [OrganizationRetentionPolicyController::class, 'index'])
->name('api.retention_policy.index');
$router->post('/retention-policy', [OrganizationRetentionPolicyController::class, 'store'])
->name('api.retention_policy.update');
}
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_USERS->value],
static function (Router $router): void {
// Invitations.
$router->get('/invitations', [InvitationController::class, 'index'])
->name('api.invitations.index');
$router->post('/invitations/{invitation}', [InvitationController::class, 'resend'])
->name('api.invitations.resend');
$router->delete('/invitations/{invitation}', [InvitationController::class, 'destroy'])
->name('api.invitations.delete');
$router->post('/invitations', [InvitationController::class, 'store'])
->name('api.invitations.store');
},
);
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_TEAM->value],
static function (Router $router): void {
// Groups.
$router->post('/groups', [GroupController::class, 'store']);
$router->get('/groups/{group}', [GroupController::class, 'show']);
$router->put('/groups/{group}', [GroupController::class, 'update']);
$router->put('/group/{group}/scope', [GroupController::class, 'updateGroupScope']);
$router->post('/group/{group}/dealRisks', [DealRiskController::class, 'updateSettings']);
// Sidekick settings
$router->group(
['middleware' => 'permission:' . PermissionEnum::MANAGE_SIDEKICK->value],
static function (Router $router): void {
$router->get('/sidekick', [SidekickController::class, 'getSidekickSettings']);
$router
->post(
'/group/{group}/sidekick',
[SidekickController::class, 'setSidekickSettings'],
)
->middleware(['can:updateSidekickSettings,group'])
->name('api.sidekick_settings.update');
$router
->post('/sidekick', [SidekickController::class, 'setSidekickSettings'])
->middleware(['permission:' . PermissionEnum::UPDATE_ALL_SIDEKICK_SETTINGS->value])
->name('api.sidekick_settings.update_all');
},
);
$router->get('/deal-insights', [TeamDealInsightsSettingController::class, 'index']);
$router->patch('/deal-insights', [TeamDealInsightsSettingController::class, 'update']);
// CRM Layout Management
$router->group(['prefix' => 'layouts'], static function (Router $router): void {
$router->get(
'/{type}',
[Controllers\API\LayoutManagementController::class, 'list'],
)->name('layouts.list');
$router->put(
'/{layout}',
[Controllers\API\LayoutManagementController::class, 'update'],
)->name('layouts.update');
});
// Users.
$router->put('/users/{user}', [TeamMemberController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.update');
$router->delete('/users/{user}', [TeamMemberController::class, 'deactivate'])
->middleware(['permission:' . PermissionEnum::MANAGE_USERS->value])
->name('api.users.deactivate');
$router->group(
[
'prefix' => 'vocabulary',
'middleware' => 'can:manage,' . Vocabulary::class,
],
static function (Router $router): void {
$router
->get('/', [VocabularyController::class, 'list'])
->name('api.vocabulary.index');
$router
->post('/', [VocabularyController::class, 'update'])
->name('api.vocabulary.create');
$router->group(['prefix' => '{vocabulary}'], static function (Router $router): void {
$router
->put('/', [VocabularyController::class, 'update'])
->middleware('can:update,vocabulary')
->name('api.vocabulary.update');
$router
->delete('/', [VocabularyController::class, 'delete'])
->middleware('can:delete,vocabulary')
->name('api.vocabulary.delete');
});
},
);
$router->group(['prefix' => 'ai-context'], static function (Router $router): void {
$router->get('/', [TeamAiContextController::class, 'index'])
->name('api.ai_context.get');
$router->post('/', [TeamAiContextController::class, 'store'])
->name('api.ai_context.store');
});
$router->group(['prefix' => 'ai-automation'], static function (Router $router): void {
$router->post('/fields/test-prompt', [TeamAiAutomationController::class, 'testCrmAiPrompt'])
->name('api.automation.templates.fields.test-prompt');
// List CRM fields per object type
$router->get('/fields/{objectType}', [TeamAiAutomationController::class, 'fields'])
->name('api.automation.fields');
// List DealStages fields per object type
$router->get('/stages', [TeamAiAutomationController::class, 'stages'])
->name('api.automation.stages');
// Create CRM AI template
$router->post('/templates', [TeamAiAutomationController::class, 'createTemplate'])
->name('api.automation.templates.create');
// Export CRM updates
$router->post('/templates/export-crm-updates', [TeamAiAutomationController::class, 'exportTemplateCrmUpdates'])
->name('api.automation.templates.export-crm-updates');
// Update CRM AI template
$router->put('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'updateTemplate'])
->name('api.automation.templates.update');
// Delete CRM AI template
$router->delete('/templates/{crmTemplate}', [TeamAiAutomationController::class, 'deleteTemplate'])
->name('api.automation.templates.delete');
// List all CRM AI templates
$router->get('/templates', [TeamAiAutomationController::class, 'templates'])
->name('api.automation.templates.list');
// Create CRM AI template field
$router->post('/templates/{crmTemplate}/fields', [TeamAiAutomationController::class, 'createField'])
->name('api.automation.templates.fields.create');
// Update CRM AI template field
$router->put('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'updateField'])
->name('api.automation.templates.fields.update');
// Delete CRM AI template field
$router->delete('/templates/{crmTemplate}/fields/{crmTemplateField}', [TeamAiAutomationController::class, 'deleteField'])
->name('api.automation.templates.fields.delete');
});
$router->group(['prefix' => 'ai-call-scoring'], static function (Router $router): void {
// Create AI scorecard
$router->post('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'createAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.create');
// Update AI scorecard
$router->put('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'updateAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.update');
// Delete AI scorecard
$router->delete('/ai-scorecards/{aiScorecard}', [Controllers\API\AiCallScoring\AiScorecardController::class, 'deleteAiScorecard'])
->name('api.ai-call-scoring.ai-scorecards.delete');
// List all AI scorecards
$router->get('/ai-scorecards', [Controllers\API\AiCallScoring\AiScorecardController::class, 'aiScorecards'])
->name('api.ai-call-scoring.ai-scorecards.list');
// Test AI scorecard prompt
$router->post(
'/ai-scorecards/{aiScorecard}/test-prompt',
[
Controllers\API\AiCallScoring\AiScorecardController::class,
'testAiScorecardPrompt',
]
)
->name('api.ai-call-scoring.ai-scorecards.test-prompt');
// Create AI Scorecard rule
$router->post('/ai-scorecards/{aiScorecard}/ai-scorecard-rules', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'createRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.create');
// Update AI Scorecard rule
$router->put('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'updateAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.update');
// Delete AI Scorecard rule
$router->delete('/ai-scorecards/{aiScorecard}/ai-scorecard-rules/{aiScorecardRule}', [Controllers\API\AiCallScoring\AiScorecardRuleController::class, 'deleteAiScorecardRule'])
->name('api.ai-call-scoring.ai-scorecards.ai-scorecard-rules.delete');
});
// Theme, topics, triggers
$router->get('/themes', [ThemeController::class, 'list']);
$router
->post('/themes', [ThemeController::class, 'updateTheme'])
->middleware('can:manage,' . PlaybackTheme::class)
->name('api.theme.create');
$router->group(
[
'prefix' => 'theme/{theme}',
'middleware' => 'can:update,theme',
],
static function (Router $router): void {
$router
->put('/', [ThemeController::class, 'updateTheme'])
->name('api.theme.update');
$router
->delete('/', [ThemeController::class, 'deleteTheme'])
->middleware('can:delete,theme')
->name('api.theme.delete');
$router
->post('/topics', [TopicController::class, 'updateTopic'])
->middleware('can:createTopic,theme')
->name('api.topic.create');
$router->group(
[
'prefix' => 'topic/{topic}',
'middleware' => 'can:update,topic',
],
static function (Router $router): void {
$router
->put('/', [TopicController::class, 'updateTopic'])
->name('api.topic.update');
$router
->delete('/', [TopicController::class, 'deleteTopic'])
->middleware('can:delete,topic')
->name('api.topic.delete');
$router
->post('/triggers', [TopicTriggerController::class, 'updateTrigger'])
->middleware('can:createTrigger,topic')
->name('api.topic_trigger.create');
$router->group(
[
'prefix' => 'trigger/{topicTrigger}',
'middleware' => 'can:update,topicTrigger',
],
static function (Router $router): void {
$router
->put('/', [TopicTriggerController::class, 'updateTrigger'])
->name('api.topic_trigger.update');
$router
->delete('/', [TopicTriggerController::class, 'deleteTrigger'])
->middleware('can:delete,topicTrigger')
->name('api.topic_trigger.delete');
},
);
},
);
},
);
$router->post('/themes/import', [Controllers\API\Themes\ImportTopicTriggerController::class, 'importThemes']);
$router->get('/themes/export', [Controllers\API\Themes\ExportTopicTriggerController::class, 'exportThemes']);
// Auto-scoring
$router->group(['prefix' => '/scorecards'], static function (Router $router) {
$router->get('/', [Controllers\API\Scorecards\ScorecardController::class, 'list']);
$router->post('/', [Controllers\API\Scorecards\ScorecardController::class, 'create']);
$router->delete('/{scorecard}', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/validate-name', [
Controllers\API\Scorecards\ScorecardController::class,
'validateNameExists',
]);
$router->get('/enabled-scorecard', [
Controllers\API\Scorecards\ScorecardController::class,
'getEnabledScorecard',
]);
$router->get('/affected-scorecards', [
Controllers\API\Scorecards\ScorecardController::class,
'getAffectedScorecards',
]);
$router->group(['prefix' => '/{scorecard}'], static function (Router $router) {
$router->put('/', [
Controllers\API\Scorecards\ScorecardController::class,
'update',
]);
$router->delete('/', [
Controllers\API\Scorecards\ScorecardController::class,
'delete',
]);
$router->post('/rules', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'create',
]);
$router->post('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'update',
]);
$router->delete('/rules/{scorecardRule}', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'delete',
]);
$router->post('/rules/{scorecardRule}/update-order', [
Controllers\API\Scorecards\ScorecardRuleController::class,
'updateOrder',
]);
});
});
// Coaching Playbook.
Route::get('/playbooks', [PlaybookController::class, 'all']);
Route::get('/playbooksTree', [PlaybookController::class, 'tree']);
Route::put('/playbooks/{playbook}', [PlaybookController::class, 'update']);
Route::post('/playbooks', [PlaybookController::class, 'store']);
Route::delete('/playbooks/{playbook}', [PlaybookController::class, 'destroy']);
Route::prefix('/playbooks/{playbook}')->group(static function () {
// Playbook Categories.
Route::get('/categories', [PlaybookCategoryController::class, 'all']);
Route::put('/categories/sequence', [PlaybookCategoryController::class, 'sequence']); // Respect order.
Route::put('/categories/{category}', [PlaybookCategoryController::class, 'update']);
Route::post('/categories', [PlaybookCategoryController::class, 'store']);
Route::post('/test-prompt', [PlaybookController::class, 'testAiActivityTypePrompt']);
Route::post('/prompt-suggestion', [PlaybookController::class, 'getPromptSuggestion']);
Route::delete('/categories/{category}', [PlaybookCategoryController::class, 'destroy']);
Route::prefix('/categories/{category}')->group(static function () {
// Coaching Sections
Route::get('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'all']);
Route::put('/coaching-section/sequence', [Controllers\Settings\Coaching\SectionsController::class, 'sequence']);
Route::put('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'update']);
Route::post('/coaching-section', [Controllers\Settings\Coaching\SectionsController::class, 'store']);
Route::delete('/coaching-section/{coachingSection}', [Controllers\Settings\Coaching\SectionsController::class, 'destroy']);
Route::prefix('coaching-section/{coachingSection}')->group(static function () {
// Coaching Section Criteria
Route::get('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'all']);
Route::put('/coaching-section-criterion/sequence', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'sequence']);
Route::put('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'update']);
Route::post('/coaching-section-criterion', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'store']);
Route::delete('/coaching-section-criterion/{coachingSectionCriterion}', [Controllers\Settings\Coaching\SectionCriteriaController::class, 'destroy']);
});
});
});
},
);
$router->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value])
->group(static function (Router $router): void {
// Job Titles.
$router->get('/job-titles', [JobTitleController::class, 'all']);
$router->put('/job-titles/{job}', [JobTitleController::class, 'update']);
$router->post('/job-titles', [JobTitleController::class, 'store']);
$router->delete('/job-titles/{job}', [JobTitleController::class, 'destroy']);
// Team Settings.
$router->put('/', [TeamSettingsController::class, 'update']);
$router->put('/notifications', [TeamSettingsController::class, 'updateNotifications']);
$router->put('/team-conference', [TeamConferenceSettingsController::class, 'update']);
$router->put('/team-coaching', [TeamCoachingSettingsController::class, 'update']);
$router->put('/team-softphone', [TeamSoftphoneSettingsController::class, 'update']);
$router->put('/owner', [Controllers\Settings\Teams\OrganizationSettingsController::class, 'updateOwner']);
$router->put('/team-recording', [TeamRecordingSettingsController::class, 'update'])
->middleware(['permission:' . PermissionEnum::MANAGE_RECORDING->value]);
// Key Moments.
$router->get('/moments/{moment}', [Controllers\Settings\MomentController::class, 'show']);
$router->put('/moments/{moment}', [Controllers\Settings\MomentController::class, 'update']);
$router->post('/moments', [Controllers\Settings\MomentController::class, 'store']);
$router->put('/activity', [TeamActivityController::class, 'store']);
// Team Domains.
$router->get('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'all']);
$router->post('/domains', [Controllers\Settings\Teams\TeamDomainsController::class, 'create']);
$router->delete('/domains/{teamDomain}', [Controllers\Settings\Teams\TeamDomainsController::class, 'destroy']);
});
});
});
});
// Integrations
$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGRATIONS->value], static function (Router $router): void {
$router->post('/integrations', [IntegrationController::class, 'internal'])
->name('api.integrations.internal');
$router->put('/integrations', [IntegrationController::class, 'toggleStatus'])
->name('api.integrations.toggle_status');
$router->delete('/integrations/{provider}', [IntegrationController::class, 'delete'])
->name('api.integrations.delete');
});
$router->get('/integrations', [IntegrationController::class, 'all'])
->middleware('permission:' . PermissionEnum::READ_INTEGRATIONS->value)
->name('api.integrations.index');
// Slack API for getting slack channels list
$router->get('{notificationProvider}/channels', [Controllers\NotificationProviderController::class, 'channels']);
// Team actions. XXX: These all need moving out to their own controllers.
$router->group(['prefix' => 'organizations'], static function (Router $router): void {
$router->get('current', [TeamController::class, 'current']);
$router->group(['prefix' => '{team}', 'middleware' => ['teamMember']], static function (Router $router): void {
$router->get('/', [TeamController::class, 'show']);
$router->get('/categories', [TeamController::class, 'categories']);
$router->get('/stages', [TeamController::class, 'stages']);
$router->get('/users', [OrganizationMembersController::class, 'index'])
->name('organization.members.index');
$router
->get('/users/download', [OrganizationMembersController::class, 'download'])
->middleware('permission:' . PermissionEnum::MANAGE_USERS->value)
->name('organization.members.download');
$router->get('/licensed-roles', [OrganizationLicensesController::class, 'index'])
->middleware('permission:' . PermissionEnum::MANAGE_BILLING->value)
->name('organization.licensed-roles.index');
$router->get('/invitations', [TeamController::class, 'invitations']);
$router->get('/groups', [TeamController::class, 'groups']);
$router->delete('/groups/{group}', [TeamController::class, 'deleteGroup'])
->middleware(['permission:' . PermissionEnum::DELETE_TEAM->value])
->name('api.groups.delete');
$router->get('/job-titles', [TeamController::class, 'jobTitles']);
$router->get('/slugs', [TeamController::class, 'slugs']);
$router->put('/api-token', [TeamController::class, 'generateApiToken'])
->middleware(['permission:' . PermissionEnum::MANAGE_ORGANIZATION_SETTINGS->value]);
$router->get('/key-moments', [MomentController::class, 'all']);
});
});
// Internal Kiosk. This whole section will be moved out to a separate file
$router
->prefix('kiosk')
->middleware('can:kiosk,' . User::class)
->group(static function (Router $router): void {
// Partner actions.
$router->get('/partners', [PartnersController::class, 'index']);
// User actions.
$router->post('/users/search', [SearchController::class, 'performBasicSearch']);
// Team actions.
$router->prefix('organizations')->group(static function (Router $router): void {
$router->get('/', [OrganizationsController::class, 'show']);
$router->put('/{team}', [OrganizationController::class, 'edit'])
->name('kiosk.organizations.edit');
$router->get('/{team}/users', [OrganizationMembersController::class, 'index'])
->name('kiosk.organization.members.index');
$router->get('onboardable', [OnboardController::class, 'available']);
$router->delete('/{team}', [OrganizationsController::class, 'deactivateAccounts']);
});
// Automated reports
// api/v1/kiosk/automated-reports
$router->prefix('automated-reports')->group(static function (Router $router): void {
$router->get('/form-data', [AutomatedReportsController::class, 'getCreateForm']);
$router->get('/form-data/{reportUuid}', [AutomatedReportsController::class, 'getEditForm']);
$router->post('/filters', [AutomatedReportsController::class, 'getFilters']);
$router->post('/', [AutomatedReportsController::class, 'create']);
$router->put('/{reportUuid}', [AutomatedReportsController::class, 'update']);
$router->patch('/{reportUuid}', [AutomatedReportsController::class, 'partialUpdate']);
$router->get('/', [AutomatedReportsController::class, 'list']);
$router->get('/{reportUuid}', [AutomatedReportsController::class, 'get']);
$router->delete('/{reportUuid}', [AutomatedReportsController::class, 'delete']);
$router->post('/activities-count', [AutomatedReportsController::class, 'getActivitiesCount']);
$router->get('/{reportUuid}/reports-count', [AutomatedReportsController::class, 'getReportsCount']);
});
// Activity actions.
$router->post('/activity/search', [SearchController::class, 'performActivitySearch']);
$router->prefix('activity/{activity}')->group(static function (Router $router): void {
$router->post('check-playable', [SearchController::class, 'performActivityCheckPlayable']);
$router->post('reset-crm-log', [SearchController::class, 'performResetCrmLogActivity']);
$router->get('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->post('diarize-via-transcript', [KioskActivityController::class, 'diarizeViaTranscript']);
$router->get('media-pipeline', [MediaPipelineController::class, 'getPipes']);
$router->post('media-pipeline', [MediaPipelineController::class, 'updatePipe']);
$router->post('language', [KioskActivityController::class, 'updateLanguage']);
$router->post('trim', [KioskActivityController::class, 'trimActivity']);
...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50909
|
1797
|
11
|
2026-05-18T08:08:57.518473+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091737518_m1.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-7165599669610941128
|
-904713372178709074
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50910
|
1798
|
12
|
2026-05-18T08:08:57.526235+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091737526_m2.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response....
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.090259306,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-2115221934846451277
|
-1048828560254564946
|
click
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response....
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50911
|
1797
|
12
|
2026-05-18T08:09:00.513713+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091740513_m1.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects->map(...)->toArray()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", Laravel collections sometimes preserve their underlying associative keys.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Instead of generating a clean JSON array (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[\"en_GB\", \"bg_BG\"]","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"), it might generate a JSON object with numeric string keys (e.g.,","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"{\"0\": \"en_GB\", \"1\": \"bg_BG\"}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). This would violate your schema's strict definition of","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->items($schema->string())","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Append","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->values()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"before","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->toArray()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to guarantee a sequentially indexed array:","depth":26,"bounds":{"left":0.0,"top":0.0,"width":0.21423611,"height":0.022777777},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.12361111,"top":0.0,"width":0.027777778,"height":0.044444446},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.15138888,"top":0.0,"width":0.027777778,"height":0.044444446},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->map(","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"fn","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$dialect","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") => $","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"dialect","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->","depth":27,"bounds":{"left":0.0,"top":0.032222223,"width":0.011805556,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getLanguageLocale","depth":27,"bounds":{"left":0.0,"top":0.032222223,"width":0.099305555,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.033680554,"top":0.032222223,"width":0.0055555557,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"))->","depth":27,"bounds":{"left":0.03923611,"top":0.032222223,"width":0.023611112,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"values","depth":27,"bounds":{"left":0.06284722,"top":0.032222223,"width":0.034722224,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.09756944,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")->","depth":27,"bounds":{"left":0.103472225,"top":0.032222223,"width":0.017708333,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"toArray","depth":27,"bounds":{"left":0.12118056,"top":0.032222223,"width":0.040625,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.16180556,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":27,"bounds":{"left":0.16770834,"top":0.032222223,"width":0.005902778,"height":0.020555556},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Are any of these properties (like","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$timezone","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Redo","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Share & export","depth":20,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":20,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXTextArea","text":"Enter a prompt for Gemini\nencrypted","depth":20,"on_screen":true,"value":"Enter a prompt for Gemini\nencrypted","help_text":"","role_description":"text entry area","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enter a prompt for Gemini","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"encrypted","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open upload file menu","depth":20,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tools","depth":18,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-4518355817301295456
|
8318692740938204582
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools...
|
50909
|
NULL
|
NULL
|
NULL
|
|
50912
|
1798
|
13
|
2026-05-18T08:09:02.090061+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091742090_m2.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Send message
Your Jiminny chats aren’t used to improve our models. Gemini is AI. It can make mistakes, so double check it.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to content
Skip to content
Open menu
Homepage (g then d)
jiminny
jiminny
app
app
Search or jump to…
Type
/
to search
Chat with Copilot
Open Copilot…
Create new...
All issues(g then i)
All pull requests
All repositories
You have unread notifications(g then n)
Open user navigation menu
Repository navigation
Repository navigation
Code
Code
Pull requests (29)
Pull requests...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.15658244,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.064494684,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.18068483,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.09158909,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.042719416,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.07679521,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.08543883,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.21791889,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.4445331,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.4557063,"width":0.04537899,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"bounds":{"left":0.0,"top":0.4772546,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.4884278,"width":0.15259309,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"bounds":{"left":0.0,"top":0.509976,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.5211492,"width":0.07413564,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.54269755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.55387074,"width":0.090259306,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"bounds":{"left":0.06732048,"top":0.54988027,"width":0.007978723,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"bounds":{"left":0.0028257978,"top":0.57701516,"width":0.07413564,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"bounds":{"left":0.0028257978,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"bounds":{"left":0.013796543,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"bounds":{"left":0.024933511,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"bounds":{"left":0.036070477,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"bounds":{"left":0.04720745,"top":0.97007185,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"bounds":{"left":0.35854387,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"bounds":{"left":0.37051198,"top":0.055067837,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"bounds":{"left":0.3678524,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"bounds":{"left":0.08494016,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"bounds":{"left":0.09823803,"top":0.10454908,"width":0.028590426,"height":0.030327214},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"bounds":{"left":0.1008976,"top":0.10973663,"width":0.021941489,"height":0.020351157},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"bounds":{"left":0.33992687,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"bounds":{"left":0.35322472,"top":0.103751,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"bounds":{"left":0.079288565,"top":0.14764565,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"bounds":{"left":0.079288565,"top":0.15003991,"width":0.1200133,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"bounds":{"left":0.12599733,"top":0.0,"width":0.050199468,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"bounds":{"left":0.17819148,"top":0.0,"width":0.0028257978,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"bounds":{"left":0.18301196,"top":0.0,"width":0.055851065,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"bounds":{"left":0.2408577,"top":0.0,"width":0.05086436,"height":0.016360734},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"bounds":{"left":0.29371676,"top":0.0,"width":0.022273935,"height":0.014764565},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"bounds":{"left":0.12400266,"top":0.0,"width":0.21974733,"height":0.038707104},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"bounds":{"left":0.1100399,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"bounds":{"left":0.120678194,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"bounds":{"left":0.13131648,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"bounds":{"left":0.1419548,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"bounds":{"left":0.15259309,"top":0.0,"width":0.010638298,"height":0.025538707},"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"bounds":{"left":0.2711104,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"bounds":{"left":0.28573802,"top":0.029130088,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"bounds":{"left":0.30568483,"top":0.035514764,"width":0.05036569,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"bounds":{"left":0.079288565,"top":0.03631285,"width":0.019946808,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"bounds":{"left":0.30568483,"top":0.037110932,"width":0.05036569,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"bounds":{"left":0.3480718,"top":0.086592175,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"bounds":{"left":0.12666224,"top":0.088986434,"width":0.030917553,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"bounds":{"left":0.1243351,"top":0.13048683,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"bounds":{"left":0.1243351,"top":0.13248204,"width":0.04105718,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"bounds":{"left":0.11336436,"top":0.13447726,"width":0.21991356,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"bounds":{"left":0.11336436,"top":0.17597765,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"bounds":{"left":0.11336436,"top":0.17757383,"width":0.03656915,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"bounds":{"left":0.15192819,"top":0.17757383,"width":0.025598405,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"bounds":{"left":0.17952128,"top":0.17757383,"width":0.036070477,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"bounds":{"left":0.14960106,"top":0.2047087,"width":0.041223403,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"bounds":{"left":0.19281915,"top":0.20590582,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"bounds":{"left":0.12599733,"top":0.2047087,"width":0.23254654,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"bounds":{"left":0.2059508,"top":0.22825219,"width":0.041888297,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"bounds":{"left":0.24983378,"top":0.22705507,"width":0.04055851,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"bounds":{"left":0.29238698,"top":0.22825219,"width":0.019448139,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.31382978,"top":0.22705507,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"bounds":{"left":0.12599733,"top":0.25818038,"width":0.22955452,"height":0.061053474},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"bounds":{"left":0.13730054,"top":0.30407023,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"bounds":{"left":0.17004654,"top":0.3028731,"width":0.00880984,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.18085106,"top":0.30407023,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.1939827,"top":0.3028731,"width":0.0014960107,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"bounds":{"left":0.12599733,"top":0.3339984,"width":0.020279255,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"bounds":{"left":0.1462766,"top":0.3339984,"width":0.10854388,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"bounds":{"left":0.13131648,"top":0.37789306,"width":0.009142287,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.32945478,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.34275267,"top":0.36911413,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0056515955,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"bounds":{"left":0.13696809,"top":0.4197925,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"bounds":{"left":0.14527926,"top":0.4197925,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"bounds":{"left":0.16206782,"top":0.4197925,"width":0.027925532,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"bounds":{"left":0.13131648,"top":0.4197925,"width":0.0809508,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"bounds":{"left":0.14245346,"top":0.4365523,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"bounds":{"left":0.15924202,"top":0.4365523,"width":0.00831117,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"bounds":{"left":0.16755319,"top":0.4365523,"width":0.06981383,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"bounds":{"left":0.23736702,"top":0.4365523,"width":0.08111702,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"bounds":{"left":0.13131648,"top":0.4365523,"width":0.19265293,"height":0.03152434},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"bounds":{"left":0.11336436,"top":0.5039904,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"bounds":{"left":0.11336436,"top":0.50558656,"width":0.09823803,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.18999335,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"bounds":{"left":0.3053524,"top":0.5339186,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"bounds":{"left":0.11336436,"top":0.53272146,"width":0.24185506,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"bounds":{"left":0.15442154,"top":0.55626494,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"bounds":{"left":0.11336436,"top":0.55506784,"width":0.24451463,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.036402926,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"bounds":{"left":0.16589096,"top":0.6097366,"width":0.055851065,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"bounds":{"left":0.2237367,"top":0.6085395,"width":0.054853722,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"bounds":{"left":0.2805851,"top":0.6097366,"width":0.044714097,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"bounds":{"left":0.12599733,"top":0.6085395,"width":0.23005319,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.04737367,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"bounds":{"left":0.17669548,"top":0.6632083,"width":0.0866024,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"bounds":{"left":0.26529256,"top":0.66201115,"width":0.023936171,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"bounds":{"left":0.2912234,"top":0.6632083,"width":0.036236703,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"bounds":{"left":0.12599733,"top":0.66201115,"width":0.22240691,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.20728059,"top":0.6855547,"width":0.011136968,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"bounds":{"left":0.22041224,"top":0.6843575,"width":0.024601065,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"bounds":{"left":0.24700798,"top":0.6855547,"width":0.030751329,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"bounds":{"left":0.27975398,"top":0.6843575,"width":0.025099734,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.050199468,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"bounds":{"left":0.17619681,"top":0.71548283,"width":0.005984043,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"bounds":{"left":0.18417554,"top":0.71668,"width":0.04737367,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"bounds":{"left":0.12599733,"top":0.71548283,"width":0.22490026,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"bounds":{"left":0.14095744,"top":0.7390263,"width":0.1171875,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"bounds":{"left":0.2601396,"top":0.7378292,"width":0.04089096,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"bounds":{"left":0.30302528,"top":0.7390263,"width":0.013962766,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"bounds":{"left":0.31898272,"top":0.7378292,"width":0.006482713,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"bounds":{"left":0.3274601,"top":0.7390263,"width":0.011303191,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.34075797,"top":0.7378292,"width":0.0013297872,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"bounds":{"left":0.11336436,"top":0.7793296,"width":0.24534574,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"bounds":{"left":0.11336436,"top":0.78092575,"width":0.08809841,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"bounds":{"left":0.12599733,"top":0.80806065,"width":0.023603724,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"When using","depth":26,"bounds":{"left":0.14960106,"top":0.80806065,"width":0.030751329,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects->map(...)->toArray()","depth":27,"bounds":{"left":0.18234707,"top":0.8092578,"width":0.10605053,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", Laravel collections sometimes preserve their underlying associative keys.","depth":26,"bounds":{"left":0.12599733,"top":0.80806065,"width":0.21343085,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"bounds":{"left":0.12599733,"top":0.86153233,"width":0.031083776,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Instead of generating a clean JSON array (e.g.,","depth":26,"bounds":{"left":0.15708111,"top":0.86153233,"width":0.115359046,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"[\"en_GB\", \"bg_BG\"]","depth":27,"bounds":{"left":0.27443483,"top":0.86272943,"width":0.05036569,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"), it might generate a JSON object with numeric string keys (e.g.,","depth":26,"bounds":{"left":0.12599733,"top":0.86153233,"width":0.22490026,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"{\"0\": \"en_GB\", \"1\": \"bg_BG\"}","depth":27,"bounds":{"left":0.2601396,"top":0.8850758,"width":0.07829122,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"). This would violate your schema's strict definition of","depth":26,"bounds":{"left":0.12599733,"top":0.8838787,"width":0.23005319,"height":0.038707104},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->items($schema->string())","depth":27,"bounds":{"left":0.24152261,"top":0.9074222,"width":0.07247341,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"bounds":{"left":0.3159907,"top":0.9062251,"width":0.0014960107,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"bounds":{"left":0.12599733,"top":0.93735033,"width":0.020279255,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Append","depth":26,"bounds":{"left":0.1462766,"top":0.93735033,"width":0.021609042,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->values()","depth":27,"bounds":{"left":0.16988032,"top":0.9385475,"width":0.027925532,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"before","depth":26,"bounds":{"left":0.19980054,"top":0.93735033,"width":0.01861702,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->toArray()","depth":27,"bounds":{"left":0.22041224,"top":0.9385475,"width":0.030585106,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to guarantee a sequentially indexed array:","depth":26,"bounds":{"left":0.25299203,"top":0.93735033,"width":0.10255984,"height":0.016360734},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"bounds":{"left":0.13131648,"top":0.98124504,"width":0.009142287,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"bounds":{"left":0.32945478,"top":0.9724661,"width":0.013297873,"height":0.027533889},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"bounds":{"left":0.34275267,"top":0.9724661,"width":0.013297873,"height":0.027533889},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"$languageDialects","depth":27,"bounds":{"left":0.09923537,"top":1.0,"width":0.047539894,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->map(","depth":27,"bounds":{"left":0.14677526,"top":1.0,"width":0.01662234,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"fn","depth":27,"bounds":{"left":0.16339761,"top":1.0,"width":0.0056515955,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.1690492,"top":1.0,"width":0.0056515955,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$dialect","depth":27,"bounds":{"left":0.1747008,"top":1.0,"width":0.022273935,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") => $","depth":27,"bounds":{"left":0.19697474,"top":1.0,"width":0.016788565,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"dialect","depth":27,"bounds":{"left":0.2137633,"top":1.0,"width":0.019448139,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->","depth":27,"bounds":{"left":0.23321144,"top":1.0,"width":0.0056515955,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getLanguageLocale","depth":27,"bounds":{"left":0.23886304,"top":1.0,"width":0.047539894,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.2864029,"top":1.0,"width":0.0026595744,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"))->","depth":27,"bounds":{"left":0.2890625,"top":1.0,"width":0.011303191,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"values","depth":27,"bounds":{"left":0.3003657,"top":1.0,"width":0.01662234,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.31698802,"top":1.0,"width":0.0028257978,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")->","depth":27,"bounds":{"left":0.31981382,"top":1.0,"width":0.008477394,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"toArray","depth":27,"bounds":{"left":0.32829124,"top":1.0,"width":0.019448139,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(","depth":27,"bounds":{"left":0.34773937,"top":1.0,"width":0.0028257978,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":27,"bounds":{"left":0.35056517,"top":1.0,"width":0.0028257978,"height":-0.023144484},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Are any of these properties (like","depth":24,"bounds":{"left":0.11336436,"top":1.0,"width":0.0787899,"height":-0.07462096},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":25,"bounds":{"left":0.19414894,"top":1.0,"width":0.013962766,"height":-0.07581806},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"or","depth":24,"bounds":{"left":0.21010639,"top":1.0,"width":0.0078125,"height":-0.07462096},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$timezone","depth":25,"bounds":{"left":0.21991356,"top":1.0,"width":0.025099734,"height":-0.07581806},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?","depth":24,"bounds":{"left":0.11336436,"top":1.0,"width":0.24069148,"height":-0.07462096},"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":21,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Redo","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Share & export","depth":20,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":20,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXTextArea","text":"Enter a prompt for Gemini\nencrypted","depth":20,"bounds":{"left":0.11469415,"top":0.83439744,"width":0.23670213,"height":0.01915403},"on_screen":true,"value":"Enter a prompt for Gemini\nencrypted","help_text":"","role_description":"text entry area","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Enter a prompt for Gemini","depth":21,"bounds":{"left":0.12134308,"top":0.8347965,"width":0.069980055,"height":0.018355945},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"encrypted","depth":21,"bounds":{"left":0.113696806,"top":0.83439744,"width":0.0066489363,"height":0.01915403},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open upload file menu","depth":20,"bounds":{"left":0.11070479,"top":0.87031126,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tools","depth":18,"bounds":{"left":0.12666224,"top":0.87031126,"width":0.013297873,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open mode picker","depth":20,"bounds":{"left":0.31399602,"top":0.867917,"width":0.026097074,"height":0.031923383},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pro","depth":23,"bounds":{"left":0.31931517,"top":0.87669593,"width":0.007480053,"height":0.014764565},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Microphone","depth":19,"bounds":{"left":0.34208778,"top":0.867917,"width":0.013297873,"height":0.031923383},"on_screen":true,"role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Send message","depth":19,"bounds":{"left":0.34840426,"top":0.8671189,"width":0.013962766,"height":0.033519555},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":false,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your Jiminny chats aren’t used to improve our models. Gemini is AI. It can make mistakes, so double check it.","depth":17,"bounds":{"left":0.11303192,"top":0.92178774,"width":0.19996676,"height":0.012370312},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Your privacy & Gemini Opens in a new window","depth":17,"bounds":{"left":0.31299868,"top":0.92178774,"width":0.040059842,"height":0.012370312},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Your privacy & Gemini","depth":18,"bounds":{"left":0.31299868,"top":0.92178774,"width":0.040059842,"height":0.012370312},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Opens in a new window","depth":19,"bounds":{"left":0.079288565,"top":0.92098963,"width":0.043218084,"height":0.012370312},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Summarize page","depth":7,"bounds":{"left":0.08494016,"top":0.95730245,"width":0.053523935,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Summarize page","depth":9,"bounds":{"left":0.09059176,"top":0.96249,"width":0.042220745,"height":0.015163607},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Skip to content","depth":7,"bounds":{"left":0.38929522,"top":0.0518755,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Skip to content","depth":8,"bounds":{"left":0.38929522,"top":0.05347167,"width":0.0029920214,"height":0.21468475},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Open menu","depth":11,"bounds":{"left":0.39461437,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"Homepage (g then d)","depth":10,"bounds":{"left":0.40924203,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"jiminny","depth":13,"bounds":{"left":0.4225399,"top":0.06464485,"width":0.018949468,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"jiminny","depth":15,"bounds":{"left":0.4245346,"top":0.07063048,"width":0.014960106,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"app","depth":13,"bounds":{"left":0.44647607,"top":0.06464485,"width":0.017785905,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"app","depth":15,"bounds":{"left":0.44847074,"top":0.07063048,"width":0.008477394,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Search or jump to…","depth":10,"bounds":{"left":0.8171542,"top":0.06464485,"width":0.06565824,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Type","depth":13,"bounds":{"left":0.8294548,"top":0.07063048,"width":0.011801862,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"/","depth":13,"bounds":{"left":0.84258646,"top":0.07222666,"width":0.002493351,"height":0.011572227},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"to search","depth":13,"bounds":{"left":0.8465758,"top":0.07063048,"width":0.021276595,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Chat with Copilot","depth":11,"bounds":{"left":0.88480717,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXMenuButton","text":"Open Copilot…","depth":10,"bounds":{"left":0.89511305,"top":0.06464485,"width":0.008643617,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXMenuButton","text":"Create new...","depth":10,"bounds":{"left":0.91173536,"top":0.06464485,"width":0.01662234,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"menu button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"All issues(g then i)","depth":10,"bounds":{"left":0.9310173,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"All pull requests","depth":10,"bounds":{"left":0.94431514,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"All repositories","depth":10,"bounds":{"left":0.95761305,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXLink","text":"You have unread notifications(g then n)","depth":10,"bounds":{"left":0.9709109,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open user navigation menu","depth":10,"bounds":{"left":0.98420876,"top":0.06464485,"width":0.010638298,"height":0.025538707},"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Repository navigation","depth":10,"bounds":{"left":0.38896278,"top":0.051077414,"width":0.0003324468,"height":0.0007980846},"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Repository navigation","depth":11,"bounds":{"left":0.38896278,"top":0.05387071,"width":0.0787899,"height":0.023144454},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Code","depth":13,"bounds":{"left":0.39461437,"top":0.09936153,"width":0.025099734,"height":0.026336791},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Code","depth":15,"bounds":{"left":0.40525267,"top":0.10574621,"width":0.011801862,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXLink","text":"Pull requests (29)","depth":13,"bounds":{"left":0.42237368,"top":0.09936153,"width":0.05501995,"height":0.026336791},"on_screen":true,"help_text":"","role_description":"link","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests","depth":15,"bounds":{"left":0.43367687,"top":0.10574621,"width":0.027925532,"height":0.013567438},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
-6138396681827013989
|
8895154601217361326
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:
When using
$languageDialects->map(...)->toArray()
, Laravel collections sometimes preserve their underlying associative keys.
The Impact:
Instead of generating a clean JSON array (e.g.,
["en_GB", "bg_BG"]
), it might generate a JSON object with numeric string keys (e.g.,
{"0": "en_GB", "1": "bg_BG"}
). This would violate your schema's strict definition of
->items($schema->string())
.
The Fix:
Append
->values()
before
->toArray()
to guarantee a sequentially indexed array:
PHP
Download code
Copy code
$languageDialects
->map(
fn
(
$dialect
) => $
dialect
->
getLanguageLocale
(
))->
values
(
)->
toArray
(
)
Are any of these properties (like
$team
or
$timezone
) guaranteed to be non-null at the database level, or should we look at adding nullable fallbacks in the output payload?
Good response
Bad response
Redo
Share & export
Copy
Show more options
Enter a prompt for Gemini
encrypted
Enter a prompt for Gemini
encrypted
Open upload file menu
Tools
Open mode picker
Pro
Microphone
Send message
Your Jiminny chats aren’t used to improve our models. Gemini is AI. It can make mistakes, so double check it.
Your privacy & Gemini Opens in a new window
Your privacy & Gemini
Opens in a new window
Summarize page
Summarize page
Skip to content
Skip to content
Open menu
Homepage (g then d)
jiminny
jiminny
app
app
Search or jump to…
Type
/
to search
Chat with Copilot
Open Copilot…
Create new...
All issues(g then i)
All pull requests
All repositories
You have unread notifications(g then n)
Open user navigation menu
Repository navigation
Repository navigation
Code
Code
Pull requests (29)
Pull requests...
|
50910
|
NULL
|
NULL
|
NULL
|
|
50913
|
1797
|
13
|
2026-05-18T08:09:03.540496+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091743540_m1.jpg...
|
Firefox
|
Comparing master...pipedrive-sdk-poc · jiminny/app Comparing master...pipedrive-sdk-poc · jiminny/app — Work...
|
1
|
github.com/jiminny/app/compare/pipedrive-sdk-poc?e github.com/jiminny/app/compare/pipedrive-sdk-poc?expand=1...
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20891] Sidekick SMS issue - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20891] Sidekick SMS issue - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6853] Moxso - Potential deal stages bug - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Feed — jiminny — Sentry","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Feed — jiminny — Sentry","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipedrive API Reference and Documentation","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipedrive API Reference and Documentation","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"pipedrive/client-php: Pipedrive API client for PHP","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - lukas.kovalik@jiminny.com - Jiminny Mail","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pull requests · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pull requests · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[JY-20906] Review of Pipedrive SDK - Jira","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":4,"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":true},{"role":"AXStaticText","text":"Comparing master...pipedrive-sdk-poc · jiminny/app","depth":5,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Close tab","depth":5,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New Tab","depth":4,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Customize sidebar","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Close Google Gemini (⌃X)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Tabs from other devices","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open history (⇧⌘H)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Open bookmarks (⌘B)","depth":6,"on_screen":true,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"AI Chat settings","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":7,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"WORK, Google Account: lukas.kovalik@jiminny.com","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Main menu","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"New chat","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"New Chat","depth":12,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Open menu for conversation actions.","depth":12,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXHeading","text":"Conversation with Gemini","depth":15,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Conversation with Gemini","depth":16,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":21,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \\'John Doe\\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\\'s timezone as an IANA timezone identifier, e.g. \\'America/New_York\\'. Use this when displaying '55+ . 'or reasoning about call times in the user\\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\\'s calls, as an array of locale codes, e.g. [\\'en_GB\\', \\'bg_BG\\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \\'Account Executives\\', \\'Customer Success\\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \\'Sales\\', \\'Customer Success\\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\\'s job title, e.g. \\'Software Engineer\\', \\'Account Executive\\', \\'Sales Manager\\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\\'admin\\', \\'recorder_and_voice\\']. Determines what the user can do in '115+ . 'the platform. Common values: \\'admin\\' (full organisation admin), \\'manager\\' (team manager with '116+ . 'coaching access), \\'recorder_and_voice\\' (can record meetings and make calls), \\'recorder\\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","depth":23,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Expand","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Listen","depth":22,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Code Summary: MCP \"Get User Details\" Tool Implementation","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Schema Definition","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Schema Definition","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User Identity:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"*","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": UUID used as the authoritative identifier for filtering calls or deals.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Full display name (e.g., 'John Doe').","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"first_name","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Extracted first name, explicitly intended for personalized greetings.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Primary email address.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"job","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Job title (e.g., 'Account Executive'); nullable.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Preferences & Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"timezone","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": IANA identifier (e.g., 'America/New_York') for reasoning about local call times.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"spoken_languages","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": An array of locale codes. The first entry serves as the primary/default language.","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"roles","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":": Platform permissions determining capabilities (e.g.,","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"admin","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"manager","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"recorder_and_voice","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":").","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Sync Settings:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Booleans indicating whether the user has automated CRM syncing enabled for:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_dialer","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Jiminny softphone calls)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_email","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Customer email conversations)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"sync_calendar","depth":29,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(Scheduled meetings)","depth":28,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Team Context:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"An object containing the team's","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"id","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"name","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", and","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"playbook","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(the active AI scoring criteria; nullable).","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Execution Logic (handle)","depth":23,"on_screen":false,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Execution Logic (","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"handle","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":")","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The request handler extracts data from the authenticated","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":25,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"model and transforms it into the structured schema format:","depth":24,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Authentication Guard:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Assures via an assertion that an authenticated","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instance is present before running.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'en'","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"as a last resort.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Payload Output:","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Maps the internal Laravel model getter methods (e.g.,","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getUuid()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"shouldSyncDialer()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":",","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getDefaultPlaybook()","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") into the predefined","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$payload","depth":27,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"array and returns a structured JSON-like response.","depth":26,"on_screen":false,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXCheckBox","text":"Good response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXCheckBox","text":"Bad response","depth":22,"on_screen":false,"help_text":"","role_description":"toggle button","subrole":"AXToggle","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Share & export","depth":21,"on_screen":false,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy","depth":22,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Show more options","depth":21,"on_screen":false,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy prompt","depth":21,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Edit","depth":21,"on_screen":true,"role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXHeading","text":"You said any potential issues?","depth":21,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"You said","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"any potential issues?","depth":23,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Listen","depth":22,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Show thinking","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"Gemini said","depth":20,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Gemini said","depth":21,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Here is an assessment of the potential issues and edge cases in the provided code snippet:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"1. Reliance on assert() in Production","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"1. Reliance on","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert()","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in Production","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP assertions (","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assert(...)","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":") can be completely bypassed or ignored in production environments depending on the","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"zend.assertions","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"configuration in","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"php.ini","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Impact:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"on","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Fix:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Use a hard exception or early return instead:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"PHP","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXButton","text":"Download code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXButton","text":"Copy code","depth":27,"on_screen":true,"help_text":"","role_description":"button","subrole":"AXUnknown","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"if","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"(!","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"instanceof","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"User) {","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"throw","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"new","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"AuthenticationException(","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"'User must be authenticated.'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":");\n}","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"2. Potential Null Reference Exceptions","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"2. Potential Null Reference Exceptions","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The code assumes certain relationships and properties will always exist on the","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"object. If any of these can be","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":25,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"in your database schema, the tool will crash with a \"Call to a member function on null\" error:","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Team:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team = $user->team;","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is directly followed by","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team->getUuid()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":". If a user is not currently assigned to a team, this will trigger a fatal error.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Missing Timezone:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$user->getTimezone()->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"assumes","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"getTimezone()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"always returns an object. If it can return","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":", chaining","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"->getName()","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will crash.","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"Language Fallback:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"If","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$languageDialects","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is empty and the user lacks a team, the fallback logic","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"... ?? $team->getDefaultLanguage() ?? 'en'","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"will fail because","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"$team","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"is","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"null","depth":27,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":".","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXHeading","text":"3. Collection to Array Serialization","depth":23,"on_screen":true,"help_text":"","role_description":"heading","subrole":"AXUnknown"},{"role":"AXStaticText","text":"3. Collection to Array Serialization","depth":24,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXStaticText","text":"The Risk:","depth":26,"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
5210126772981968288
|
-903622665166781018
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
JY-20891 add support for secondary email by LakyLak · Pull Request #12073 · jiminny/app
[JY-20891] Sidekick SMS issue - Jira
[JY-20891] Sidekick SMS issue - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
[JY-20915] Add environment-specific email domains for text relay to prevent duplicate processing - Jira
Usage | Windsurf
Usage | Windsurf
[SRD-6853] Moxso - Potential deal stages bug - Jira
[SRD-6853] Moxso - Potential deal stages bug - Jira
Pipelines - jiminny/app
Pipelines - jiminny/app
Feed — jiminny — Sentry
Feed — jiminny — Sentry
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Pipedrive API Reference and Documentation
Pipedrive API Reference and Documentation
pipedrive/client-php: Pipedrive API client for PHP
pipedrive/client-php: Pipedrive API client for PHP
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
[jiminny/infrastructure] JY-20623 Add SQS queue for panorama reports (PR #728) - [EMAIL] - Jiminny Mail
Pull requests · jiminny/app
Pull requests · jiminny/app
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20912] Fallback mechanism for users with active SF tokens for CRM Matching - Jira
[JY-20906] Review of Pipedrive SDK - Jira
[JY-20906] Review of Pipedrive SDK - Jira
Comparing master...pipedrive-sdk-poc · jiminny/app
Comparing master...pipedrive-sdk-poc · jiminny/app
Close tab
New Tab
Customize sidebar
Close Google Gemini (⌃X)
Tabs from other devices
Open history (⇧⌘H)
Open bookmarks (⌘B)
AI Chat settings
Close
WORK, Google Account: [EMAIL]
Main menu
New chat
Gemini
New Chat
Open menu for conversation actions.
Conversation with Gemini
Conversation with Gemini
Copy prompt
You said I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected. Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
You said
I’m on page “<tabTitle>Jy 20846 mcp enable the ai to know details about t</tabTitle>” with “<selection>@@ -0,0 +1,157 @@1+ $schema->string()35+ ->description(36+'UUID of the authenticated user. Use this as the authoritative identifier when filtering calls or deals by '37+ . 'the current user.'38+ )39+ ->required(),40+'name' => $schema->string()41+ ->description('Full display name of the user, e.g. \'John Doe\'.')42+ ->required(),43+'first_name' => $schema->string()44+ ->description(45+'First name of the user, derived from the full name field. Use for personalised greetings or addressing '46+ . 'the user directly.'47+ )48+ ->required(),49+'email' => $schema->string()50+ ->description('Primary email address of the user.')51+ ->required(),52+'timezone' => $schema->string()53+ ->description(54+'The user\'s timezone as an IANA timezone identifier, e.g. \'America/New_York\'. Use this when displaying '55+ . 'or reasoning about call times in the user\'s local time.'56+ )57+ ->required(),58+'spoken_languages' => $schema->array()59+ ->items($schema->string())60+ ->description(61+'Languages spoken during this user\'s calls, as an array of locale codes, e.g. [\'en_GB\', \'bg_BG\']. '62+ . 'The first entry is the primary/default language — used when language detection is unavailable. '63+ . 'Additional entries are other languages the user handles.'64+ )65+ ->required(),66+'sync_dialer' => $schema->boolean()67+ ->description(68+'True if the user has automatic syncing of softphone and outbound calls to their CRM enabled. When true, '69+ . 'calls made through the Jiminny dialler are automatically logged as CRM activities.'70+ )71+ ->required(),72+'sync_email' => $schema->boolean()73+ ->description(74+'True if the user has email syncing enabled. When true, customer email conversations are captured as '75+ . 'activities.'76+ )77+ ->required(),78+'sync_calendar' => $schema->boolean()79+ ->description(80+'True if the user has calendar syncing enabled. When true, scheduled meetings will be automatically '81+ . 'recorded.'82+ )83+ ->required(),84+'team' => $schema->object([85+'id' => $schema->string()86+ ->description(87+'Team UUID. Use this when filtering calls or deals by the user\'s team in search_calls or '88+ . 'search_deals.'89+ )90+ ->required(),91+'name' => $schema->string()92+ ->description('Team name, e.g. \'Account Executives\', \'Customer Success\'.')93+ ->required(),94+'playbook' => $schema->string()95+ ->description(96+'Name of the active playbook assigned to this team, e.g. \'Sales\', \'Customer Success\'. A playbook '97+ . 'defines the call evaluation criteria used for AI scoring. Null if no playbook is assigned.'98+ )99+ ->nullable()100+ ->required(),101+ ])102+ ->description('The team this user belongs to.')103+ ->required(),104+'job' => $schema->string()105+ ->description(106+'The user\'s job title, e.g. \'Software Engineer\', \'Account Executive\', \'Sales Manager\'. Null if no '107+ . 'job title has been assigned.'108+ )109+ ->nullable()110+ ->required(),111+'roles' => $schema->array()112+ ->items($schema->string())113+ ->description(114+'Roles assigned to the user, e.g. [\'admin\', \'recorder_and_voice\']. Determines what the user can do in '115+ . 'the platform. Common values: \'admin\' (full organisation admin), \'manager\' (team manager with '116+ . 'coaching access), \'recorder_and_voice\' (can record meetings and make calls), \'recorder\' (recording only).'117+ )118+ ->required(),119+ ];120+ }121+122+public function handle(Request $request): ResponseFactory123+ {124+$user = $request->user();125+assert($user instanceof User, 'auth:api + McpTierMiddleware must guarantee an authenticated User before the tool runs');126+127+$team = $user->team;128+$roles = $user->getRoles();129+$jobTitle = $user->job;130+131+$languageDialects = $user->languageDialects()->with('language')->get();132+$spokenLanguages = $languageDialects->isNotEmpty()133+ ? $languageDialects->map(fn ($dialect) => $dialect->getLanguageLocale())->toArray()134+ : [$user->getLanguage() ?? $team->getDefaultLanguage() ?? 'en'];135+136+$payload = [137+'id' => $user->getUuid(),138+'name' => $user->getName(),139+'first_name' => $user->getFirstName(),140+'email' => $user->getEmailAddress(),141+'timezone' => $user->getTimezone()->getName(),142+'spoken_languages' => $spokenLanguages,143+'sync_dialer' => $user->shouldSyncDialer(),144+'sync_email' => $user->isSyncEmailEnabled(),145+'sync_calendar' => $user->isSyncConferenceEnabled(),146+'team' => [147+'id' => $team->getUuid(),148+'name' => $team->getName(),149+'playbook' => $team->getDefaultPlaybook()?->getName(),150+ ],151+'job' => $jobTitle?->getName(),152+'roles' => $roles,153+ ];154+155+return Response::structured($payload);156+ }157+}</selection>” selected.
Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
Expand
Listen
Gemini said
Gemini said
Code Summary: MCP "Get User Details" Tool Implementation
Code Summary: MCP "Get User Details" Tool Implementation
This code snippet defines the schema and execution logic for a Model Context Protocol (MCP) tool that exposes the authenticated user's profile details to an AI assistant.
Schema Definition
Schema Definition
The tool outputs a structured object containing the following required fields to guide the AI's contextual understanding and filtering capabilities:
User Identity:
*
id
: UUID used as the authoritative identifier for filtering calls or deals.
name
: Full display name (e.g., 'John Doe').
first_name
: Extracted first name, explicitly intended for personalized greetings.
email
: Primary email address.
job
: Job title (e.g., 'Account Executive'); nullable.
Preferences & Context:
timezone
: IANA identifier (e.g., 'America/New_York') for reasoning about local call times.
spoken_languages
: An array of locale codes. The first entry serves as the primary/default language.
roles
: Platform permissions determining capabilities (e.g.,
admin
,
manager
,
recorder_and_voice
).
Sync Settings:
Booleans indicating whether the user has automated CRM syncing enabled for:
sync_dialer
(Jiminny softphone calls)
sync_email
(Customer email conversations)
sync_calendar
(Scheduled meetings)
Team Context:
An object containing the team's
id
,
name
, and
playbook
(the active AI scoring criteria; nullable).
Execution Logic (handle)
Execution Logic (
handle
)
The request handler extracts data from the authenticated
User
model and transforms it into the structured schema format:
Authentication Guard:
Assures via an assertion that an authenticated
User
instance is present before running.
Language Fallback:
Collects the user's configured language dialects. If none are explicitly set, it falls back to the user's default language, the team's default language, or
'en'
as a last resort.
Payload Output:
Maps the internal Laravel model getter methods (e.g.,
getUuid()
,
shouldSyncDialer()
,
getDefaultPlaybook()
) into the predefined
$payload
array and returns a structured JSON-like response.
Good response
Bad response
Share & export
Copy
Show more options
Copy prompt
Edit
You said any potential issues?
You said
any potential issues?
Listen
Show thinking
Gemini said
Gemini said
Here is an assessment of the potential issues and edge cases in the provided code snippet:
1. Reliance on assert() in Production
1. Reliance on
assert()
in Production
The Risk:
PHP assertions (
assert(...)
) can be completely bypassed or ignored in production environments depending on the
zend.assertions
configuration in
php.ini
.
The Impact:
If the middleware fails to guarantee an authenticated user and assertions are disabled, the code will proceed. It will then throw a fatal error when attempting to call methods like
$user->team
on
null
.
The Fix:
Use a hard exception or early return instead:
PHP
Download code
Copy code
if
(!
$user
instanceof
User) {
throw
new
AuthenticationException(
'User must be authenticated.'
);
}
2. Potential Null Reference Exceptions
2. Potential Null Reference Exceptions
The code assumes certain relationships and properties will always exist on the
$user
object. If any of these can be
null
in your database schema, the tool will crash with a "Call to a member function on null" error:
Missing Team:
$team = $user->team;
is directly followed by
$team->getUuid()
. If a user is not currently assigned to a team, this will trigger a fatal error.
Missing Timezone:
$user->getTimezone()->getName()
assumes
getTimezone()
always returns an object. If it can return
null
, chaining
->getName()
will crash.
Language Fallback:
If
$languageDialects
is empty and the user lacks a team, the fallback logic
... ?? $team->getDefaultLanguage() ?? 'en'
will fail because
$team
is
null
.
3. Collection to Array Serialization
3. Collection to Array Serialization
The Risk:...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50915
|
1798
|
14
|
2026-05-18T08:09:05.125097+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091745125_m2.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.jsroledey© WhichWorkerlsWorkingOnWhichJob.phpCConvertLeadActivities.ongPurgelookupcache.onp© SyncToPlanhat.php> D SchedulingC kernel.one› D ContractsW Domairv DDTO0 Activity— customerApi0 ExternalServicelntegrations) ImportcalliInvitationC) Userinvitationdto.phoRolesaSCIM117 Scorecard1 ServiceC UsersT UserEmail.phpM Smails→ M SnumeD EventsM Activitiec>@ ActivityProviderTM AiAutomationD AudicTm Rote>D Coachingconterencesm connectionsLocal Changes• Chandes Z tlles= env.local apophp loaaina.ono confialUnversioned Files 9 filesE.env.nikilocal appc createrlaybookcreatedevent.ong©) AcuivityLeadConverted.pnp©) salestorce/service.phppnp api.ono x" TeamController.phpc) IntearationApp/Service.php(C) CreateCommentedEvent.phpC) CreateSmsSentEvent.phoC) PlanhatactivityListener.php(C)AskAnythinaPromptService.php(C) AutomatedReportsRepository.ohp(C)AutomatedReportsCommand.ohvphp api_y2.ohdC) RequestGenerateReport.Job.ohvC) AutomatedReportResult.oho(C) AutomatedRenort.ohoV EditTeamModal.vueCreateTeamRequest.phpC) UserinvitationDTO.ongorganizaSrouter->aroundf'middleware'= 'auth:aoi' staticiSrouter->arouodf 'orefix'42 A5 X3 X16 ^'/settings'], static function (Router $router): vSrouter->aroundi'orefix'ations'l, static function Router S~333->post( uri:"/', [OrganizationController::class,Istorp!1)lzations.store');$router-›group(['prefix' => '{team}', 'middleware' => ['teamMember' ]]369H):// Integrations$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGR•/Srouter-›get ( uri: "/integrations'. [IntegrationController::class, 'all']) /apil374>mldd Leware miadleware:permission:. PermissionEnum••PFAN TNTECPATTONÇ 375Log xA007c5180 confialloaaina.nhnl'driver' =>'level' => env('LOG _LEVEL', 'info'),= env.other apo(@ CanAccessAiRenortsTest nhn tests/Unit/Policies@ CreateMockAsk liminnvReportRecultCommand.oho.app/Console/Commands/R'path' => storage_path('logs/Laravel.log').o favicon.ico nublidE ids.txt appTraw sal querv sal ann.(C) SimulateWehhooksCommand.nhnann/Console/Commands/Crm/HubsnotlM+ WEBHOOK_FILTERING_IMPLEMENTATION.md app=custom.log4 SF [jiminny@localhost]& console (STAGINGIA HS_local [jiminny@localhost]* console (PROD1fii users (PROD](C) TestPinedriveOfficialSdkcommand.onp© Kernel.php x M+ PIPEDRIVE_V2_MIGRATION_PLAN.mdM+ PIPEDRIVE_VZ_MIGRATION_TICKEIS.mQ# console EUclass Kernel extends consolekernelRestoreTracksCommand::class355nuospocweonookservicecommand..cudssgrrocesshergeduogecrscommand..cudssnuosporsournatroccrngcommand..cuass,secupJournaLuealwednooksuscr1pc1onscommano..class,LiscJournaLwebnooksudscr1pc1onscommand::class,RemoveGhostParticipantsCommand::classAUCOdeCeCCALACCIVicyl ypecommand:: classcommanos urm marchupporcunirvacrivirlescommand:.class.Cleandup.ncateFrelddatacommand::class.commandserm.TestPinedrivelfficialSdkcommand:class.Accent Rerectnrivate Schedule Sschedule:ouSaleesprivate string Soutput;Accept File &-X Reject File 0x@protected function schedule(Schedule $schedule): void{...}+ 1of Afiles →Migrating to Pipedrive"Preparation tor kerl... In sh o1m100% 1Mon 18 May 11:09:04+O •Current vercionAsk anvthina (*4L)SWE.1G'driver' => 'errorlog''level' => env('LOG LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single'.'path' => storage_path('logs/custom.log')'level' => env('LOG LEVEL', 'info').Reject allAccept all1ditterence# 6 files committedpipedrive offical SDK v2 POCEdit Commit Messaae.Phenad...
|
NULL
|
1146345408816007458
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.jsroledey© WhichWorkerlsWorkingOnWhichJob.phpCConvertLeadActivities.ongPurgelookupcache.onp© SyncToPlanhat.php> D SchedulingC kernel.one› D ContractsW Domairv DDTO0 Activity— customerApi0 ExternalServicelntegrations) ImportcalliInvitationC) Userinvitationdto.phoRolesaSCIM117 Scorecard1 ServiceC UsersT UserEmail.phpM Smails→ M SnumeD EventsM Activitiec>@ ActivityProviderTM AiAutomationD AudicTm Rote>D Coachingconterencesm connectionsLocal Changes• Chandes Z tlles= env.local apophp loaaina.ono confialUnversioned Files 9 filesE.env.nikilocal appc createrlaybookcreatedevent.ong©) AcuivityLeadConverted.pnp©) salestorce/service.phppnp api.ono x" TeamController.phpc) IntearationApp/Service.php(C) CreateCommentedEvent.phpC) CreateSmsSentEvent.phoC) PlanhatactivityListener.php(C)AskAnythinaPromptService.php(C) AutomatedReportsRepository.ohp(C)AutomatedReportsCommand.ohvphp api_y2.ohdC) RequestGenerateReport.Job.ohvC) AutomatedReportResult.oho(C) AutomatedRenort.ohoV EditTeamModal.vueCreateTeamRequest.phpC) UserinvitationDTO.ongorganizaSrouter->aroundf'middleware'= 'auth:aoi' staticiSrouter->arouodf 'orefix'42 A5 X3 X16 ^'/settings'], static function (Router $router): vSrouter->aroundi'orefix'ations'l, static function Router S~333->post( uri:"/', [OrganizationController::class,Istorp!1)lzations.store');$router-›group(['prefix' => '{team}', 'middleware' => ['teamMember' ]]369H):// Integrations$router->group(['middleware' => 'permission:' . PermissionEnum::MANAGE_INTEGR•/Srouter-›get ( uri: "/integrations'. [IntegrationController::class, 'all']) /apil374>mldd Leware miadleware:permission:. PermissionEnum••PFAN TNTECPATTONÇ 375Log xA007c5180 confialloaaina.nhnl'driver' =>'level' => env('LOG _LEVEL', 'info'),= env.other apo(@ CanAccessAiRenortsTest nhn tests/Unit/Policies@ CreateMockAsk liminnvReportRecultCommand.oho.app/Console/Commands/R'path' => storage_path('logs/Laravel.log').o favicon.ico nublidE ids.txt appTraw sal querv sal ann.(C) SimulateWehhooksCommand.nhnann/Console/Commands/Crm/HubsnotlM+ WEBHOOK_FILTERING_IMPLEMENTATION.md app=custom.log4 SF [jiminny@localhost]& console (STAGINGIA HS_local [jiminny@localhost]* console (PROD1fii users (PROD](C) TestPinedriveOfficialSdkcommand.onp© Kernel.php x M+ PIPEDRIVE_V2_MIGRATION_PLAN.mdM+ PIPEDRIVE_VZ_MIGRATION_TICKEIS.mQ# console EUclass Kernel extends consolekernelRestoreTracksCommand::class355nuospocweonookservicecommand..cudssgrrocesshergeduogecrscommand..cudssnuosporsournatroccrngcommand..cuass,secupJournaLuealwednooksuscr1pc1onscommano..class,LiscJournaLwebnooksudscr1pc1onscommand::class,RemoveGhostParticipantsCommand::classAUCOdeCeCCALACCIVicyl ypecommand:: classcommanos urm marchupporcunirvacrivirlescommand:.class.Cleandup.ncateFrelddatacommand::class.commandserm.TestPinedrivelfficialSdkcommand:class.Accent Rerectnrivate Schedule Sschedule:ouSaleesprivate string Soutput;Accept File &-X Reject File 0x@protected function schedule(Schedule $schedule): void{...}+ 1of Afiles →Migrating to Pipedrive"Preparation tor kerl... In sh o1m100% 1Mon 18 May 11:09:04+O •Current vercionAsk anvthina (*4L)SWE.1G'driver' => 'errorlog''level' => env('LOG LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single'.'path' => storage_path('logs/custom.log')'level' => env('LOG LEVEL', 'info').Reject allAccept all1ditterence# 6 files committedpipedrive offical SDK v2 POCEdit Commit Messaae.Phenad...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
50914
|
1797
|
14
|
2026-05-18T08:09:05.451827+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779091745451_m1.jpg...
|
PhpStorm
|
faVsco.js – Kernel.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpj Preparation for Refi... in 3h 51m100% (78• Mon 18 May 11:09:05DOCKER (docker-compose)О ₴2181DOCKER81DEV (-zsh)X 11DOCKER (docker-compose)'telus''talkdesk'--from='2026-05-18 07:51:00' --to='2026-05-18 08:07:00' > */proc/1/fd/1' 2>&1docker_lamp_1docker_1amp_1docker_lamp_1run_artisan_schedule: Done waiting for schedule:run2026-05-18 08:07:36 Jiminny\Jobs\Mailbox\CreateBatchesRUNNINGdocker_lamp_12026-05-18 08:07:36 Jiminny\Jobs\Mailbox\CreateBatches92.34msDONEdocker_lamp_1docker_lamp_12026-05-18 08:08:08 Running ['artisan'meeting-bot:schedule-bot]2s DONEdocker_lamp_1fd/1'• '/usr/local/bin/php' 'artisan'meeting-bot:schedule-bot › '/proc/1/2>&1docker_1amp_12026-05-18 08:08:11 Running ['artisan' dialers:monitor-activities] .1sDONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' dialers:monitor-activities › '/proc/1/fd/1'2>&1docker_lamp_1 |2026-05-18 08:08:13 Running ['artisan'jiminny:monitor-social-accountS]3S DONEdocker_lamp_11 , '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > '/proc/1/fd/1' 2>&1docker_lamp_112026-05-18 08:08:17 Running ['artisan'mailbox:skip-lists:refresh].4S DONEdocker_1amp_1l '/usr/local/bin/php' 'artisan' mailbox:skip-lists:refresh > '/proc/1/fd/1'2>&1docker_lamp_12026-05-18 08:08:21 Running ['artisan'mailbox:batch:process --max-batches=15]3sDONEdocker_lamp_1l '/usr/local/bin/php' 'artisan' mailbox:batch:process --max-batches=15 > '/proc/1/fd/1' 2>&1docker_lamp_12026-05-18 08:08:24 Running ['artisan' conference:monitor:count] ….2s DONEdocker_lamp_11 l '/usr/local/bin/php' 'artisan'conference:monitor:count > */proc/1/fd/1' 2>&1docker_1amp_12026-05-18 08:08:27 Running ['artisan'mailbox:batch:retry-failed --max-batches=15]in background1.98ms DONEdocker_lamp_11 ('/usr/local/bin/php' 'artisan'mailbox:batch:retry-failed --max-batches=15 ›'/proc/1/fd/1' 2>&1; '/usr/local/bin/php''artisan'schedule: finish "framework/schedule-390defd641effbabf73a895e426ded4cf2ba7f11" "$?") > '/dev/null' 2>&1 &docker_lamp_1docker_lamp_1I run_artisan_schedule: Done waiting for schedule:rundocker_lamp_1APP (-zsh)*3screenpipe"Y2PROD (ssh)Run'do-release-upgrade'to upgrade to it.*** System restart required ***Last login: Thu May 14 07:41:362026 from 212.5.153.87lukas@jiminny-prod-bastion:~$X L3 EU (-zsh)Last login: Sat May 16 18:04:33on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ I|T4STAGE (ssh)See [URL_WITH_CREDENTIALS] ~ $ IX W7 ExT(-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsEXTENSIONPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|V View in Docker Desktop@ View ConfigEnable Watch...
|
NULL
|
-8016359048189001802
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelpj Preparation for Refi... in 3h 51m100% (78• Mon 18 May 11:09:05DOCKER (docker-compose)О ₴2181DOCKER81DEV (-zsh)X 11DOCKER (docker-compose)'telus''talkdesk'--from='2026-05-18 07:51:00' --to='2026-05-18 08:07:00' > */proc/1/fd/1' 2>&1docker_lamp_1docker_1amp_1docker_lamp_1run_artisan_schedule: Done waiting for schedule:run2026-05-18 08:07:36 Jiminny\Jobs\Mailbox\CreateBatchesRUNNINGdocker_lamp_12026-05-18 08:07:36 Jiminny\Jobs\Mailbox\CreateBatches92.34msDONEdocker_lamp_1docker_lamp_12026-05-18 08:08:08 Running ['artisan'meeting-bot:schedule-bot]2s DONEdocker_lamp_1fd/1'• '/usr/local/bin/php' 'artisan'meeting-bot:schedule-bot › '/proc/1/2>&1docker_1amp_12026-05-18 08:08:11 Running ['artisan' dialers:monitor-activities] .1sDONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' dialers:monitor-activities › '/proc/1/fd/1'2>&1docker_lamp_1 |2026-05-18 08:08:13 Running ['artisan'jiminny:monitor-social-accountS]3S DONEdocker_lamp_11 , '/usr/local/bin/php' 'artisan' jiminny:monitor-social-accounts > '/proc/1/fd/1' 2>&1docker_lamp_112026-05-18 08:08:17 Running ['artisan'mailbox:skip-lists:refresh].4S DONEdocker_1amp_1l '/usr/local/bin/php' 'artisan' mailbox:skip-lists:refresh > '/proc/1/fd/1'2>&1docker_lamp_12026-05-18 08:08:21 Running ['artisan'mailbox:batch:process --max-batches=15]3sDONEdocker_lamp_1l '/usr/local/bin/php' 'artisan' mailbox:batch:process --max-batches=15 > '/proc/1/fd/1' 2>&1docker_lamp_12026-05-18 08:08:24 Running ['artisan' conference:monitor:count] ….2s DONEdocker_lamp_11 l '/usr/local/bin/php' 'artisan'conference:monitor:count > */proc/1/fd/1' 2>&1docker_1amp_12026-05-18 08:08:27 Running ['artisan'mailbox:batch:retry-failed --max-batches=15]in background1.98ms DONEdocker_lamp_11 ('/usr/local/bin/php' 'artisan'mailbox:batch:retry-failed --max-batches=15 ›'/proc/1/fd/1' 2>&1; '/usr/local/bin/php''artisan'schedule: finish "framework/schedule-390defd641effbabf73a895e426ded4cf2ba7f11" "$?") > '/dev/null' 2>&1 &docker_lamp_1docker_lamp_1I run_artisan_schedule: Done waiting for schedule:rundocker_lamp_1APP (-zsh)*3screenpipe"Y2PROD (ssh)Run'do-release-upgrade'to upgrade to it.*** System restart required ***Last login: Thu May 14 07:41:362026 from 212.5.153.87lukas@jiminny-prod-bastion:~$X L3 EU (-zsh)Last login: Sat May 16 18:04:33on ttys001Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parents@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ I|T4STAGE (ssh)See [URL_WITH_CREDENTIALS] ~ $ IX W7 ExT(-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsEXTENSIONPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|V View in Docker Desktop@ View ConfigEnable Watch...
|
NULL
|
NULL
|
NULL
|
NULL
|