@fayz-ai/admin 0.62.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{CommandPalette-B5PMQFDG.js → CommandPalette-XDJJ5E3O.js} +3 -3
- package/dist/{CommandPalette-B5PMQFDG.js.map → CommandPalette-XDJJ5E3O.js.map} +1 -1
- package/dist/ConnectedBrandingSettings-43ZM3HCI.js +680 -0
- package/dist/ConnectedBrandingSettings-43ZM3HCI.js.map +1 -0
- package/dist/ConnectedCompanySettings-I4MALI2Q.js +197 -0
- package/dist/ConnectedCompanySettings-I4MALI2Q.js.map +1 -0
- package/dist/ConnectedModulesSettings-A2VJ5BMJ.js +563 -0
- package/dist/ConnectedModulesSettings-A2VJ5BMJ.js.map +1 -0
- package/dist/ConnectedSecuritySettings-25AHA4HJ.js +189 -0
- package/dist/ConnectedSecuritySettings-25AHA4HJ.js.map +1 -0
- package/dist/ConnectedUserProfile-E7USJRUE.js +424 -0
- package/dist/ConnectedUserProfile-E7USJRUE.js.map +1 -0
- package/dist/FieldRulesEditor-YGIPKI5T.js +129 -0
- package/dist/FieldRulesEditor-YGIPKI5T.js.map +1 -0
- package/dist/GettingStartedWidget-HMIJ3Y24.js +12 -0
- package/dist/GettingStartedWidget-HMIJ3Y24.js.map +1 -0
- package/dist/PermissionProfilesTab-3ZPRCHMZ.js +548 -0
- package/dist/PermissionProfilesTab-3ZPRCHMZ.js.map +1 -0
- package/dist/PersonTimeline-HQZT527S.js +3 -0
- package/dist/{PersonTimeline-X7JTPUWJ.js.map → PersonTimeline-HQZT527S.js.map} +1 -1
- package/dist/ProjectsSettings-7O2F27MD.js +5 -0
- package/dist/ProjectsSettings-7O2F27MD.js.map +1 -0
- package/dist/ScheduleEditor-54ELWR45.js +8 -0
- package/dist/{ScheduleEditor-L2UJXKMY.js.map → ScheduleEditor-54ELWR45.js.map} +1 -1
- package/dist/SeuSiteWidget-QLSUVC63.js +6 -0
- package/dist/SeuSiteWidget-QLSUVC63.js.map +1 -0
- package/dist/SubscriptionPage-5GE5HFJK.js +17 -0
- package/dist/{SubscriptionPage-IDVEFLZ5.js.map → SubscriptionPage-5GE5HFJK.js.map} +1 -1
- package/dist/TeamTab-6RAULFLW.js +502 -0
- package/dist/TeamTab-6RAULFLW.js.map +1 -0
- package/dist/TrialWidget-Z6CLUCU3.js +7 -0
- package/dist/TrialWidget-Z6CLUCU3.js.map +1 -0
- package/dist/app/AdminShell.d.ts.map +1 -1
- package/dist/app/admin-app.d.ts.map +1 -1
- package/dist/app/config.d.ts +14 -0
- package/dist/app/config.d.ts.map +1 -1
- package/dist/app/routing.d.ts +6 -0
- package/dist/app/routing.d.ts.map +1 -1
- package/dist/app/scaffold.d.ts.map +1 -1
- package/dist/billing/index.js +9 -4
- package/dist/chunk-2H46WDG5.js +53 -0
- package/dist/chunk-2H46WDG5.js.map +1 -0
- package/dist/chunk-3HLX7YB2.js +94 -0
- package/dist/chunk-3HLX7YB2.js.map +1 -0
- package/dist/chunk-3OUD4XBK.js +3 -0
- package/dist/chunk-3OUD4XBK.js.map +1 -0
- package/dist/{chunk-IXSY4GPK.js → chunk-5D5QLMOQ.js} +5 -3
- package/dist/chunk-5D5QLMOQ.js.map +1 -0
- package/dist/chunk-5WR62X2R.js +1081 -0
- package/dist/chunk-5WR62X2R.js.map +1 -0
- package/dist/{chunk-KPYRTOCK.js → chunk-6CQFGMEO.js} +4 -140
- package/dist/chunk-6CQFGMEO.js.map +1 -0
- package/dist/chunk-6MMSYC2I.js +116 -0
- package/dist/chunk-6MMSYC2I.js.map +1 -0
- package/dist/chunk-6QFOE3UD.js +1466 -0
- package/dist/chunk-6QFOE3UD.js.map +1 -0
- package/dist/chunk-6UUFVQ2E.js +283 -0
- package/dist/chunk-6UUFVQ2E.js.map +1 -0
- package/dist/chunk-7WNLL3F6.js +7889 -0
- package/dist/chunk-7WNLL3F6.js.map +1 -0
- package/dist/chunk-A74IHAQE.js +439 -0
- package/dist/chunk-A74IHAQE.js.map +1 -0
- package/dist/{chunk-JOBRLBIQ.js → chunk-AFA6IO23.js} +4 -3
- package/dist/chunk-AFA6IO23.js.map +1 -0
- package/dist/{chunk-P5UIGQFY.js → chunk-ALCSIN3Z.js} +5 -5
- package/dist/chunk-ALCSIN3Z.js.map +1 -0
- package/dist/chunk-AWM4AMBS.js +39 -0
- package/dist/chunk-AWM4AMBS.js.map +1 -0
- package/dist/{chunk-OQARLKZN.js → chunk-BG5Z6KLN.js} +6 -18
- package/dist/chunk-BG5Z6KLN.js.map +1 -0
- package/dist/chunk-DPNFZONC.js +19 -0
- package/dist/chunk-DPNFZONC.js.map +1 -0
- package/dist/chunk-DRCV643L.js +193 -0
- package/dist/chunk-DRCV643L.js.map +1 -0
- package/dist/chunk-E6DF3VR6.js +206 -0
- package/dist/chunk-E6DF3VR6.js.map +1 -0
- package/dist/chunk-ENHCVGXZ.js +120 -0
- package/dist/chunk-ENHCVGXZ.js.map +1 -0
- package/dist/chunk-FK6RHGI7.js +56 -0
- package/dist/chunk-FK6RHGI7.js.map +1 -0
- package/dist/chunk-G5HNA5QK.js +233 -0
- package/dist/chunk-G5HNA5QK.js.map +1 -0
- package/dist/chunk-HCFO4Z6A.js +53 -0
- package/dist/chunk-HCFO4Z6A.js.map +1 -0
- package/dist/chunk-JFIU6NQP.js +51 -0
- package/dist/chunk-JFIU6NQP.js.map +1 -0
- package/dist/chunk-JRZJBMOY.js +3 -0
- package/dist/chunk-JRZJBMOY.js.map +1 -0
- package/dist/{chunk-3YP7KW47.js → chunk-JVVOL4BB.js} +41 -512
- package/dist/chunk-JVVOL4BB.js.map +1 -0
- package/dist/chunk-KAQ3UIAA.js +415 -0
- package/dist/chunk-KAQ3UIAA.js.map +1 -0
- package/dist/{chunk-P6XUDT2P.js → chunk-KJELMULA.js} +4 -51
- package/dist/chunk-KJELMULA.js.map +1 -0
- package/dist/chunk-KLO7DKXX.js +1651 -0
- package/dist/chunk-KLO7DKXX.js.map +1 -0
- package/dist/chunk-M2CMDWUI.js +75 -0
- package/dist/chunk-M2CMDWUI.js.map +1 -0
- package/dist/chunk-PQ47MLTH.js +255 -0
- package/dist/chunk-PQ47MLTH.js.map +1 -0
- package/dist/{chunk-VX3WJLKK.js → chunk-QD7BMUDF.js} +17 -8
- package/dist/chunk-QD7BMUDF.js.map +1 -0
- package/dist/{chunk-E4MESMNR.js → chunk-QTSKCPSW.js} +7 -10
- package/dist/chunk-QTSKCPSW.js.map +1 -0
- package/dist/chunk-S56FQKPL.js +186 -0
- package/dist/chunk-S56FQKPL.js.map +1 -0
- package/dist/chunk-SGAFPNMA.js +75 -0
- package/dist/chunk-SGAFPNMA.js.map +1 -0
- package/dist/{chunk-OD7P4L3D.js → chunk-SOKCLVM4.js} +3 -3
- package/dist/{chunk-OD7P4L3D.js.map → chunk-SOKCLVM4.js.map} +1 -1
- package/dist/chunk-TL42NKTH.js +3 -0
- package/dist/chunk-TL42NKTH.js.map +1 -0
- package/dist/chunk-U6ZCO2PS.js +2964 -0
- package/dist/chunk-U6ZCO2PS.js.map +1 -0
- package/dist/chunk-VSXNROYH.js +16 -0
- package/dist/chunk-VSXNROYH.js.map +1 -0
- package/dist/chunk-WPFZCYZL.js +619 -0
- package/dist/chunk-WPFZCYZL.js.map +1 -0
- package/dist/chunk-XFTNIBDW.js +23 -0
- package/dist/chunk-XFTNIBDW.js.map +1 -0
- package/dist/chunk-Y66JDL6I.js +1020 -0
- package/dist/chunk-Y66JDL6I.js.map +1 -0
- package/dist/chunk-YEHIBPIX.js +137 -0
- package/dist/chunk-YEHIBPIX.js.map +1 -0
- package/dist/chunk-YKPPCV2Z.js +53 -0
- package/dist/chunk-YKPPCV2Z.js.map +1 -0
- package/dist/components/chat/markdown.d.ts.map +1 -1
- package/dist/components/plugins/PluginSettingsPanel.d.ts.map +1 -1
- package/dist/components/settings/SettingsPage.d.ts.map +1 -1
- package/dist/connect-XKQJOWR7.js +18 -0
- package/dist/connect-XKQJOWR7.js.map +1 -0
- package/dist/crud/CrudDetailPage.d.ts.map +1 -1
- package/dist/crud/CrudListView.d.ts.map +1 -1
- package/dist/crud/CrudPage.d.ts.map +1 -1
- package/dist/crud/RelatedListTab.d.ts.map +1 -1
- package/dist/crud/archetypes/EntityLink.d.ts +1 -1
- package/dist/crud/archetypes/EntityLink.d.ts.map +1 -1
- package/dist/crud/record-chrome.d.ts.map +1 -1
- package/dist/entity-routes-PGU7EHAD.js +4 -0
- package/dist/{entity-routes-LXOBW5IB.js.map → entity-routes-PGU7EHAD.js.map} +1 -1
- package/dist/hooks/useModuleNavigation.d.ts.map +1 -1
- package/dist/impersonation/ImpersonateDialog.d.ts.map +1 -1
- package/dist/impersonation/session.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11902 -33551
- package/dist/index.js.map +1 -1
- package/dist/lib/entity-routes.d.ts.map +1 -1
- package/dist/org/index.js +4 -2
- package/dist/plugins/PluginRegistryManager.d.ts.map +1 -1
- package/dist/settings/MessageTemplateRow.d.ts.map +1 -1
- package/dist/timeline/PersonTimeline.d.ts.map +1 -1
- package/dist/timeline/TimelineList.d.ts.map +1 -1
- package/dist/units/UnitRailPage.d.ts.map +1 -1
- package/dist/units/useUnitFilter.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/PersonTimeline-X7JTPUWJ.js +0 -3
- package/dist/ScheduleEditor-L2UJXKMY.js +0 -6
- package/dist/SubscriptionPage-IDVEFLZ5.js +0 -8
- package/dist/chunk-3YP7KW47.js.map +0 -1
- package/dist/chunk-E4MESMNR.js.map +0 -1
- package/dist/chunk-IXSY4GPK.js.map +0 -1
- package/dist/chunk-JOBRLBIQ.js.map +0 -1
- package/dist/chunk-KPYRTOCK.js.map +0 -1
- package/dist/chunk-OQARLKZN.js.map +0 -1
- package/dist/chunk-P5UIGQFY.js.map +0 -1
- package/dist/chunk-P6XUDT2P.js.map +0 -1
- package/dist/chunk-VX3WJLKK.js.map +0 -1
- package/dist/entity-routes-LXOBW5IB.js +0 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/report-attachments.ts","../src/app/core-entities.ts","../src/lib/core-ai-tools.ts","../src/lib/tool-search.ts","../src/lib/attachments.ts","../src/lib/agent-approvals.ts","../src/lib/record-refs.ts","../src/lib/agent-threads.ts","../src/lib/assistant-text.ts","../src/stores/chat.store.ts"],"names":["registry","byName","listeners","read","write"],"mappings":"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","file":"chunk-KLO7DKXX.js","sourcesContent":["/**\n * The images a turn carried, held for the tool that files a report.\n *\n * A print the user drops in the chat goes to the model as an inline file (so it\n * can SEE it) — but the support plugin's `reportIssue` also wants to attach that\n * same print to the ticket it opens, or the user's evidence is lost the moment\n * the turn ends. The bytes never belong to the turn payload, so the chat stashes\n * the turn's image/PDF Files here and `reportIssue` takes them after it writes\n * the request. Set per turn, consumed once; a turn that files no report just\n * overwrites them on the next send.\n */\n\nlet turnFiles: File[] = []\n\n/**\n * Called by the chat as a turn is sent: the image/PDF files it is carrying.\n *\n * A turn with NO files does NOT clear the holder — the report flow spans two\n * turns (\"há um bug…\" + the print, then \"sim, pode registrar\"), and `reportIssue`\n * fires on the second, empty one. Clearing there would drop the print. The stash\n * is cleared when it is taken, or when the conversation resets.\n */\nexport function setTurnReportFiles(files: File[]): void {\n if (files.length) turnFiles = files.slice(0, 8)\n}\n\n/** Taken by `reportIssue` after it creates the request. Clears the holder so the\n * same print is never attached to two tickets. */\nexport function takeTurnReportFiles(): File[] {\n const out = turnFiles\n turnFiles = []\n return out\n}\n\n/** Forget any held print — the conversation it belonged to is gone. */\nexport function clearTurnReportFiles(): void {\n turnFiles = []\n}\n","import type { EntityDef } from '@fayz-ai/core'\nimport { taskEntityDef } from '@fayz-ai/tasks/data/supabase'\n\n// ---------------------------------------------------------------------------\n// CORE_QUERY_ENTITIES — spine tables the agent may read and write even though\n// no app declares a CRUD page for them.\n//\n// The data primitives (createRecord / updateRecord / searchRecords / queryData)\n// build their entity enum from three sources: the app's registered CRUD\n// entities, plugin registries, and plugin `queryEntities`. A spine table that\n// lives inside a DETAIL TAB rather than on a page of its own belongs to none of\n// them — so the agent could not see it, and would answer \"esses campos não\n// estão disponíveis\" to a request the UI handles fine.\n//\n// These are declared here, merged in by deriveAgentContract and useAITools, so\n// EVERY app gets them without editing a single app config. They intentionally\n// produce NO route and NO nav item: the surface for an address is the person's\n// Endereços tab, not a top-level page.\n// ---------------------------------------------------------------------------\n\n/** public.addresses — @fayz-ai/db spine migration 017_core_addresses. */\nconst addressEntity: EntityDef = {\n name: 'Address',\n namePlural: 'Addresses',\n icon: 'MapPin',\n data: {\n table: 'addresses',\n tenantScoped: true,\n tenantIdColumn: 'tenant_id',\n searchColumns: ['label', 'street', 'district', 'city', 'postal_code', 'recipient'],\n },\n fields: [\n // owner_id/owner_type are how an address attaches to its person. They are\n // required and described rather than hidden: the agent reaches this table\n // from a record it already has in hand (\"adiciona o endereço X pra ele\"),\n // so the id it must pass is the one it just searched for or created.\n {\n key: 'owner_id',\n label: 'Owner record id',\n type: 'text',\n required: true,\n hint: 'Id of the person (client, contact, supplier, staff…) this address belongs to.',\n },\n {\n key: 'owner_type',\n label: 'Owner kind',\n type: 'select',\n options: ['person', 'location', 'tenant'],\n hint: \"Defaults to 'person'. Only set it for an address that belongs to a business location or to the tenant itself.\",\n },\n { key: 'label', label: 'Label', type: 'text', searchable: true, hint: 'Casa, Trabalho, Depósito…' },\n {\n key: 'kind',\n label: 'Use',\n type: 'select',\n options: ['both', 'shipping', 'billing'],\n hint: \"Defaults to 'both'.\",\n },\n { key: 'postal_code', label: 'Postal code', type: 'text', required: true, searchable: true, hint: 'CEP.' },\n { key: 'street', label: 'Street', type: 'text', required: true, searchable: true },\n { key: 'number', label: 'Number', type: 'text' },\n { key: 'complement', label: 'Complement', type: 'text' },\n { key: 'district', label: 'District', type: 'text', searchable: true, hint: 'Bairro.' },\n { key: 'city', label: 'City', type: 'text', required: true, searchable: true },\n { key: 'state', label: 'State', type: 'text', required: true, hint: 'Two-letter UF.' },\n { key: 'country', label: 'Country', type: 'text', hint: \"Defaults to 'BR'.\" },\n { key: 'recipient', label: 'Recipient', type: 'text', searchable: true, hint: 'Who receives, if not the owner.' },\n { key: 'phone', label: 'Phone', type: 'text' },\n {\n key: 'is_default',\n label: 'Default address',\n type: 'boolean',\n hint: 'Only one default per owner and kind — clear the previous one before setting this.',\n },\n ],\n}\n\nexport interface CoreQueryEntity {\n key: string\n entity: EntityDef\n writable?: boolean\n /**\n * How this entity attaches to another record — appended to the descriptions\n * of the data primitives that can act on it. These entities do NOT stand\n * alone, and nothing in a flat enum of ~37 keys conveys that.\n *\n * Both directions matter, and each fails differently without it. On WRITE the\n * model reaches for `updateRecord` on the parent, is told the fields do not\n * exist, and then gives up or buries the data in a free-text column. On READ\n * it searches the parent, finds no address on it, and confidently answers\n * \"this contact has no address registered\" — a falsehood, since the row is\n * one filtered query away.\n *\n * Stated here rather than inferred at failure time on purpose: guessing the\n * intended entity from rejected FIELD NAMES points \"add an address to this\n * contact\" at the business-locations table, which declares the same columns.\n */\n agentHint?: string\n}\n\nexport const CORE_QUERY_ENTITIES: CoreQueryEntity[] = [\n {\n key: 'address',\n entity: addressEntity,\n writable: true,\n agentHint:\n '`address` holds the street address of a PERSON (client, contact, supplier, staff). A person record has NO street/city/postal_code fields of its own. ' +\n \"To read where someone lives, search `address` with filters {\\\"owner_id\\\": <the person's id>} — a person with no address field is not a person without an address. \" +\n \"To record one, create an `address` with owner_id set to the person's id. Never write an address into a notes field.\",\n },\n {\n key: 'task',\n entity: taskEntityDef,\n writable: true,\n agentHint:\n '`task` is the workspace to-do list shown in the assistant column. Rows with source = \"onboarding\" are the setup checklist and are assigned to whoever created the workspace. ' +\n 'To close one, update its `status` to \"done\" — never delete it. Leave `source` and `source_key` alone when creating a task; they belong to the feature that seeded the row.',\n },\n]\n","import type { PluginAITool, PluginRegistryDef } from '../types/plugins'\nimport type { EntityDef, FeatureDeclaration, PermissionProfile } from '@fayz-ai/core'\nimport { getAllEntities, type RegisteredEntity } from '@fayz-ai/core'\nimport { CORE_QUERY_ENTITIES } from '../app/core-entities'\n\n/**\n * Core SaaS-level AI tools — always available, no plugin required.\n */\nexport const coreAITools: PluginAITool[] = [\n {\n id: 'core.business-summary',\n name: 'getBusinessSummary',\n description: 'Returns a summary of the current business: name, plan, vertical, team size, and key metrics.',\n icon: 'Building2',\n mode: 'read',\n parameters: { type: 'object', properties: {}, required: [] },\n category: 'Core',\n // No opener suggestion: plugin-dashboard's kpi-summary already offers the\n // \"how is the business doing\" prompt (localised). A second one here only\n // duplicated it, in English, on the opening screen.\n suggestions: [],\n },\n {\n id: 'core.team-members',\n name: 'getTeamMembers',\n description: \"Lists the WORKSPACE TEAM exactly as the Team screen shows it: the people who staff this business. Each row carries `kind` (what they do here — teacher, staff…), `accessRole` (their permission role, null when they have no login) and `hasLogin`. Answer headcount questions from this — never report the number of logins as the size of the team.\",\n icon: 'Users',\n mode: 'read',\n parameters: {\n type: 'object',\n properties: {\n role: {\n type: 'string',\n description: 'Optional filter, matched against either the person kind (teacher, staff…) or the access role (owner, admin, manager, viewer).',\n },\n },\n },\n permission: { feature: 'members', action: 'read' },\n category: 'Core',\n suggestions: [\n { label: 'Quem está na minha equipe?' },\n ],\n },\n {\n id: 'core.navigate',\n name: 'navigateTo',\n description:\n 'Opens one of the app\\'s PAGES (Agenda, Clients, Financial…). Takes a page name, never a person or a record — to open a specific record use openRecord.',\n icon: 'Compass',\n mode: 'read',\n parameters: {\n type: 'object',\n properties: {\n page: { type: 'string', description: 'Name of the page, as listed in the app pages' },\n },\n required: ['page'],\n },\n category: 'Core',\n },\n {\n id: 'core.find-connectors',\n name: 'findConnectors',\n description:\n 'Finds the INTEGRATIONS this app can have with outside providers — payments (Pix, cartão, boleto), frete, nota fiscal/ERP, banco, CRM, agenda, mensagens. ' +\n 'Call it WHENEVER someone asks for a provider by name (\"tem AppMax?\", \"queria pagamento com a appmax\") or by need (\"quero receber por Pix\", \"preciso emitir nota fiscal\"). ' +\n 'You have NO other way to know what this app can connect — never answer that you cannot help with a specific provider without calling this first. ' +\n 'Each result carries a `state`, and they mean different things: `connected` (already live), `needs_setup` (authorized, but it CANNOT work until something is chosen on its screen — ' +\n 'never call this one ready or working), `ready` (installed here — the person can connect it right now), ' +\n '`soon` (a module this app has declared it, but it is not built yet — do NOT offer to connect it), and `needs_module` (it belongs to a module this app was not built with — ' +\n 'the person CANNOT install a module, so say plainly that it would come with a new version of the app and offer nothing to click). ' +\n 'The reply already renders a card per result, with the brand mark and the button; answer in one or two short sentences and do not repeat the list, the logos or any link. ' +\n 'You never connect anything yourself — connecting is a human step on the connector screen.',\n icon: 'Plug',\n mode: 'read',\n parameters: {\n type: 'object',\n properties: {\n need: {\n type: 'string',\n description:\n \"What the person needs, in their own words — a provider name (\\\"appmax\\\") or a need (\\\"pagamento\\\", \\\"receber por pix\\\", \\\"frete\\\", \\\"nota fiscal\\\").\",\n },\n },\n required: ['need'],\n },\n category: 'Core',\n suggestions: [{ label: 'Dá pra receber por Pix aqui?' }],\n },\n {\n id: 'core.open-record',\n name: 'openRecord',\n description:\n 'Opens the detail page of a record already returned by a tool in THIS conversation. Takes that record\\'s id — never a name.',\n icon: 'ExternalLink',\n mode: 'read',\n parameters: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Record id from an earlier tool result' },\n },\n required: ['id'],\n },\n category: 'Core',\n },\n]\n\n/**\n * Builds the LLM-facing function name for a registry tool.\n *\n * Derived from the registry id, never from the entity's display label: the\n * label is translated, so a pt-BR app would mint `listCategoriasdeServiço` —\n * which both violates the provider's `^[a-zA-Z0-9_-]+$` function-name rule and\n * renames the tool whenever the user switches locale. The id is a stable ASCII\n * slug. The human plural still drives the description, which is what the model\n * actually reads to decide when to call it.\n */\nfunction pascalCase(value: string): string {\n // Transliterate diacritics BEFORE splitting: splitting on [^a-zA-Z0-9]\n // treated an accent as a word boundary and dropped it mid-word — \"Serviço\"\n // became \"ServiO\" and \"Preço de Serviço\" became \"PreODeServiO\", which both\n // renames tools per locale and can trip the broker's ^[a-zA-Z0-9_-]+$ rule.\n return value\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .split(/[^a-zA-Z0-9]+/)\n .filter(Boolean)\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join('')\n}\n\nexport function registryToolName(pluginId: string, registryId: string): string {\n // Namespaced by plugin because registry ids only need to be unique within\n // their own plugin — `crm` and `inventory` can both ship a `tags` registry,\n // and two tools sharing a function name is ambiguous to the model.\n return `list${pascalCase(pluginId)}${pascalCase(registryId)}`\n}\n\n/**\n * Auto-generates read tools from plugin registry definitions.\n * Each registry gets a basic \"list\" tool so plugins get AI capabilities for free.\n */\nexport function generateRegistryTools(pluginId: string, registries: PluginRegistryDef[]): PluginAITool[] {\n return registries\n .filter((r) => !r.readOnly)\n .map((registry) => {\n const plural = registry.entity.namePlural ?? `${registry.entity.name}s`\n return {\n id: `${pluginId}.list-${registry.id}`,\n name: registryToolName(pluginId, registry.id),\n description: `Lists all ${plural.toLowerCase()} for the current business.`,\n icon: registry.icon ?? registry.entity.icon,\n mode: 'read' as const,\n parameters: {\n type: 'object' as const,\n properties: {\n search: { type: 'string' as const, description: 'Search text' },\n },\n },\n category: pluginId.charAt(0).toUpperCase() + pluginId.slice(1),\n }\n })\n}\n\n/**\n * Formats a tool as a function signature: e.g. \"getRevenue(period?)\"\n */\nexport function formatToolSignature(tool: PluginAITool): string {\n const params = tool.parameters?.properties\n ? Object.entries(tool.parameters.properties)\n .map(([key]) => tool.parameters?.required?.includes(key) ? key : `${key}?`)\n .join(', ')\n : ''\n return `${tool.name}(${params})`\n}\n\nexport function entityToolName(entityKey: string): string {\n return `search${pascalCase(entityKey)}`\n}\n\n/**\n * Auto-generates a search tool for every entity the app exposes as a CRUD page.\n *\n * Registries cover a plugin's auxiliary tables; these are the entities the\n * business actually talks about — clients, appointments, services. Deriving\n * them from the entity registry is what makes the agent useful in a new vertical\n * without anyone hand-writing tools: whatever the app declares, the agent can\n * read.\n */\n/**\n * The TWO generic data primitives that replace the per-entity/per-registry\n * generated tools (43 searches → 2 tools): the model picks the TARGET as a\n * parameter (closed enum), and the executor checks the target entity's OWN\n * read permission at call time — one tool, per-user access preserved.\n */\n/**\n * Sentences describing entities that ATTACH to another record instead of\n * standing alone. Appended to createRecord's description so the relationship is\n * stated once, up front, rather than discovered through a failed write.\n * Only entities actually present in this app's catalog are mentioned.\n */\nfunction attachedRecordGuidance(writableKeys: string[]): string {\n const hints = CORE_QUERY_ENTITIES\n .filter((q) => q.agentHint && writableKeys.includes(q.key))\n .map((q) => q.agentHint as string)\n return hints.length ? ` Records that attach to another record: ${hints.join(' ')}` : ''\n}\n\n/**\n * The writable fields of every entity the agent may create, as\n * `key: field, field*, …` with `*` marking required.\n *\n * Shipped WITH the tool instead of discovered by failing: without it the model\n * guesses column names, gets `unknown_field`, and burns a round trip per\n * mistake — and the user watches a red step go by before anything happens.\n */\n/**\n * The catalogue is written ONCE, in `searchRecords`, and the write tools point\n * at it. Repeating it in createRecord and updateRecord cost ~8k characters of\n * every request in a mature app to say the same thing three times — and all\n * three tools are in the same prompt, so a pointer reads as well as a copy.\n */\nconst FIELD_CATALOGUE_POINTER =\n '\\n\\nField names per entity are listed in the `searchRecords` tool description (* = required). Send nothing outside them.'\n\nfunction fieldCatalogue(\n entries: Array<{ key: string; fields: Array<{ key: string; required?: boolean }> }>,\n): string {\n const lines = entries\n .filter((e) => e.fields.length > 0)\n .map((e) => {\n const names = e.fields.slice(0, 24).map((f) => (f.required ? `${f.key}*` : f.key))\n return `${e.key}: ${names.join(', ')}`\n })\n if (!lines.length) return ''\n return `\\n\\nFields by entity (* = required, send nothing else):\\n${lines.join('\\n')}`\n}\n\nexport function buildDataPrimitiveTools(input: {\n entities: RegisteredEntity[]\n registries: Map<string, PluginRegistryDef[]>\n queryEntities?: Array<{ key: string; entity: EntityDef; writable?: boolean }>\n}): PluginAITool[] {\n const options: Array<{ key: string; label: string; hint?: string }> = []\n const schema: Array<{ key: string; fields: Array<{ key: string; required?: boolean }> }> = []\n for (const e of input.entities) {\n if (e.entityDef && !e.entityDef.agentHidden) {\n options.push({ key: e.entityKey, label: e.labelPlural })\n schema.push({ key: e.entityKey, fields: e.entityDef.fields ?? [] })\n }\n }\n for (const [pluginId, defs] of input.registries) {\n for (const registry of defs) {\n if (registry.readOnly) continue\n const key = `${pluginId}:${registry.id}`\n options.push({\n key,\n label: registry.entity.namePlural ?? registry.entity.name,\n ...(registry.agentHint ? { hint: registry.agentHint } : {}),\n })\n schema.push({ key, fields: registry.entity.fields ?? [] })\n }\n }\n // Read-models are readable; base-table ones may opt into writes.\n const writableKeys = options.map((o) => o.key)\n // CORE_QUERY_ENTITIES is merged HERE, not at each call site: the primitives\n // are built from three different places (deriveAgentContract, useAITools and\n // the conversational e2e harness), and a spine entity added to only some of\n // them is a catalog that disagrees with itself.\n for (const q of [...(input.queryEntities ?? []), ...CORE_QUERY_ENTITIES]) {\n options.push({ key: q.key, label: q.entity.namePlural ?? q.entity.name })\n if (q.writable) writableKeys.push(q.key)\n }\n if (!options.length) return []\n const keys = options.map((o) => o.key)\n // A label alone is a noun, and a noun is what a model matches on. \"payment\n // methods\" reads as somewhere to file a supplier you just paid — it is in\n // fact the store's card-brand-and-fee configuration, and a bill filed there\n // is silently gone. A registry that is easy to mistake says so here: the\n // catalogue is the only place the model is told what a key MEANS.\n const catalogLine = options\n .map((o) => `${o.key} (${o.label.toLowerCase()}${o.hint ? ` — ${o.hint}` : ''})`)\n .join(', ')\n\n return [\n {\n id: 'data.find-anything',\n name: 'findAnything',\n description:\n 'Global search (like the app command center): looks a name/text up across ALL record types the user may see at once and returns grouped matches. Use this FIRST whenever you do not know what kind of record a name refers to (\"quem é X?\", \"o que é Y?\") — then drill down with searchRecords.' +\n ' It matches TEXT INSIDE each record, so records attached to a person are never returned by that person\\'s name — reaching them takes a second searchRecords call filtered by owner_id.' +\n attachedRecordGuidance(keys),\n icon: 'Command',\n mode: 'read',\n parameters: {\n type: 'object',\n properties: {\n search: { type: 'string', description: 'Name or text to look up everywhere.' },\n },\n required: ['search'],\n },\n category: 'Data',\n suggestions: [{ label: 'Quem é …?' }],\n },\n ...(writableKeys.length\n ? [\n {\n id: 'data.create-record',\n name: 'createRecord',\n description:\n 'Creates ONE new record of an entity (client, service, supplier, …). ALWAYS search first to avoid duplicates. Use ONLY the fields listed below for that entity. ' +\n 'Do NOT interview the user field-by-field. Ask in chat only for required (*) fields you genuinely cannot infer, and call this tool the moment you have them — omit every optional field you were not given (its default applies; a boolean status like active defaults to on/true). ' +\n 'The confirmation this opens is an EDITABLE FORM, not a yes/no receipt: the user reviews, fills and corrects anything there — including optionals — before it is written. Opening that form fast beats collecting everything in chat.' +\n attachedRecordGuidance(writableKeys) +\n FIELD_CATALOGUE_POINTER,\n icon: 'Plus',\n mode: 'persist' as const,\n // The handler orchestrates its own guard chain (permission → plan\n // cap → confirmation → create) because the target is dynamic.\n requiresConfirmation: false,\n parameters: {\n type: 'object' as const,\n properties: {\n entity: { type: 'string' as const, description: 'Which entity to create', enum: writableKeys },\n values: { type: 'object' as const, description: 'Field values for the new record, e.g. {\"name\":\"…\",\"phone\":\"…\"}' },\n },\n required: ['entity', 'values'],\n },\n category: 'Data',\n } satisfies PluginAITool,\n ]\n : []),\n ...(writableKeys.length\n ? [\n {\n id: 'data.update-record',\n name: 'updateRecord',\n description:\n 'Updates fields on an EXISTING record (add an email, fix a phone, change a name…). Use the record id already known from this conversation or from a search. Only send the fields being changed, and only fields the entity actually has. The user confirms before anything is written.' +\n FIELD_CATALOGUE_POINTER,\n icon: 'Pencil',\n mode: 'persist' as const,\n requiresConfirmation: false,\n parameters: {\n type: 'object' as const,\n properties: {\n entity: { type: 'string' as const, description: 'Which entity the record belongs to', enum: writableKeys },\n id: { type: 'string' as const, description: 'Id of the record to update' },\n values: { type: 'object' as const, description: 'Only the fields to change, e.g. {\"email\":\"…\"}' },\n },\n required: ['entity', 'id', 'values'],\n },\n category: 'Data',\n } satisfies PluginAITool,\n ]\n : []),\n {\n id: 'data.search-records',\n name: 'searchRecords',\n description:\n `Searches or lists records of ONE entity and returns matching rows with their fields. Use \\`search\\` for names/text and \\`filters\\` for exact field values (e.g. {\"status\":\"open\"}) — never put a status word in search.` +\n ' For ANY date or period question (\"hoje\", \"amanhã\", \"esta semana\"), use `dateField` + `from`/`to` — NOT `filters`. Plain dates (YYYY-MM-DD) are read as whole days in the business timezone, so `from` and `to` both being today is exactly \"today\".' +\n ` Entities: ${catalogLine}.${attachedRecordGuidance(keys)}` +\n fieldCatalogue(schema),\n icon: 'Search',\n mode: 'read',\n parameters: {\n type: 'object',\n properties: {\n entity: { type: 'string', description: 'Which entity to search', enum: keys },\n search: { type: 'string', description: 'Name or text to match. Omit to list recent records.' },\n filters: { type: 'object', description: 'Optional exact-value filters {field: value}, e.g. {\"status\":\"open\"}. Do not use for dates.' },\n dateField: { type: 'string', description: \"Date field the from/to window applies to (e.g. startsAt for agenda; default 'createdAt').\" },\n from: { type: 'string', description: 'Start of the window: YYYY-MM-DD (whole local day) or ISO datetime.' },\n to: { type: 'string', description: 'End of the window: YYYY-MM-DD (whole local day, inclusive) or ISO datetime.' },\n orderBy: { type: 'string', description: 'Field to sort by (e.g. startsAt for \"next/upcoming\" questions).' },\n direction: { type: 'string', description: 'Sort direction', enum: ['asc', 'desc'] },\n },\n required: ['entity'],\n },\n category: 'Data',\n },\n ...(writableKeys.length\n ? [\n {\n id: 'data.propose-import',\n name: 'proposeImport',\n description:\n 'Proposes how the columns of an ATTACHED spreadsheet/CSV map to the fields of one entity, and opens the mapping for the user to review and correct. ' +\n 'Use this the moment a file is attached and the user talks about importing — it is the ONLY way to answer that, and it replaces writing the mapping out as text. ' +\n 'Send one entry per column you can map; columns you leave out are shown as unmapped for the user to fill in. ' +\n 'Field names come from the entity field list in createRecord. When it comes back ok, call commitImport with the same mapping — that is what actually writes the rows.' +\n ` Entities: ${catalogLine}.`,\n icon: 'ArrowRightLeft',\n mode: 'read' as const,\n category: 'Data',\n parameters: {\n type: 'object' as const,\n properties: {\n entity: { type: 'string' as const, description: 'Which entity the rows would become', enum: writableKeys },\n mapping: {\n type: 'object' as const,\n description:\n 'Column name from the file → field name in the app, e.g. {\"Nome\":\"name\",\"Celular\":\"phone\"}. Leave a column out when no field fits.',\n },\n file: { type: 'string' as const, description: 'File name, when more than one is attached.' },\n },\n required: ['entity', 'mapping'],\n },\n } satisfies PluginAITool,\n {\n id: 'data.commit-import',\n name: 'commitImport',\n description:\n 'WRITES the rows of an attached file as records, using a mapping the user already confirmed through proposeImport. ' +\n 'Call it only after proposeImport came back ok, and pass the SAME mapping it returned. The user confirms the row count before anything is written. ' +\n 'Never call this to \"check\" anything — it creates records.' +\n ` Entities: ${catalogLine}.`,\n icon: 'Upload',\n mode: 'persist' as const,\n requiresConfirmation: false,\n category: 'Data',\n parameters: {\n type: 'object' as const,\n properties: {\n entity: { type: 'string' as const, description: 'Which entity the rows become', enum: writableKeys },\n mapping: {\n type: 'object' as const,\n description: 'The confirmed mapping from proposeImport: file column -> app field.',\n },\n file: { type: 'string' as const, description: 'File name, when more than one is attached.' },\n },\n required: ['entity', 'mapping'],\n },\n } satisfies PluginAITool,\n ]\n : []),\n {\n id: 'data.query-data',\n name: 'queryData',\n description: `Aggregates over ONE entity: count, sum or avg of a field, with optional date range, equality filters and a group-by. Use for analytical questions (revenue this week, new clients this month, busiest professional). Entities: ${catalogLine}.`,\n icon: 'Sigma',\n mode: 'read',\n parameters: {\n type: 'object',\n properties: {\n entity: { type: 'string', description: 'Which entity to aggregate', enum: keys },\n metric: { type: 'string', description: 'Aggregation', enum: ['count', 'sum', 'avg'] },\n field: { type: 'string', description: 'Field to sum/avg (record field name). Ignored for count.' },\n dateField: { type: 'string', description: \"Date field to range-filter (default 'createdAt'; use startsAt for agenda questions).\" },\n from: { type: 'string', description: 'Lower bound (inclusive): YYYY-MM-DD (whole local day) or ISO datetime.' },\n to: { type: 'string', description: 'Upper bound (inclusive): YYYY-MM-DD (whole local day) or ISO datetime.' },\n groupBy: { type: 'string', description: 'Optional field to group results by.' },\n filters: { type: 'object', description: 'Optional equality filters {field: value}.' },\n },\n required: ['entity', 'metric'],\n },\n category: 'Data',\n },\n ]\n}\n\n/**\n * The action vocabulary a profile can grant. Same four the permission matrix\n * renders — a role built in chat and a role built on the screen must be the\n * same kind of object, or the matrix would show a grant it cannot express.\n */\nexport const PROFILE_ACTIONS = ['read', 'create', 'edit', 'delete'] as const\n\n/** Beyond this the catalog costs more in tokens than the choice is worth. */\nconst MAX_FEATURES = 80\n\n/**\n * The access-profile tool, built from the features the app actually declares.\n *\n * Written as ONE tool that takes the whole grant map rather than a\n * create-then-add-permission sequence: asked to describe access in prose,\n * gpt-4o-mini writes a plausible-sounding paragraph and stops — the schema is\n * what makes it commit to real feature ids, and what lets the executor reject\n * the ones it invented.\n *\n * What it CANNOT do is deliberate: the four actions here never include\n * `manage_roles`, the grant that confers `manage_permissions`. A profile minted\n * from chat can therefore never hand out permissions itself.\n */\nexport function buildPermissionTools(\n features: FeatureDeclaration[],\n profiles: PermissionProfile[] = [],\n): PluginAITool[] {\n const usable = features.filter((f) => f.id && f.label).slice(0, MAX_FEATURES)\n if (!usable.length) return []\n\n const catalog = usable\n .map((f) => `${f.id} (${f.label}${f.group && f.group !== f.label ? ` — ${f.group}` : ''})`)\n .join('; ')\n\n // Only custom profiles are editable. System roles are app-defined and static\n // — the screen makes you duplicate one to change it, and so does the agent.\n const editable = profiles.filter((p) => !p.isSystem)\n\n return [\n {\n id: 'core.create-permission-profile',\n name: 'createPermissionProfile',\n description:\n 'Creates an ACCESS PROFILE (permission role) that people on the team can then be assigned to — \"um perfil para Marketing\", \"um acesso só de leitura para o estagiário\". ' +\n 'YOU choose which areas the role needs from the catalog below and which actions on each: pick the smallest set that lets that role do its job, and grant `delete` only when the user asked for it. ' +\n 'Use the exact ids from the catalog — an id that is not in it is rejected. ' +\n 'After it comes back ok, tell the user in plain language what the profile can and cannot reach, using the labels — not the ids.' +\n `\\n\\nAreas (id (label — group)): ${catalog}` +\n `\\nActions on each area: ${PROFILE_ACTIONS.join(', ')}.`,\n icon: 'ShieldCheck',\n mode: 'persist',\n // Own guard chain: system permission → validation → confirmation → write.\n requiresConfirmation: false,\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Name of the profile, e.g. \"Marketing\".' },\n description: { type: 'string', description: 'One line on what this role is for.' },\n grants: {\n type: 'object',\n description:\n 'Area id → actions, e.g. {\"crm.leads\":[\"read\",\"create\",\"edit\"],\"agenda\":[\"read\"]}. Leave an area out entirely when the role should not reach it.',\n },\n },\n required: ['name', 'grants'],\n },\n category: 'Core',\n suggestions: [{ label: 'Cria um perfil de acesso para Marketing' }],\n },\n ...(editable.length\n ? [\n {\n id: 'core.update-permission-profile',\n name: 'updatePermissionProfile',\n description:\n 'Changes an access profile that ALREADY exists — \"dá edição em cursos para o Marketing\", \"deixa o estagiário só com leitura\", \"renomeia para Growth\". ' +\n 'This is the ONLY way to change a profile: never reach for a record tool, a profile is not a record. ' +\n 'Grants MERGE by area: an area you send replaces that area\\'s actions, an area you leave out keeps what it had, and an area only goes away when you name it in `revoke`. ' +\n 'So \"só leitura em cursos\" is grants {courses:[\"read\"]}, and \"tira o financeiro\" is revoke [\"financial\"]. ' +\n 'YOU decide which areas and actions that means — ask the user only when the areas themselves are ambiguous, never to have them dictate the action lists.' +\n `\\n\\nProfiles you can change (id — name): ${editable.map((p) => `${p.id} — ${p.name}`).join('; ')}` +\n `\\nAreas (id (label — group)): ${catalog}` +\n `\\nActions on each area: ${PROFILE_ACTIONS.join(', ')}.`,\n icon: 'ShieldCheck',\n mode: 'persist' as const,\n requiresConfirmation: false,\n parameters: {\n type: 'object' as const,\n properties: {\n id: { type: 'string' as const, description: 'Id of the profile to change, from the list above.', enum: editable.map((p) => p.id) },\n name: { type: 'string' as const, description: 'New name, only when renaming.' },\n description: { type: 'string' as const, description: 'New one-line description, only when changing it.' },\n grants: {\n type: 'object' as const,\n description:\n 'Areas to set, e.g. {\"courses\":[\"read\",\"edit\"]}. Replaces the actions of the areas named here and leaves every other area alone.',\n },\n revoke: {\n type: 'array' as const,\n items: { type: 'string' as const },\n description: 'Area ids the profile should lose entirely.',\n },\n },\n required: ['id'],\n },\n category: 'Core',\n } satisfies PluginAITool,\n ]\n : []),\n ]\n}\n\nexport function generateEntityTools(entities: RegisteredEntity[] = getAllEntities()): PluginAITool[] {\n return entities\n .filter((e) => !!e.entityDef)\n .map((entity) => ({\n id: `entity.${entity.entityKey}`,\n name: entityToolName(entity.entityKey),\n description: `Searches ${entity.labelPlural.toLowerCase()} and returns matching records with their fields. Use this to answer questions about a specific ${entity.label.toLowerCase()} or to count them.`,\n icon: entity.icon,\n mode: 'read' as const,\n parameters: {\n type: 'object' as const,\n properties: {\n search: {\n type: 'string' as const,\n description: `Name or text to match. Omit to list recent ${entity.labelPlural.toLowerCase()}.`,\n },\n },\n },\n category: 'Data',\n }))\n}\n","import type { PluginAITool } from '../types/plugins'\nimport { formatToolSignature } from './core-ai-tools'\n\n/**\n * On-demand tools: the agent carries a few, and reaches the rest by asking.\n *\n * One agent per app means one agent owns every capability the app has — a\n * mature app declares ~40 tools, and beauty-saas' catalog alone serializes to\n * ~55k characters. Shipping all of it on every round is paid twice: in input\n * tokens on each request of each turn, and in the model's own choice, which\n * gets worse as the list grows.\n *\n * So the turn carries three kinds of tool: the PRIMITIVES that answer anything\n * (search, navigate, read, write), whatever the OPEN SCREEN and its plugin\n * publish, and whatever the agent has ASKED for. `findTools` is the ask: the\n * index of what exists travels in one short description, and the schemas only\n * arrive for the areas the conversation actually reached.\n *\n * Loaded tools are sticky for the conversation on purpose. Dropping them again\n * would re-cost the same round trip on the next question about the same\n * subject, and every change to the tool list invalidates the prompt cache from\n * that point on — growing once per area is the cheap direction to be wrong in.\n */\n\n/**\n * The tools that ship on every turn, whatever the conversation is about.\n *\n * Deliberately the generic ones: they take their target as a parameter, so\n * they cover every entity the app has without a name per feature. A tool that\n * only makes sense inside one area (import, connectors, permissions, units)\n * belongs behind the search.\n */\nexport const ALWAYS_ON_TOOLS: ReadonlySet<string> = new Set([\n 'getBusinessSummary',\n 'getTeamMembers',\n 'navigateTo',\n 'openRecord',\n 'findAnything',\n 'searchRecords',\n 'queryData',\n 'createRecord',\n 'updateRecord',\n 'findTools',\n // Reporting a problem has to work from ANY screen — that is where a user hits\n // one. So the Dico carries it every turn instead of behind findTools; the\n // plugin registers its executor, and a turn without the plugin simply has no\n // such tool to ship. findSupportRequests rides along so the agent can check an\n // existing ticket before opening a duplicate.\n 'reportIssue',\n 'findSupportRequests',\n])\n\nconst loaded = new Set<string>()\n/** Areas whose tools a screen asked for up front (see `openAssistantAs`). */\nconst areas = new Set<string>()\nlet version = 0\nconst listeners = new Set<() => void>()\n\nfunction bump(): void {\n version++\n for (const fn of listeners) fn()\n}\n\nexport function loadedToolNames(): ReadonlySet<string> {\n return loaded\n}\n\nexport function getLoadedToolsVersion(): number {\n return version\n}\n\nexport function subscribeLoadedTools(fn: () => void): () => void {\n listeners.add(fn)\n return () => listeners.delete(fn)\n}\n\n/** Put tools in the agent's hands from the next round on. Returns what was new. */\nexport function loadTools(names: string[]): string[] {\n const added = names.filter((n) => !loaded.has(n))\n if (!added.length) return []\n for (const n of added) loaded.add(n)\n bump()\n return added\n}\n\n/** Hand over a whole area at once — what a screen means by \"ask X about this\".\n * Resolved against the catalog rather than stored as names, so it also covers\n * tools a plugin mounted after the request. */\nexport function loadArea(area: string): void {\n if (areas.has(area)) return\n areas.add(area)\n bump()\n}\n\n/** A new conversation starts with the carry-on set again. */\nexport function resetLoadedTools(): void {\n if (!loaded.size && !areas.size) return\n loaded.clear()\n areas.clear()\n bump()\n}\n\n/**\n * Every tool the agent has asked for, by name — the two ways of asking\n * (`findTools`, an area handed over by a screen) resolved against the live\n * catalog. Read fresh on each round of a turn: a search only pays off if its\n * result is in the very next request.\n */\nexport function resolveLoadedNames(catalog: PluginAITool[]): Set<string> {\n const names = new Set(loaded)\n if (areas.size) {\n for (const tool of catalog) {\n if (tool.role && areas.has(tool.role)) names.add(tool.name)\n }\n }\n return names\n}\n\n/** Where a tool lives in the index the agent searches. */\nfunction areaOf(tool: PluginAITool): string {\n return tool.category ?? tool.id.split('.')[0] ?? 'Outros'\n}\n\n/** Tools that are not carried by default — the searchable half of the catalog. */\nexport function searchableTools(catalog: PluginAITool[], pinned: ReadonlySet<string>): PluginAITool[] {\n return catalog.filter((t) => !pinned.has(t.name) && !ALWAYS_ON_TOOLS.has(t.name))\n}\n\n/**\n * The index the model reads instead of the schemas: area, then the tool names\n * in it. Names and nothing else — a name plus its area is enough to recognise\n * \"this is where I ask\", and it costs ~2% of what the schemas cost.\n */\nexport function buildToolIndex(tools: PluginAITool[]): string {\n const byArea = new Map<string, string[]>()\n for (const tool of tools) {\n const area = areaOf(tool)\n if (!byArea.has(area)) byArea.set(area, [])\n byArea.get(area)!.push(tool.name)\n }\n return [...byArea.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([area, names]) => `${area}: ${names.join(', ')}`)\n .join('\\n')\n}\n\n/**\n * The one tool that reaches the other ones.\n *\n * Absent when everything already ships this turn — a search that can only\n * return what the model is holding is a wasted round.\n */\nexport function buildToolSearchTool(searchable: PluginAITool[]): PluginAITool[] {\n if (!searchable.length) return []\n return [\n {\n id: 'core.find-tools',\n name: 'findTools',\n description:\n 'Loads MORE tools. You carry the general-purpose ones plus the ones for the screen the user is on; everything else in this app is listed below and arrives only when you ask for it here.' +\n ' Call this the moment a request belongs to an area you have no tool for — BEFORE saying you cannot do something. The tools come back callable from your next step, so ask and then act; never announce that you are loading them.' +\n `\\n\\nWhat this app can also do:\\n${buildToolIndex(searchable)}`,\n icon: 'Wrench',\n mode: 'read',\n category: 'Core',\n parameters: {\n type: 'object',\n properties: {\n tools: {\n type: 'array',\n items: { type: 'string' },\n description: 'Tool names from the list above. Ask for everything the task needs at once.',\n },\n query: {\n type: 'string',\n description: 'What you are trying to do, in a few words. Use when no name above clearly matches.',\n },\n },\n },\n },\n ]\n}\n\n/** camelCase and prose alike, as words: a model that paraphrases `createCampaign`\n * as \"campaignCreate\" is asking for the same tool, and making it spend a round\n * discovering that is the one cost this whole mechanism exists to avoid. */\nfunction words(text: string): string[] {\n return text\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((w) => w.length > 2)\n}\n\nfunction matches(tool: PluginAITool, query: string): boolean {\n const terms = words(query)\n if (!terms.length) return false\n const haystack = new Set(words(`${tool.name} ${areaOf(tool)} ${tool.description}`))\n return terms.some((t) => haystack.has(t))\n}\n\nexport interface ToolSearchResult {\n loaded: Array<{ name: string; signature: string; description: string }>\n available?: string\n note?: string\n}\n\n/**\n * Resolve a request for tools against the searchable catalog and hand them\n * over. Unknown names fall back to the query path rather than erroring — the\n * model paraphrasing a name it saw once must not cost a whole round.\n */\nexport function resolveToolSearch(\n args: { tools?: unknown; query?: unknown },\n searchable: PluginAITool[],\n /**\n * Names the turn is ALREADY carrying — the primitives plus whatever the open\n * screen published. Not searchable (they need no loading), which is exactly\n * why they have to be named here: without them a request for a tool already\n * in hand fell through to \"nothing matched\", whose note tells the model to\n * say the app cannot do it. That is how the site builder answered \"I have no\n * tool for that\" while holding builder_request_change.\n */\n carried: ReadonlySet<string> = new Set(),\n): ToolSearchResult {\n const asked = Array.isArray(args.tools) ? args.tools.filter((n): n is string => typeof n === 'string') : []\n const query = typeof args.query === 'string' ? args.query : ''\n const byName = new Map(searchable.map((t) => [t.name.toLowerCase(), t]))\n\n const found = new Map<string, PluginAITool>()\n for (const name of asked) {\n const hit = byName.get(name.trim().toLowerCase())\n if (hit) found.set(hit.name, hit)\n }\n if (!found.size && query) {\n for (const tool of searchable.filter((t) => matches(t, query)).slice(0, 8)) found.set(tool.name, tool)\n }\n if (!found.size && asked.length) {\n for (const tool of searchable.filter((t) => matches(t, asked.join(' '))).slice(0, 8)) found.set(tool.name, tool)\n }\n\n const tools = [...found.values()]\n\n // Asked for something already in hand. Answering \"nothing matched\" here is\n // worse than unhelpful: the note tells the model to declare the app incapable\n // of the very thing it can do this turn.\n if (!tools.length) {\n const byName = new Map([...carried].map((n) => [n.toLowerCase(), n]))\n const held = asked.map((n) => byName.get(n.trim().toLowerCase())).filter((n): n is string => !!n)\n if (held.length) {\n return {\n loaded: [],\n note:\n `You are already carrying ${held.join(', ')} this turn — call ${\n held.length > 1 ? 'them' : 'it'\n } directly instead of loading anything.`,\n }\n }\n }\n\n if (!tools.length) {\n return {\n loaded: [],\n available: buildToolIndex(searchable),\n note: 'Nothing matched. Pick names from the list above, or tell the user this app has no tool for that.',\n }\n }\n\n loadTools(tools.map((t) => t.name))\n return {\n loaded: tools.map((t) => ({\n name: t.name,\n signature: formatToolSignature(t),\n // Truncated: the full schema arrives with the tool itself on the next\n // step, so repeating it here pays for the same text twice.\n description: t.description.slice(0, 400),\n })),\n note: 'Available from your next step. Continue the task now — do not tell the user about this step.',\n }\n}\n","/**\n * Attachments for the assistant, parsed in the browser.\n *\n * The bytes NEVER leave this module. A spreadsheet of clients or patients is\n * personal data (LGPD) — shipping it to a model provider to \"have a look\" is\n * not a technical shortcut, it is a disclosure. So the file stays in the tab\n * and only a DIGEST travels: the shape (sheets, headers, row count, inferred\n * column types) plus a handful of sample rows. The model sees the FORM, never\n * the data set.\n *\n * Everything here is therefore bounded on purpose: a 50k-row file and a 50-row\n * file produce digests of roughly the same size.\n */\n\nexport type AttachmentKind = 'spreadsheet' | 'csv' | 'text' | 'image' | 'pdf' | 'binary'\n\nexport type AttachmentColumnType =\n | 'text'\n | 'number'\n | 'date'\n | 'boolean'\n | 'email'\n | 'phone'\n\nexport interface AttachmentColumn {\n name: string\n type: AttachmentColumnType\n /** Detected literal shape ('dd/mm/yyyy', 'yyyy-mm-dd', '1.234,56'), when known. */\n format?: string\n /** Why this column is not the type it looks like — ambiguity, mixed values. */\n note?: string\n}\n\nexport interface AttachmentSheet {\n name: string\n headers: string[]\n /** Data rows, header excluded. Absent when only the head of the file was read. */\n rowCount?: number\n sample: string[][]\n columnTypes: AttachmentColumn[]\n}\n\nexport interface AttachmentDigest {\n id: string\n name: string\n mime: string\n size: number\n kind: AttachmentKind\n /** Spreadsheets with more than one tab. Single-tab files use the flat fields. */\n sheets?: AttachmentSheet[]\n headers?: string[]\n rowCount?: number\n sample?: string[][]\n columnTypes?: AttachmentColumn[]\n /** Head of a text/markdown file. */\n preview?: string\n /** Everything the reader had to guess at, truncate or give up on. */\n warnings?: string[]\n /** Set when the file could not be opened at all — the chip says what to do\n * instead of pretending a shapeless digest is a reading. */\n unreadable?: 'spreadsheet'\n}\n\n// Digest budget. These caps are what keeps a 50k-row file cheap to describe.\nconst SAMPLE_ROWS = 5\n/** Rows read for type inference — beyond this the extra evidence is noise. */\nconst SCAN_ROWS = 200\nconst MAX_HEADERS = 50\nconst MAX_CELL_CHARS = 120\nconst MAX_SHEETS = 8\nconst PREVIEW_CHARS = 2000\n/** Above this we read only the head, so a 200MB export cannot freeze the tab. */\nconst FULL_READ_LIMIT = 8 * 1024 * 1024\nconst HEAD_SLICE = 512 * 1024\n\n// ---------------------------------------------------------------------------\n// Registry — the only place the bytes live\n// ---------------------------------------------------------------------------\n\ninterface RegistryEntry {\n file: File\n parsed?: AttachmentDigest\n}\n\nconst registry = new Map<string, RegistryEntry>()\nlet seq = 0\n\n/** Park a file and get the handle everything else refers to it by. */\nexport function registerAttachment(file: File): string {\n const id = `att-${Date.now().toString(36)}-${(seq++).toString(36)}`\n registry.set(id, { file })\n return id\n}\n\nexport function getAttachmentFile(id: string): File | undefined {\n return registry.get(id)?.file\n}\n\nexport function getAttachmentDigest(id: string): AttachmentDigest | undefined {\n return registry.get(id)?.parsed\n}\n\n/** Drop the bytes. Called on chip removal and whenever the thread is cleared. */\nexport function releaseAttachment(id: string): void {\n registry.delete(id)\n}\n\nexport function clearAttachmentRegistry(): void {\n registry.clear()\n}\n\n/**\n * The file a tool call is about, resolved the way the MODEL can name it.\n *\n * The prompt shows attachments by filename, never by handle, so a tool that\n * demanded the registry id would be asking for something the model was never\n * told. Name match first, then the most recent readable file — a turn almost\n * always has exactly one.\n */\nexport function resolveAttachmentDigest(name?: string): AttachmentDigest | undefined {\n const readable = [...registry.values()]\n .map((e) => e.parsed)\n .filter((d): d is AttachmentDigest => !!d?.headers?.length)\n // A NAMED file that does not match is an error, never the latest one: falling\n // back there would map a different file than the one the user meant.\n if (name?.trim()) {\n const wanted = name.trim().toLowerCase()\n return readable.find((d) => d.name.toLowerCase() === wanted)\n }\n return readable[readable.length - 1]\n}\n\n/**\n * Every row of an attached delimited file, as objects keyed by header.\n *\n * The digest carries five sample rows — enough to map, useless to import. This\n * re-reads the bytes still parked in the registry, so the full set never had to\n * travel anywhere: it goes browser -> database, and the model never sees a row.\n */\nexport async function readAllRows(digest: AttachmentDigest): Promise<Array<Record<string, string>>> {\n const entry = [...registry.values()].find((e) => e.parsed === digest)\n if (!entry) throw new Error('The file is no longer available in this conversation.')\n if (digest.kind !== 'csv') {\n throw new Error('Only CSV files can be read in full here. Ask the user to save the sheet as CSV.')\n }\n const text = await entry.file.text()\n const delimiter = detectDelimiter(text, /\\.tsv$/i.test(entry.file.name) ? '\\t' : undefined)\n const { rows } = parseDelimited(text, delimiter)\n if (!rows.length) return []\n const headers = rows[0].map((h) => h.trim())\n return rows.slice(1)\n .filter((row) => row.some((cell) => cell.trim()))\n .map((row) => {\n const record: Record<string, string> = {}\n headers.forEach((header, i) => {\n if (header) record[header] = (row[i] ?? '').trim()\n })\n return record\n })\n}\n\n/** Every readable file currently in play, for an error that can name them. */\nexport function listAttachmentNames(): string[] {\n return [...registry.values()]\n .map((e) => e.parsed)\n .filter((d): d is AttachmentDigest => !!d?.headers?.length)\n .map((d) => d.name)\n}\n\n/**\n * What IS attached but has no columns — a photo, a PDF, a binary.\n *\n * So a tool that only reads tables can refuse by NAME instead of claiming\n * nothing is attached. An image is not a missing file; it is a file this\n * particular tool is the wrong reader for.\n */\nexport function nonTabularAttachmentNames(): string[] {\n return [...registry.values()]\n .map((e) => e.parsed)\n .filter((d): d is AttachmentDigest => !!d && !d.headers?.length && !d.sheets?.length)\n .map((d) => d.name)\n}\n\n// ---------------------------------------------------------------------------\n// Parsing\n// ---------------------------------------------------------------------------\n\nconst SPREADSHEET_EXT = /\\.(xlsx|xlsm|xls)$/i\nconst CSV_EXT = /\\.(csv|tsv)$/i\nconst TEXT_EXT = /\\.(txt|md|markdown|json|log)$/i\n\nexport function detectKind(file: File): AttachmentKind {\n const name = file.name\n const mime = file.type || ''\n if (SPREADSHEET_EXT.test(name) || mime.includes('spreadsheetml') || mime === 'application/vnd.ms-excel') {\n return 'spreadsheet'\n }\n if (CSV_EXT.test(name) || mime === 'text/csv' || mime === 'text/tab-separated-values') return 'csv'\n if (mime.startsWith('image/')) return 'image'\n if (mime === 'application/pdf' || /\\.pdf$/i.test(name)) return 'pdf'\n if (TEXT_EXT.test(name) || mime.startsWith('text/') || mime === 'application/json') return 'text'\n return 'binary'\n}\n\n/**\n * Read a file into its digest. Registers the bytes when no id is supplied, so\n * a caller that wants to show a \"parsing\" chip first can register up front and\n * pass the id back in.\n */\nexport async function parseAttachment(file: File, id?: string): Promise<AttachmentDigest> {\n const attachmentId = id ?? registerAttachment(file)\n const base: AttachmentDigest = {\n id: attachmentId,\n name: file.name,\n mime: file.type || 'application/octet-stream',\n size: file.size,\n kind: detectKind(file),\n }\n\n let digest = base\n switch (base.kind) {\n case 'csv':\n digest = await parseDelimitedFile(file, base)\n break\n case 'spreadsheet':\n digest = await parseSpreadsheetFile(file, base)\n break\n case 'text':\n digest = await parseTextFile(file, base)\n break\n // Images and PDFs carry meaning in pixels, not in columns. Phase 1 declares\n // them and stops there — reading them is a different road (vision/OCR).\n default:\n break\n }\n\n const entry = registry.get(attachmentId)\n if (entry) entry.parsed = digest\n return digest\n}\n\nasync function readTextBounded(file: File): Promise<{ text: string; partial: boolean }> {\n if (file.size <= FULL_READ_LIMIT) return { text: await file.text(), partial: false }\n return { text: await file.slice(0, HEAD_SLICE).text(), partial: true }\n}\n\nasync function parseTextFile(file: File, base: AttachmentDigest): Promise<AttachmentDigest> {\n const { text } = await readTextBounded(file)\n const preview = text.slice(0, PREVIEW_CHARS)\n return {\n ...base,\n preview,\n ...(text.length > PREVIEW_CHARS\n ? { warnings: [`preview truncated to ${PREVIEW_CHARS} characters`] }\n : {}),\n }\n}\n\nasync function parseDelimitedFile(file: File, base: AttachmentDigest): Promise<AttachmentDigest> {\n const { text, partial } = await readTextBounded(file)\n const warnings: string[] = []\n if (partial) {\n // The tail was never read, so any row count would be a guess.\n warnings.push('file too large — only the first rows were read, row count unknown')\n }\n const delimiter = detectDelimiter(text, /\\.tsv$/i.test(file.name) ? '\\t' : undefined)\n const sheet = buildSheet(file.name.replace(CSV_EXT, ''), parseDelimited(text, delimiter), warnings)\n if (partial) delete sheet.rowCount\n return {\n ...base,\n headers: sheet.headers,\n ...(sheet.rowCount === undefined ? {} : { rowCount: sheet.rowCount }),\n sample: sheet.sample,\n columnTypes: sheet.columnTypes,\n ...(warnings.length ? { warnings } : {}),\n }\n}\n\n/**\n * xlsx is an optional peer — the same posture plugin-reports takes for export.\n * `new Function` keeps the specifier opaque so Vite does not try to resolve it\n * at build time in an app that never installed it.\n */\nconst loadModule = (name: string) => new Function('m', 'return import(m)')(name) as Promise<any>\n\nasync function parseSpreadsheetFile(file: File, base: AttachmentDigest): Promise<AttachmentDigest> {\n let XLSX: any\n try {\n XLSX = await loadModule('xlsx')\n } catch {\n // Deliberate, not a missing install: reading Excel means parsing hostile\n // binary with a library the registry stopped updating in 2022, and CSV\n // costs the user one \"save as\". Phrase it for the PERSON — this text is\n // the chip AND what the model repeats back.\n return {\n ...base,\n unreadable: 'spreadsheet',\n warnings: [\n 'Excel files are not read here. Ask the user to save the sheet as CSV (File > Save as > CSV) and attach it again — CSV is read in full.',\n ],\n }\n }\n\n const warnings: string[] = []\n let book: any\n try {\n book = XLSX.read(await file.arrayBuffer(), { type: 'array' })\n } catch {\n return { ...base, warnings: ['could not read this spreadsheet — the file may be corrupted'] }\n }\n\n const names: string[] = book.SheetNames ?? []\n const sheets: AttachmentSheet[] = []\n for (const name of names.slice(0, MAX_SHEETS)) {\n const ws = book.Sheets?.[name]\n if (!ws) continue\n // raw:false gives the displayed text, which is what a person reading the\n // file would see — and what the column inference below is written against.\n const raw: unknown[][] = XLSX.utils.sheet_to_json(ws, { header: 1, raw: false, defval: '' })\n const rows = raw.slice(0, SCAN_ROWS).map((row) => row.map((cell) => String(cell ?? '')))\n sheets.push(buildSheet(name, { rows, count: raw.length }, warnings))\n }\n if (names.length > MAX_SHEETS) {\n warnings.push(`${names.length - MAX_SHEETS} additional sheet(s) not described`)\n }\n\n const only = sheets.length === 1 ? sheets[0] : undefined\n return {\n ...base,\n ...(only\n ? {\n headers: only.headers,\n ...(only.rowCount === undefined ? {} : { rowCount: only.rowCount }),\n sample: only.sample,\n columnTypes: only.columnTypes,\n }\n : { sheets }),\n ...(warnings.length ? { warnings } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Delimited text\n// ---------------------------------------------------------------------------\n\n/** Excel in pt-BR writes `;` — guessing `,` there yields one giant column. */\nfunction detectDelimiter(text: string, forced?: string): string {\n if (forced) return forced\n const firstLine = text.slice(0, 4000).split(/\\r?\\n/)[0] ?? ''\n let best = ','\n let bestCount = 0\n for (const candidate of [',', ';', '\\t', '|']) {\n const count = countOutsideQuotes(firstLine, candidate)\n if (count > bestCount) {\n best = candidate\n bestCount = count\n }\n }\n return best\n}\n\nfunction countOutsideQuotes(line: string, char: string): number {\n let inQuotes = false\n let count = 0\n for (let i = 0; i < line.length; i++) {\n const c = line[i]\n if (c === '\"') inQuotes = !inQuotes\n else if (!inQuotes && c === char) count++\n }\n return count\n}\n\n/**\n * Records are counted to the end of the text but only the first `SCAN_ROWS`\n * are kept — the count is the fact worth reporting, the rows are not.\n */\nexport function parseDelimited(text: string, delimiter: string): { rows: string[][]; count: number } {\n const rows: string[][] = []\n let count = 0\n let row: string[] = []\n let field = ''\n let inQuotes = false\n\n const endField = () => {\n row.push(field)\n field = ''\n }\n const endRow = () => {\n endField()\n count++\n if (rows.length < SCAN_ROWS) rows.push(row)\n row = []\n }\n\n const source = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text\n for (let i = 0; i < source.length; i++) {\n const c = source[i]\n if (inQuotes) {\n if (c === '\"') {\n if (source[i + 1] === '\"') {\n field += '\"'\n i++\n } else inQuotes = false\n } else field += c\n continue\n }\n if (c === '\"' && field === '') inQuotes = true\n else if (c === delimiter) endField()\n else if (c === '\\n') endRow()\n else if (c === '\\r') continue\n else field += c\n }\n // A trailing newline is not a record.\n if (field !== '' || row.length) endRow()\n return { rows, count }\n}\n\nfunction buildSheet(\n name: string,\n parsed: { rows: string[][]; count: number },\n warnings: string[],\n): AttachmentSheet {\n const [rawHeader = [], ...body] = parsed.rows\n const headers = rawHeader.slice(0, MAX_HEADERS).map((h, i) => truncateCell(h).trim() || `col_${i + 1}`)\n if (rawHeader.length > MAX_HEADERS) {\n warnings.push(`${name}: ${rawHeader.length - MAX_HEADERS} column(s) beyond the first ${MAX_HEADERS} not described`)\n }\n\n const sample = body.slice(0, SAMPLE_ROWS).map((row) => headers.map((_, i) => truncateCell(row[i] ?? '')))\n const columnTypes = headers.map((header, i) =>\n inferColumn(header, body.map((row) => row[i] ?? ''), name, warnings),\n )\n\n return { name, headers, rowCount: Math.max(0, parsed.count - 1), sample, columnTypes }\n}\n\nfunction truncateCell(value: string): string {\n const text = String(value ?? '')\n return text.length > MAX_CELL_CHARS ? `${text.slice(0, MAX_CELL_CHARS)}…` : text\n}\n\n// ---------------------------------------------------------------------------\n// Column inference — pt-BR literate\n// ---------------------------------------------------------------------------\n\nconst SLASH_DATE = /^(\\d{1,2})[/.-](\\d{1,2})[/.-](\\d{2,4})$/\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}([T ]\\d{2}:\\d{2}(:\\d{2})?)?/\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/\nconst MASKED_PHONE = /^\\+?[\\d\\s().-]{8,20}$/\nconst BOOLEANS = new Set(['sim', 'não', 'nao', 'true', 'false', 'yes', 'no', 'verdadeiro', 'falso'])\n\nexport function inferColumn(\n name: string,\n values: string[],\n sheetName = '',\n warnings: string[] = [],\n): AttachmentColumn {\n const filled = values\n .slice(0, SCAN_ROWS)\n .map((v) => String(v ?? '').trim())\n .filter((v) => v !== '')\n if (!filled.length) return { name, type: 'text' }\n\n if (filled.every((v) => BOOLEANS.has(v.toLowerCase()))) return { name, type: 'boolean' }\n if (filled.every((v) => EMAIL.test(v))) return { name, type: 'email' }\n\n const dates = inferDates(filled)\n if (dates) {\n if (dates.ambiguous) {\n const note =\n dates.reason === 'conflict'\n ? 'mixed dd/mm and mm/dd rows — read as text'\n : 'day/month order cannot be told apart (all values ≤ 12) — read as text'\n warnings.push(`${sheetName ? `${sheetName}: ` : ''}column \"${name}\": ${note}`)\n return { name, type: 'text', note }\n }\n return { name, type: 'date', format: dates.format }\n }\n\n // Identifiers before numbers. A real export writes phones and documents as\n // bare digits — \"21964445090\", \"07872499711\" — and reading those as numbers\n // is not a cosmetic slip: it drops the leading zero of a CPF and offers the\n // model a quantity to average. Digits nobody would do arithmetic on are not\n // numbers, and the header usually says which.\n const identifier = inferIdentifier(name, filled)\n if (identifier) return identifier\n\n const numbers = inferNumbers(filled)\n if (numbers) {\n return {\n name,\n type: 'number',\n ...(numbers.format ? { format: numbers.format } : {}),\n ...(numbers.note ? { note: numbers.note } : {}),\n }\n }\n\n // Masked phones: a bare integer column already went through inferIdentifier.\n if (filled.every((v) => MASKED_PHONE.test(v) && /[\\s().+-]/.test(v) && digitsOf(v).length >= 8)) {\n return { name, type: 'phone' }\n }\n\n return { name, type: 'text' }\n}\n\nfunction digitsOf(value: string): string {\n return value.replace(/\\D/g, '')\n}\n\n/**\n * `03/04/2026` is 3 April in pt-BR and 4 March in en-US, and nothing in the\n * cell says which. A column only becomes a date when some row settles it — a\n * day past 12 in one position or the other. Otherwise it stays text and says\n * so, because a silently wrong date is worse than an unparsed one.\n */\nfunction inferDates(\n values: string[],\n): { format: string; ambiguous: false } | { ambiguous: true; reason: 'conflict' | 'undecidable' } | null {\n if (values.every((v) => ISO_DATE.test(v))) return { format: 'yyyy-mm-dd', ambiguous: false }\n if (!values.every((v) => SLASH_DATE.test(v))) return null\n\n let dayFirst = false\n let monthFirst = false\n for (const value of values) {\n const match = SLASH_DATE.exec(value)!\n const a = Number(match[1])\n const b = Number(match[2])\n if (a > 12 && b <= 12) dayFirst = true\n if (b > 12 && a <= 12) monthFirst = true\n if (a > 31 || b > 31) return null\n }\n if (dayFirst && monthFirst) return { ambiguous: true, reason: 'conflict' }\n if (dayFirst) return { format: 'dd/mm/yyyy', ambiguous: false }\n if (monthFirst) return { format: 'mm/dd/yyyy', ambiguous: false }\n return { ambiguous: true, reason: 'undecidable' }\n}\n\nconst BR_DECIMAL = /^-?\\d{1,3}(\\.\\d{3})*,\\d+$|^-?\\d+,\\d+$/\nconst US_DECIMAL = /^-?\\d{1,3}(,\\d{3})*\\.\\d+$|^-?\\d+\\.\\d+$/\nconst PLAIN_INT = /^-?\\d+$/\n/** `1.234` is 1234 in pt-BR and 1.234 in en-US — a separator, or a decimal. */\nconst DOTTED_GROUP = /^-?\\d{1,3}(\\.\\d{3})+$/\n\nconst PHONE_HEADER = /(celular|telefone|fone|whats|tel\\b|mobile|phone)/i\nconst DOCUMENT_HEADER = /(cpf|cnpj|\\brg\\b|documento|\\bdoc\\b|inscri|matr[ií]cula|identificador|c[oó]digo)/i\nconst CEP_HEADER = /(cep|zip|postal)/i\n\n/**\n * A digit-only column that is an identifier, not a quantity. Recognised by what\n * the header calls it, or by a leading zero — no quantity ever starts with one.\n */\nfunction inferIdentifier(name: string, values: string[]): AttachmentColumn | null {\n const bareDigits = values.every((v) => /^\\d+$/.test(v))\n if (!bareDigits) return null\n const lengths = values.map((v) => v.length)\n const hasLeadingZero = values.some((v) => v.length > 1 && v.startsWith('0'))\n\n if (PHONE_HEADER.test(name) && lengths.every((n) => n >= 8 && n <= 15)) {\n return { name, type: 'phone', note: 'digits only, no mask' }\n }\n if (DOCUMENT_HEADER.test(name) || CEP_HEADER.test(name)) {\n return {\n name,\n type: 'text',\n note: 'identifier, not a quantity — keep it as text so leading zeros survive',\n }\n }\n if (hasLeadingZero) {\n return { name, type: 'text', note: 'leading zeros — read as text, not a number' }\n }\n return null\n}\n\nfunction inferNumbers(values: string[]): { format?: string; note?: string } | null {\n const stripped = values.map((v) => v.replace(/[R$€£%\\s ]/g, ''))\n let br = false\n let us = false\n let dotted = false\n for (const value of stripped) {\n if (BR_DECIMAL.test(value)) br = true\n else if (DOTTED_GROUP.test(value)) dotted = true\n else if (US_DECIMAL.test(value)) us = true\n else if (!PLAIN_INT.test(value)) return null\n }\n if (br && us) return { note: 'mixed decimal separators (, and .)' }\n if (br) return { format: '1.234,56' }\n if (us) return { format: '1,234.56' }\n if (dotted) return { note: 'dots could be thousand separators or decimals' }\n return {}\n}\n\n// ---------------------------------------------------------------------------\n// Handing a file over to be READ\n// ---------------------------------------------------------------------------\n\n/**\n * The bytes of a registered attachment, base64 for the wire.\n *\n * The registry already holds them — `parseAttachment` keeps the File so a CSV\n * can be re-read in full for an import — so nothing new is stored here. What is\n * new is that the caller can now ship them, and that is deliberately a separate\n * function from the digest path: reading a file to the model is an act someone\n * has to WRITE, not a flag that can drift onto a digest.\n *\n * Returns null when the id is unknown (a resumed thread, a cleared registry).\n */\nexport async function readAttachmentBytes(\n id: string,\n): Promise<InlineFileBytes | null> {\n const file = getAttachmentFile(id)\n if (!file) return null\n return toInlineFile(file)\n}\n\n/**\n * O teto de visão da Anthropic: acima disso a imagem é reduzida do outro lado\n * de qualquer forma, então subir mais que isso é pagar banda por pixels que\n * ninguém vai olhar. Uma foto de celular tem 3000–4000px de lado.\n */\nconst VISION_MAX_EDGE = 1568\n\n/**\n * Encolhe uma foto até o que o modelo realmente consome, ANTES de virar base64.\n *\n * Sem isso um recibo fotografado sai do celular com 3–5MB, vira ~4–7MB em\n * base64 e o turno morre antes de chegar ao modelo — foi exatamente o\n * `PayloadTooLargeError` que a primeira leitura de recibo produziu. Reduzir\n * aqui é a única correção que serve os dois lados: menos bytes na rede de quem\n * está no 4G, e um corpo de request que cabe.\n *\n * Devolve o arquivo original quando não há o que ganhar (não é imagem, já é\n * pequena) ou quando o ambiente não tem canvas — um teste em node não pode\n * quebrar por causa de uma otimização.\n */\nexport async function shrinkForVision(file: File, maxEdge = VISION_MAX_EDGE): Promise<File> {\n if (!file.type.startsWith('image/')) return file\n if (typeof document === 'undefined' || typeof createImageBitmap !== 'function') return file\n\n try {\n const bitmap = await createImageBitmap(file)\n const longest = Math.max(bitmap.width, bitmap.height)\n if (longest <= maxEdge) { bitmap.close?.(); return file }\n\n const scale = maxEdge / longest\n const canvas = document.createElement('canvas')\n canvas.width = Math.round(bitmap.width * scale)\n canvas.height = Math.round(bitmap.height * scale)\n const ctx = canvas.getContext('2d')\n if (!ctx) { bitmap.close?.(); return file }\n ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height)\n bitmap.close?.()\n\n // JPEG e não PNG: o alvo é uma FOTO, e um PNG de foto sai várias vezes\n // maior sem uma diferença que o modelo consiga ler.\n const blob = await new Promise<Blob | null>((resolve) =>\n canvas.toBlob((b) => resolve(b), 'image/jpeg', 0.85),\n )\n if (!blob || blob.size >= file.size) return file\n return new File([blob], file.name.replace(/\\.[^.]+$/, '') + '.jpg', { type: 'image/jpeg' })\n } catch {\n // Um formato que o navegador não decodifica (HEIC em alguns) não pode\n // impedir o envio: segue o original e o servidor decide.\n return file\n }\n}\n\nexport interface InlineFileBytes {\n name: string\n mime: string\n /** Base64, no `data:` prefix. */\n data: string\n}\n\n/** Same, for a File the caller already holds (an upload the assistant asked for). */\nexport async function toInlineFile(file: File): Promise<InlineFileBytes> {\n return {\n name: file.name,\n mime: file.type || 'application/octet-stream',\n data: await toBase64(file),\n }\n}\n\n/**\n * Chunked, because the one-liner does not survive a photo.\n *\n * `btoa` needs a binary STRING, and the obvious way to build one —\n * `String.fromCharCode(...bytes)` — spreads every byte as an argument: a 3MB\n * receipt is three million arguments and a blown call stack. 32KB at a time\n * costs nothing and has no ceiling.\n *\n * `arrayBuffer()` rather than FileReader so this is the same code in a browser\n * and in a test runner; FileReader is a DOM API and does not exist in node.\n */\nasync function toBase64(file: File): Promise<string> {\n const bytes = new Uint8Array(await file.arrayBuffer())\n const CHUNK = 0x8000\n let binary = ''\n for (let i = 0; i < bytes.length; i += CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK))\n }\n return btoa(binary)\n}\n\n// ---------------------------------------------------------------------------\n// Display helpers\n// ---------------------------------------------------------------------------\n\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n","import { useAuthStore } from '@fayz-ai/auth'\nimport { useOrganizationStore } from '../stores/organization.store'\n\n// ---------------------------------------------------------------------------\n// Auto-approval — the assistant's \"don't ask me again\", modelled on Claude\n// Code's allowlist.\n//\n// Confirming every single write is the right default (an agent write is still\n// a write), but a manager who books twenty appointments an hour answers the\n// same card twenty times. So the card offers a third answer: approve, and\n// remember this ONE kind of action.\n//\n// Two grades, on purpose:\n// • persisted — additive writes (create/update). Survive reloads, scoped to\n// org + user, revocable from the assistant's tools panel.\n// • session — destructive writes (delete/cancel). Never persisted: a\n// misheard voice command must not be able to erase data on a\n// rule the user granted last month and forgot.\n// ---------------------------------------------------------------------------\n\nconst STORAGE_PREFIX = 'fayz.agent.auto-approve.v1'\n\nexport interface AutoApprovalRule {\n /** `toolName` or `toolName:scope` — what the decision is remembered against. */\n key: string\n toolName: string\n /** Entity the rule is limited to, so \"always create clients\" never becomes\n * \"always create anything\". */\n scope?: string\n /** Human label for the revoke list (\"Criar Cliente\"). */\n label: string\n createdAt: string\n}\n\n/** Writes that remove or void data. Their grants never outlive the tab. */\nconst DESTRUCTIVE = /(delete|destroy|remove|cancel|archive|refund|void)/i\n\nexport function isDestructiveTool(toolName: string): boolean {\n return DESTRUCTIVE.test(toolName)\n}\n\nexport function approvalKey(toolName: string, scope?: string): string {\n return scope ? `${toolName}:${scope}` : toolName\n}\n\nfunction storageKey(): string | null {\n const orgId = useOrganizationStore.getState().currentOrg?.id\n const userId = useAuthStore.getState().user?.id\n // No identity yet → nothing is remembered. Anonymous grants would follow the\n // browser, not the person, and leak across tenants on a shared machine.\n if (!orgId || !userId) return null\n return `${STORAGE_PREFIX}:${orgId}:${userId}`\n}\n\nfunction read(): AutoApprovalRule[] {\n const key = storageKey()\n if (!key || typeof localStorage === 'undefined') return []\n try {\n const raw = localStorage.getItem(key)\n const parsed = raw ? (JSON.parse(raw) as unknown) : []\n return Array.isArray(parsed) ? (parsed as AutoApprovalRule[]).filter((r) => r && r.key) : []\n } catch {\n return []\n }\n}\n\nfunction write(rules: AutoApprovalRule[]): void {\n const key = storageKey()\n if (!key || typeof localStorage === 'undefined') return\n try {\n localStorage.setItem(key, JSON.stringify(rules))\n } catch {\n // Private mode / quota — the grant simply stays session-scoped.\n }\n}\n\n/** Grants that die with the tab (destructive tools, and private-mode fallback). */\nconst sessionGrants = new Set<string>()\n\nconst listeners = new Set<() => void>()\n\nfunction notify(): void {\n for (const listener of Array.from(listeners)) listener()\n}\n\n/** Re-render the revoke list when a grant is added or removed. */\nexport function subscribeAutoApprovals(listener: () => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\nexport function listAutoApprovals(): AutoApprovalRule[] {\n return read()\n}\n\nexport function isAutoApproved(toolName: string, scope?: string): boolean {\n const key = approvalKey(toolName, scope)\n if (sessionGrants.has(key)) return true\n if (isDestructiveTool(toolName)) return false\n return read().some((rule) => rule.key === key)\n}\n\nexport function grantAutoApproval(input: {\n toolName: string\n scope?: string\n label: string\n /** Force session-only. Destructive tools are session-only regardless. */\n session?: boolean\n}): void {\n const key = approvalKey(input.toolName, input.scope)\n if (input.session || isDestructiveTool(input.toolName)) {\n sessionGrants.add(key)\n notify()\n return\n }\n const rules = read().filter((rule) => rule.key !== key)\n rules.push({\n key,\n toolName: input.toolName,\n scope: input.scope,\n label: input.label,\n createdAt: new Date().toISOString(),\n })\n write(rules)\n notify()\n}\n\nexport function revokeAutoApproval(key: string): void {\n sessionGrants.delete(key)\n write(read().filter((rule) => rule.key !== key))\n notify()\n}\n\nexport function revokeAllAutoApprovals(): void {\n sessionGrants.clear()\n write([])\n notify()\n}\n","import type { ChatRecordLink } from '../stores/chat.store'\n\n/** Index of every record seen this conversation, so a confirmation card can\n * show \"Zeilma Cunha\" for an id the model passed. Never fetches — the label\n * already came back with the tool result that produced the id. */\n\nconst MAX_ENTRIES = 300\n\nconst refs = new Map<string, ChatRecordLink>()\n\n/** Fields a row uses to call itself something, best first. */\nconst LABEL_FIELDS = [\n 'name',\n 'full_name',\n 'fullName',\n 'title',\n 'label',\n 'client_name',\n 'customer_name',\n 'service_name',\n 'display_name',\n 'email',\n]\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\n/** True for the shape the model passes around as `client_id`, `service_id`, … */\nexport function looksLikeRecordId(value: unknown): value is string {\n return typeof value === 'string' && UUID.test(value)\n}\n\nfunction bestLabel(row: Record<string, unknown>, ref: Record<string, unknown>): string | null {\n if (typeof ref.label === 'string' && ref.label.trim()) return ref.label.trim()\n for (const field of LABEL_FIELDS) {\n const value = row[field]\n if (typeof value === 'string' && value.trim()) return value.trim()\n }\n return null\n}\n\nfunction remember(link: ChatRecordLink): void {\n const existing = refs.get(link.id)\n // Never downgrade — losing the archetype or the entity key turns a working\n // link into plain text.\n const merged: ChatRecordLink = {\n ...link,\n ...(!link.archetype && existing?.archetype\n ? { archetype: existing.archetype, ...(existing.kind ? { kind: existing.kind } : {}) }\n : {}),\n ...(!link.entityKey && existing?.entityKey ? { entityKey: existing.entityKey } : {}),\n }\n // Re-insert to move it to the end: eviction drops the least recently used.\n refs.delete(link.id)\n refs.set(link.id, merged)\n while (refs.size > MAX_ENTRIES) {\n const oldest = refs.keys().next().value\n if (oldest === undefined) break\n refs.delete(oldest)\n }\n}\n\n/** Walk a tool result and index every `{ ref }` in it, whatever shape the tool\n * answers in. */\nexport function rememberRecordRefs(content: string): void {\n let parsed: unknown\n try {\n parsed = JSON.parse(content)\n } catch {\n return // non-JSON tool result — nothing to index\n }\n walk(parsed, 0)\n}\n\nfunction walk(node: unknown, depth: number): void {\n if (depth > 6 || !node || typeof node !== 'object') return\n if (Array.isArray(node)) {\n for (const item of node) walk(item, depth + 1)\n return\n }\n const row = node as Record<string, unknown>\n const ref = row.ref\n if (ref && typeof ref === 'object' && !Array.isArray(ref)) {\n const r = ref as Record<string, unknown>\n const id = typeof r.id === 'string' ? r.id : null\n if (id) {\n const [archetype, kind] = String(r.archetype ?? '').split(':')\n const label = bestLabel(row, r)\n if (label) {\n remember({\n id,\n label,\n ...(archetype ? { archetype } : {}),\n ...(kind ? { kind } : {}),\n ...(typeof r.entityKey === 'string' ? { entityKey: r.entityKey } : {}),\n })\n }\n }\n } else if (looksLikeRecordId(row.id)) {\n // No ref: entities without an archetype still have an id and a name. No\n // archetype means no route, so the chip renders as plain text.\n const label = bestLabel(row, {})\n if (label) remember({ id: row.id, label })\n }\n for (const value of Object.values(row)) walk(value, depth + 1)\n}\n\n/**\n * Index what the SURFACE put in front of the model this turn.\n *\n * The ref index used to learn only from tool results, but the request also\n * carries the open screen (agent-surface snapshot) and the current route — both\n * of which name a record id. So the client would hand the model an id and then\n * refuse it: asked \"me fale sobre este lead\" on a lead page, the model called\n * openRecord with the id from the context and got back \"that id was not seen in\n * this conversation\". The system contradicting itself is worse than not\n * offering the id at all.\n */\nexport function rememberSurfaceRefs(snapshot: unknown): void {\n if (!snapshot) return\n const list = Array.isArray(snapshot) ? snapshot : [snapshot]\n for (const entry of list) {\n if (!entry || typeof entry !== 'object') continue\n const record = (entry as { record?: unknown }).record\n if (record && typeof record === 'object') {\n const r = record as { id?: unknown; entity?: unknown; label?: unknown; summary?: unknown }\n if (looksLikeRecordId(r.id)) {\n const label =\n (typeof r.label === 'string' && r.label.trim()) ||\n bestLabel((r.summary as Record<string, unknown>) ?? {}, {})\n if (label) {\n remember({\n id: r.id,\n label,\n ...(typeof r.entity === 'string' ? { entityKey: r.entity } : {}),\n })\n }\n }\n }\n // The rest of the snapshot is arbitrary surface state — the same walker\n // that reads tool results finds any {ref} or {id,name} rows inside it.\n walk(entry, 0)\n }\n}\n\n/** Null means \"show the raw value\" — never a guessed name. */\nexport function lookupRecordRef(id: unknown): ChatRecordLink | null {\n if (!looksLikeRecordId(id)) return null\n return refs.get(id) ?? null\n}\n\n/** A new thread starts with no memory of the old one's records. */\nexport function clearRecordRefs(): void {\n refs.clear()\n}\n\n/**\n * The records a reply is ABOUT — write results always, single-match reads too.\n * Narrower than the index above on purpose: everything gets remembered, only\n * these earn a goto button.\n */\nexport function extractRecordLinks(content: string): ChatRecordLink[] {\n let parsed: Record<string, unknown>\n try {\n parsed = JSON.parse(content) as Record<string, unknown>\n } catch {\n return []\n }\n const links: ChatRecordLink[] = []\n const push = (ref: unknown, fallbackLabel?: unknown) => {\n if (!ref || typeof ref !== 'object') return\n const r = ref as { id?: string; label?: string; archetype?: string; entityKey?: string; path?: string }\n if (!r.id) return\n const [archetype, kind] = (r.archetype ?? '').split(':')\n links.push({\n id: r.id,\n label: String(r.label ?? fallbackLabel ?? 'registro'),\n ...(archetype ? { archetype } : {}),\n ...(kind ? { kind } : {}),\n ...(r.entityKey ? { entityKey: r.entityKey } : {}),\n // A destination the route map cannot derive (a settings tab) — the only\n // way a non-record result gets a working button.\n ...(typeof r.path === 'string' && r.path.startsWith('/') ? { path: r.path } : {}),\n })\n }\n const record = parsed.record as Record<string, unknown> | undefined\n push(parsed.ref, record?.name)\n push(record?.ref, record?.client_name ?? record?.service_name)\n const singleFrom = (rows: unknown) => {\n if (Array.isArray(rows) && rows.length === 1) {\n const row = rows[0] as Record<string, unknown>\n push(row.ref, row.name)\n }\n }\n singleFrom(parsed.records)\n for (const m of Array.isArray(parsed.matches) ? parsed.matches : []) {\n singleFrom((m as Record<string, unknown>).records)\n }\n return links\n}\n\n/** Same id twice is the same button. */\nexport function dedupeLinks(links: ChatRecordLink[]): ChatRecordLink[] {\n const seen = new Map<string, ChatRecordLink>()\n for (const l of links) if (!seen.has(l.id)) seen.set(l.id, l)\n return Array.from(seen.values()).slice(0, 3)\n}\n","/**\n * Which colleague a past conversation belongs to.\n *\n * The broker stores one agent per project, so a conversation it hands back\n * carries no idea of who was speaking. Until it does, the surface remembers:\n * every thread the user starts is tagged here as it gets its server id, and the\n * history drawer reads the tag to show one colleague's conversations at a time.\n *\n * Local by design and deliberately lossy — a thread from another browser, or\n * from before the cast existed, has no tag and belongs to nobody. The drawer\n * shows those to the host rather than hiding them.\n */\n\nconst KEY = 'fayz.agent.thread-roles'\nconst CAP = 200\n\nfunction read(): Record<string, string> {\n if (typeof localStorage === 'undefined') return {}\n try {\n const raw = localStorage.getItem(KEY)\n return raw ? (JSON.parse(raw) as Record<string, string>) : {}\n } catch {\n return {}\n }\n}\n\nfunction write(map: Record<string, string>): void {\n if (typeof localStorage === 'undefined') return\n try {\n // Oldest tags fall off first — the map is a convenience, not a record.\n const entries = Object.entries(map)\n const kept = entries.length > CAP ? entries.slice(entries.length - CAP) : entries\n localStorage.setItem(KEY, JSON.stringify(Object.fromEntries(kept)))\n } catch {\n /* quota or private mode — the drawer just stops filtering */\n }\n}\n\nexport function tagConversation(conversationId: string, role: string): void {\n const map = read()\n if (map[conversationId] === role) return\n map[conversationId] = role\n write(map)\n}\n\nexport function roleOfConversation(conversationId: string): string | null {\n return read()[conversationId] ?? null\n}\n","// ---------------------------------------------------------------------------\n// What the model MEANT to do, typed out instead of done.\n//\n// A model that reaches for a tool it cannot see writes the call as prose:\n// `{requestMenuPhotos}`, or a button drawn in brackets — `[Subir foto do\n// cardápio]`. Both read to the user as a broken widget, and clicking them does\n// nothing because there is nothing there.\n//\n// The real fix is the tool being available (that is a catalogue problem, fixed\n// where the catalogue is built). This is the floor: whatever slips through is\n// not shown as if it were an interface.\n// ---------------------------------------------------------------------------\n\n/** `{toolName}`, `{ toolName() }`, `{toolName(arg)}` — never prose. */\nconst TOOL_IN_BRACES = /\\{\\s*[a-z][A-Za-z0-9_]{2,}\\s*(?:\\([^()]*\\))?\\s*\\}/g\n\n/** A line that is nothing but `[Label]` — a button that was never rendered.\n * Markdown links (`[a](b)`), images and task boxes are left alone. */\nconst FAKE_BUTTON_LINE = /^[ \\t]*\\[[^\\][\\n]{2,60}\\][ \\t]*$(?!\\()/gm\n\n/** Split on fenced code so a question ABOUT JSON keeps its braces. */\nfunction mapOutsideCode(text: string, transform: (chunk: string) => string): string {\n return text\n .split(/(```[\\s\\S]*?```|`[^`\\n]*`)/g)\n .map((chunk, index) => (index % 2 === 1 ? chunk : transform(chunk)))\n .join('')\n}\n\n/**\n * Strip tool-call artifacts from assistant text.\n *\n * Runs on every delta, so it must be cheap and must never mangle a partial\n * token: an unclosed `{requestMenuPhotos` matches nothing and simply survives\n * until the closing brace arrives.\n */\nexport function sanitizeAssistantText(text: string): string {\n if (!text || (!text.includes('{') && !text.includes('['))) return text\n\n const cleaned = mapOutsideCode(text, (chunk) =>\n chunk.replace(TOOL_IN_BRACES, '').replace(FAKE_BUTTON_LINE, ''),\n )\n if (cleaned === text) return text\n\n // Removing a line leaves the hole it was in: collapse the run of blank lines\n // it created, and drop trailing space left mid-sentence.\n return cleaned\n .replace(/[ \\t]+$/gm, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .replace(/^\\n+/, '')\n}\n","import { create } from 'zustand'\nimport { isAutoApproved, isDestructiveTool } from '../lib/agent-approvals'\nimport { clearRecordRefs } from '../lib/record-refs'\nimport { clearTurnReportFiles } from '../lib/report-attachments'\nimport { resetLoadedTools } from '../lib/tool-search'\nimport { tagConversation } from '../lib/agent-threads'\nimport {\n clearAttachmentRegistry,\n detectKind,\n parseAttachment,\n registerAttachment,\n releaseAttachment,\n type AttachmentDigest,\n type AttachmentKind,\n} from '../lib/attachments'\nimport type { ConnectorSuggestionState } from '../components/connect/connector-search'\nimport { sanitizeAssistantText } from '../lib/assistant-text'\n\n/** A record the reply concerns — rendered as a \"goto\" button under the\n * bubble. Semantic ref only (id + archetype/kind); the SURFACE resolves the\n * URL against its own route map (agents.md: a missing link is correct, an\n * invented one is a dead end). */\nexport interface ChatRecordLink {\n id: string\n label: string\n archetype?: string\n kind?: string\n /** The route a non-archetype entity has — without it a product resolves to\n * nothing and the goto button silently disappears. */\n entityKey?: string\n /** An explicit destination, for what the entity route map cannot resolve: a\n * settings tab, a plugin screen. Wins over the archetype lookup. */\n path?: string\n}\n\n/**\n * An integration the reply is about, rendered as a card under the bubble.\n *\n * A sibling of {@link ChatRecordLink}, not a new channel: same \"the tool result\n * decorates the message\" mechanism, one step richer because a connector needs\n * its mark, its state and — only when there is somewhere to go — a button.\n * `route` is the whole safety property: it is present for `connected`/`ready`\n * and absent for the two inert states, so a card that cannot be acted on cannot\n * render something to press.\n */\nexport interface ChatConnectorCard {\n kind: 'connector'\n slug: string\n /** Connector id, when a definition backs it — opens the journey directly. */\n id?: string\n name: string\n description?: string\n /** Raw `ConnectorCategory`; the badge is resolved through i18n. */\n category?: string\n icon?: string\n logoUrl?: string\n /** The tool's own vocabulary, not a copy of it — see {@link ConnectorSuggestionState}. */\n state: ConnectorSuggestionState\n /** Module that hosts it — what \"requer o módulo X\" names. */\n moduleId?: string\n /** The screen a human finishes it on. Only ever set for a state that has one. */\n route?: string\n}\n\n/**\n * A colleague the tenant has not hired, offered under the reply.\n *\n * The third sibling of {@link ChatRecordLink} and {@link ChatConnectorCard}, and\n * the honest answer to a request whose tools are absent: the agent cannot do\n * growth work without the growth toolset, so instead of improvising it names\n * who does that job and what hiring them would buy.\n */\n\n/**\n * \"Qual destes?\" — the doubt a tool could not settle, handed to the reader.\n *\n * A tool that resolves words to records (a product from what a customer typed,\n * a client from a first name) is right most of the time and occasionally torn\n * between two rows. Torn used to become PROSE: the model printed the candidates\n * with their SKUs, prices and stock as a numbered list and asked in a sentence.\n * That is a database dump where a question belongs, and it asks the reader to\n * answer by typing a name back.\n *\n * So the tool says it is unsure IN ITS RESULT and this renders the question as\n * rows to tap. Deliberately generic — it names no entity and no plugin, because\n * every resolver has this moment.\n *\n * The tool decides WHEN. A resolver confident in its top hit must not emit one:\n * a card for something obvious is a click charged for nothing.\n */\nexport interface ChatChoiceCard {\n kind: 'choice'\n /** Distinguishes two questions in one reply (\"costela\" and \"barbecue\"). */\n id: string\n /** What is being asked, in the reader's language. */\n question: string\n options: Array<{\n /** Sent back when the row is tapped, so the model reads an unambiguous id. */\n id: string\n label: string\n /** The line under the label — price, stock, whatever tells them apart. */\n meta?: string\n }>\n}\n\nexport type ChatCard = ChatConnectorCard | ChatChoiceCard\n\n/** Identity for dedupe: a connector is its slug, a question its own id. The\n * kind prefix keeps the id spaces from colliding. */\nexport function chatCardKey(card: ChatCard): string {\n if (card.kind === 'connector') return `connector:${card.slug}`\n return `choice:${card.id}`\n}\n\nexport interface ChatToolCall {\n name: string\n /** Pretty-printed arguments (may be empty). */\n args: string\n /** Result content, truncated for display. */\n result?: string\n /** The write ran without asking because a standing grant covered it — the\n * trace says so, so an auto-approved action is never a silent one. */\n autoApproved?: boolean\n /** The user (or a guard) turned this call down. */\n declined?: boolean\n}\n\nexport interface ChatMessage {\n id: string\n role: 'user' | 'assistant'\n content: string\n timestamp: string\n /** Tool calls this assistant reply made, accumulated across rounds —\n * rendered as an expandable trace under the bubble. */\n toolCalls?: ChatToolCall[]\n /** Records to offer as navigation buttons under the reply. */\n links?: ChatRecordLink[]\n /** Integrations the reply found, rendered as cards under it. */\n cards?: ChatCard[]\n /** Files sent with this turn. Name and shape only — the bytes stayed here. */\n attachments?: Array<{\n name: string\n kind: AttachmentKind\n rowCount?: number\n /** Object URL for an image the user handed over — a photo IS its own label. */\n previewUrl?: string\n }>\n /** Which colleague said it — the avatar and the name on the bubble. Absent on\n * an app with no cast, and on every message written before it had one. */\n agentKey?: string\n /** Renders as a divider instead of a bubble: the baton passing, in the open.\n * A hand-off the user cannot see is a personality change out of nowhere. */\n notice?: { kind: 'handoff'; from?: string; to: string; topic?: string }\n}\n\n/**\n * A write the agent wants to perform, parked until the human decides. The turn\n * loop awaits the promise this action's resolver settles — the chat composer\n * stays frozen behind the card, mirroring the broker's PENDING_CONFIRMATION\n * step for server-plane tools.\n */\nexport interface PendingAgentAction {\n id: string\n toolName: string\n /** Human title when the caller has one. A tool's `description` is written\n * for the model and must never be used here. */\n title?: string\n params: Record<string, unknown>\n /** 'client' = executed locally after approval; 'server' = approval is sent\n * back to the broker as confirmAction. */\n plane: 'client' | 'server'\n /**\n * A PARTIAL write: only the fields present are changing, and a blank one means\n * \"leave it as it is\". Set on updates, where the record already satisfies its\n * own required fields — an update of a product's cost asked for the name\n * again, blocked on it, and would have written back every declared default\n * along the way.\n */\n partial?: boolean\n /** Entity this write targets. Auto-approval is remembered per tool+scope, so\n * \"always create clients\" never becomes \"always create anything\". */\n scope?: string\n /** Human label for the remembered rule (\"Criar Cliente\"). */\n scopeLabel?: string\n /** Removes or voids data — grants for it never outlive the tab. */\n destructive?: boolean\n /** This action is decided EVERY time: no \"always allow\" button, and a\n * standing grant from an earlier turn does not cover it. For writes whose\n * blast radius is other people's access, not one record. */\n noAutoApproval?: boolean\n /** The target entity's declared fields, so the card can let the user correct\n * what the model guessed and fill what it left out. */\n fields?: PendingActionField[]\n}\n\nexport interface PendingActionField {\n key: string\n label?: string\n /** The entity's own `FieldType` — the card renders the matching control. */\n type?: string\n required?: boolean\n options?: Array<{ value: string; label?: string }>\n placeholder?: string\n hint?: string\n min?: number\n max?: number\n /** The entity's declared default, seeded into the card when the model left the\n * field out — so a boolean like `active` shows on without anyone typing it. */\n default?: unknown\n /** Shown, never edited: computed values, relations needing an option load,\n * and anything the database fills in on its own. */\n readOnly?: boolean\n}\n\n/**\n * A file the user dropped on the composer, waiting for the next turn. The\n * BYTES are not here — they stay in the attachments registry and never leave\n * the browser; what travels with the turn is `digest`.\n */\nexport interface ChatAttachment {\n id: string\n name: string\n size: number\n mime: string\n kind: AttachmentKind\n status: 'parsing' | 'ready' | 'error'\n digest?: AttachmentDigest\n /** Object URL for an image, so the chip shows the picture instead of a\n * paperclip. Revoked when the chip goes. */\n previewUrl?: string\n /** i18n suffix under `chat.attachments.error.*` — the chip says what failed. */\n error?: string\n}\n\n/** A stored file referenced by URL, not by bytes. */\nexport interface ComposerMedia {\n url: string\n name: string\n mime?: string\n}\n\n/**\n * A file the assistant asked for, and what to do with it.\n *\n * `onFiles` belongs to the tool that asked — the chat is the doorway, not the\n * destination. That is what lets a photo be READ (menu import, receipt capture)\n * instead of merely mentioned: the bytes go to the screen that knows what they\n * are, never into the turn.\n */\nexport interface UploadRequest {\n id: string\n /** The button's own words: \"Subir foto do cardápio\". */\n label: string\n /** One line under it — what a good photo looks like. */\n description?: string\n accept?: string\n multiple?: boolean\n /** 'environment' opens the rear camera on a phone; ignored on desktop. */\n capture?: 'environment' | 'user'\n onFiles: (files: File[]) => void | Promise<void>\n status?: 'waiting' | 'working'\n /**\n * What the user has handed over so far, held in the card.\n *\n * They accumulate instead of firing on the first pick: a menu is several\n * pages, and reading page one while the user is still photographing page two\n * would split one cardápio into two imports.\n */\n files: StagedUpload[]\n}\n\nexport interface StagedUpload {\n file: File\n /** Object URL for the thumbnail. Revoked when the file leaves the card. */\n previewUrl?: string\n}\n\n/** Nothing to revoke for a file that was never an image. */\nfunction revokePreview(url?: string): void {\n if (url) URL.revokeObjectURL(url)\n}\n\n/** Does this file answer the request? `image/*`, `.pdf`, `image/png` — the\n * same vocabulary the input's `accept` takes. */\nfunction matchesAccept(file: File, accept?: string): boolean {\n if (!accept) return true\n return accept.split(',').map((rule) => rule.trim().toLowerCase()).some((rule) => {\n if (!rule) return false\n if (rule.startsWith('.')) return file.name.toLowerCase().endsWith(rule)\n if (rule.endsWith('/*')) return file.type.toLowerCase().startsWith(rule.slice(0, -1))\n return file.type.toLowerCase() === rule\n })\n}\n\n/** Beyond this the turn is an import job, not a question. */\nconst MAX_ATTACHMENTS = 5\nconst MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024\n\n/** What the card sends back: the decision, plus the values as edited. */\nexport interface PendingActionResult {\n approved: boolean\n values?: Record<string, unknown>\n}\n\ninterface ChatState {\n messages: ChatMessage[]\n isOpen: boolean\n isStreaming: boolean\n setOpen: (open: boolean) => void\n toggleOpen: () => void\n addMessage: (message: ChatMessage) => void\n /** Rewrite ONE bubble by id — how a locally-said line types itself in. */\n setMessageContent: (id: string, content: string) => void\n updateLastAssistant: (content: string) => void\n setStreaming: (streaming: boolean) => void\n setConversationId: (id: string | null) => void\n reset: () => void\n /**\n * Server-side thread id when talking to a Fayz agent. History lives in Fayz,\n * so subsequent turns only send the new message plus this id.\n */\n conversationId: string | null\n /**\n * Whose thread is loaded right now, and the parked transcripts of everyone\n * else. Each colleague keeps their own conversation — you don't ask the\n * front desk something and find the growth specialist mid-sentence.\n *\n * Empty on an app with no cast: `threadRole` stays null and the store behaves\n * exactly as it did with a single assistant.\n */\n threadRole: string | null\n threads: Record<string, { messages: ChatMessage[]; conversationId: string | null }>\n /** Park the open thread and load `role`'s, appending `seed` to it. */\n switchThread: (role: string, seed?: ChatMessage) => void\n /** The write awaiting human confirmation (renders the ConfirmActionCard). */\n pendingAction: PendingAgentAction | null\n /**\n * Writes of the SAME tool queued BEHIND `pendingAction`.\n *\n * Not the raw queue length: the queue can hold a create and a delete\n * together, and only siblings can honestly be answered in one press. This is\n * the number `resolveAllPending` would settle, minus the head.\n */\n pendingSameTool: number\n /** Park an action + register its decision resolver. Resolves immediately\n * when a standing auto-approval already covers it. */\n requestConfirmation: (action: PendingAgentAction) => Promise<PendingActionResult>\n /** Card buttons: settle the parked action, carrying the edited values. */\n resolvePendingAction: (approved: boolean, values?: Record<string, unknown>) => void\n /** Settle the head AND every queued write of the same tool, in one answer. */\n resolveAllPending: (approved: boolean) => void\n /** Tool names auto-approved during the turn being rendered — the trace marks\n * them so the user can see what ran unattended. */\n autoApprovedTools: string[]\n /** Speak short confirmations back. Persisted per browser. */\n voiceReplies: boolean\n setVoiceReplies: (enabled: boolean) => void\n /** Text handed to the composer for the user to check, edit and send. A\n * suggestion fills the field; it never sends on the user's behalf. */\n draft: string\n setDraft: (text: string) => void\n /** Files staged for the next turn, each with its parse state. */\n attachments: ChatAttachment[]\n /** Stage files: registers the bytes locally, then parses each into a digest. */\n addAttachments: (files: File[]) => Promise<void>\n removeAttachment: (id: string) => void\n clearAttachments: () => void\n /** Hand the turn its digests and unstage everything (bytes included). */\n consumePendingAttachments: () => AttachmentDigest[]\n /**\n * A file the user is already looking at, attached by ADDRESS.\n *\n * Distinct from `attachments`, which stage bytes the browser has to read and\n * digest. This one is a photo the app itself stored and can serve: sending\n * the URL lets the model fetch the original, at full resolution, instead of\n * us inlining megabytes of base64 into the turn — and a picture that came\n * from our own storage never needed to be re-uploaded to be talked about.\n */\n composerMedia: ComposerMedia | null\n attachMedia: (media: ComposerMedia) => void\n clearComposerMedia: () => void\n /**\n * The assistant asked for a file, and the composer offers it as one big\n * button (\"Subir foto do cardápio\").\n *\n * This is not an attachment: the bytes never enter the conversation. The tool\n * that asked hands in `onFiles`, and the files go straight there — which is\n * the only way a photo reaches anything today, since a turn carries a digest\n * of a file, never its pixels.\n */\n uploadRequest: UploadRequest | null\n requestUpload: (request: Omit<UploadRequest, 'id' | 'status' | 'files'>) => string\n /** Stage files in the card — the user can still add more or drop one. */\n addUploadFiles: (files: File[]) => void\n removeUploadFile: (index: number) => void\n /** Hands everything staged to whoever asked. */\n submitUpload: () => Promise<void>\n clearUploadRequest: () => void\n /**\n * The one door for files arriving from anywhere — the composer's `+`, a\n * paste, a drop on the transcript.\n *\n * While a request is up, a matching file belongs to IT: staging a menu photo\n * as a chat attachment would send the assistant a digest with no pixels in\n * it, which is the one thing that cannot answer \"manda a foto\".\n */\n receiveFiles: (files: File[]) => void\n /** Explicit delegation (\"resolver com IA\"): the user already said go, so the\n * conversation sends this as soon as it can. Distinct from `draft`, which\n * never sends on the user's behalf. */\n queuedPrompt: string | null\n queuePrompt: (text: string, filesToRead?: File[]) => void\n consumeQueuedPrompt: () => string | null\n /**\n * Files handed over TO BE READ with the queued prompt — bytes, not digests.\n *\n * Its own field, not a flag on a staged attachment: the composer's `+` stages\n * files to be DESCRIBED, and the difference between describing a file and\n * shipping it to a model is the whole privacy boundary. Two doors, so nobody\n * walks through the wrong one by editing an object.\n */\n queuedFilesToRead: File[] | null\n consumeQueuedFilesToRead: () => File[] | null\n /**\n * O que o campo sugere quando uma tela delega um assunto ao assistente\n * (\"Ex.: receita por mesa no jantar\"). Dica, não rascunho: a tela sabe do que\n * se trata, o usuário ainda escreve o pedido. Some no primeiro envio — a\n * partir daí a conversa tem assunto próprio.\n */\n composerHint: string | null\n setComposerHint: (hint: string | null) => void\n /** O painel foi aberto por um handoff — o campo assume o cursor assim que\n * existir. Quem clicou \"Criar com IA\" já está escrevendo o pedido; obrigar\n * um segundo clique no campo é cobrar duas vezes pela mesma decisão. */\n composerFocusRequested: boolean\n requestComposerFocus: () => void\n consumeComposerFocus: () => void\n /** Suggestions panel is pinned open by the user (works mid-conversation). */\n suggestionsOpen: boolean\n setSuggestionsOpen: (open: boolean) => void\n /** \"Talk\" was tapped outside the panel — the composer starts listening as\n * soon as it mounts, so one tap goes from anywhere to speaking. */\n voiceStartRequested: boolean\n requestVoiceStart: () => void\n consumeVoiceStart: () => void\n /** Tool names executing right now — rendered as live activity chips. */\n activeTools: string[]\n setActiveTools: (names: string[]) => void\n /** Record the tool calls an assistant reply made (expandable per-message trace). */\n appendToolCallsToLastAssistant: (calls: ChatToolCall[]) => void\n /** Attach goto-record buttons to the reply (deduped by id, capped). */\n appendLinksToLastAssistant: (links: ChatRecordLink[]) => void\n /** Attach connector cards to the reply (deduped by slug, capped). */\n appendCardsToLastAssistant: (cards: ChatCard[]) => void\n /** The signed-in user's threads (history drawer). */\n conversations: Array<{ id: string; title: string | null; updatedAt: string }>\n /** First fetch settled — before this the panel shows a skeleton, not \"empty\". */\n conversationsLoaded: boolean\n setConversations: (rows: Array<{ id: string; title: string | null; updatedAt: string }>) => void\n /** Replace the transcript wholesale (resuming a past conversation). */\n setMessages: (messages: ChatMessage[]) => void\n}\n\n// Module-level so the resolver never round-trips through React state.\n/**\n * Writes waiting on a human, in the order they were asked for.\n *\n * This used to be a SINGLE resolver slot, and a newer request declined the one\n * before it as stale. That is right for a request abandoned by a superseded\n * turn, and catastrophically wrong for the common case: a model that asks for\n * six writes in ONE round (six `createRecordHere` calls for six products) has\n * every request but the last auto-declined before anybody sees a card. The\n * screen showed five struck-through rows and one green tick, and the user never\n * refused anything.\n *\n * So they queue. The head is what `pendingAction` renders; answering it settles\n * that one and brings the next forward.\n */\ninterface PendingEntry {\n action: PendingAgentAction\n resolve: (result: PendingActionResult) => void\n}\nlet pendingQueue: PendingEntry[] = []\n\n/** What the card renders: the head, and how many siblings sit behind it. */\nfunction headState(): { pendingAction: PendingAgentAction | null; pendingSameTool: number } {\n const head = pendingQueue[0]\n if (!head) return { pendingAction: null, pendingSameTool: 0 }\n return {\n pendingAction: head.action,\n // Siblings only. A queue of \"create a product\" + \"delete a client\" is two\n // decisions, and offering one button for both is how the wrong thing gets\n // approved by someone answering the card in front of them.\n pendingSameTool: pendingQueue.filter((e, i) => i > 0 && e.action.toolName === head.action.toolName).length,\n }\n}\n\n/** Decline everything still waiting — a reset, a thread switch, a new turn. */\nfunction declineAllPending(): void {\n const queued = pendingQueue\n pendingQueue = []\n for (const entry of queued) entry.resolve({ approved: false })\n}\n\nconst VOICE_REPLIES_KEY = 'fayz.agent.voice-replies'\n\nfunction readVoiceRepliesPref(): boolean {\n if (typeof localStorage === 'undefined') return false\n try {\n return localStorage.getItem(VOICE_REPLIES_KEY) === '1'\n } catch {\n return false\n }\n}\n\nexport const useChatStore = create<ChatState>((set, get) => ({\n messages: [],\n isOpen: false,\n isStreaming: false,\n conversationId: null,\n\n // Fechar o painel encerra o handoff junto: uma dica sobrevivente devolveria\n // \"Ex.: receita por mesa\" a quem abriu o assistente para outra coisa.\n setOpen: (open) => set(open ? { isOpen: true } : { isOpen: false, composerHint: null, composerFocusRequested: false }),\n toggleOpen: () => set((s) => ({ isOpen: !s.isOpen })),\n\n // Stamped with whoever is on duty, here rather than at each call site: every\n // message in the transcript needs a face, and a caller that forgets leaves an\n // anonymous bubble in the middle of a conversation with a named colleague.\n addMessage: (message) =>\n set((s) => ({\n messages: [...s.messages, s.threadRole && !message.agentKey ? { ...message, agentKey: s.threadRole } : message],\n })),\n\n setMessageContent: (id, content) =>\n set((s) => {\n const i = s.messages.findIndex((m) => m.id === id)\n // The bubble can be gone — a reset, a thread switch, a message the user\n // cleared while a line was still being typed into it.\n if (i < 0) return s\n const msgs = [...s.messages]\n msgs[i] = { ...msgs[i]!, content: sanitizeAssistantText(content) }\n return { messages: msgs }\n }),\n\n updateLastAssistant: (content) =>\n set((s) => {\n const msgs = [...s.messages]\n const lastIdx = msgs.length - 1\n if (lastIdx >= 0 && msgs[lastIdx].role === 'assistant') {\n // The one place every assistant word passes through, streaming deltas\n // included — so a tool call the model typed out instead of making\n // (`{requestMenuPhotos}`) is dropped once, here.\n msgs[lastIdx] = { ...msgs[lastIdx], content: sanitizeAssistantText(content) }\n }\n return { messages: msgs }\n }),\n\n setStreaming: (isStreaming) => set({ isStreaming }),\n setConversationId: (conversationId) => {\n // The broker knows one agent per project, so it cannot tell us whose thread\n // this was. Remember it here, while we still know.\n const role = get().threadRole\n if (conversationId && role) tagConversation(conversationId, role)\n set({ conversationId })\n },\n\n threadRole: null,\n threads: {},\n switchThread: (role, seed) => {\n const s = get()\n if (s.threadRole === role && !seed) return\n // A parked action belongs to the conversation it was raised in; carrying it\n // into someone else's thread would ask the wrong colleague to sign.\n declineAllPending()\n // Staged bytes belong to the thread that staged them, same as on reset().\n clearAttachmentRegistry()\n clearTurnReportFiles()\n const threads = { ...s.threads }\n if (s.threadRole) {\n threads[s.threadRole] = { messages: s.messages, conversationId: s.conversationId }\n }\n const parked = threads[role] ?? { messages: [], conversationId: null }\n // A thread that belonged to NOBODY — everything said before a cast was\n // registered — is adopted by whoever comes on duty first. Dropping it would\n // silently lose the opening of the conversation.\n const target =\n !s.threadRole && s.messages.length\n ? {\n messages: [...s.messages, ...parked.messages],\n conversationId: s.conversationId ?? parked.conversationId,\n }\n : parked\n delete threads[role]\n set({\n threads,\n threadRole: role,\n messages: seed ? [...target.messages, seed] : target.messages,\n conversationId: target.conversationId,\n pendingAction: null,\n pendingSameTool: 0,\n activeTools: [],\n autoApprovedTools: [],\n attachments: [],\n })\n },\n\n reset: () => {\n declineAllPending()\n clearRecordRefs()\n // Tools the agent asked for belong to the conversation that needed them —\n // a new one starts from the carry-on set again.\n resetLoadedTools()\n // The bytes belong to the thread that staged them — a new conversation\n // must not inherit someone's client list.\n clearAttachmentRegistry()\n clearTurnReportFiles()\n set({\n messages: [],\n isStreaming: false,\n conversationId: null,\n pendingAction: null,\n pendingSameTool: 0,\n activeTools: [],\n autoApprovedTools: [],\n attachments: [],\n // suggestionsOpen is a standing preference, not thread state.\n })\n },\n\n pendingAction: null,\n pendingSameTool: 0,\n autoApprovedTools: [],\n requestConfirmation: (action) => {\n const destructive = action.destructive ?? isDestructiveTool(action.toolName)\n // Standing grant covers this exact tool+scope: run it, but record it so\n // the transcript still shows the write happened and on whose authority.\n if (!action.noAutoApproval && isAutoApproved(action.toolName, action.scope)) {\n set((s) => ({\n autoApprovedTools: s.autoApprovedTools.includes(action.toolName)\n ? s.autoApprovedTools\n : [...s.autoApprovedTools, action.toolName],\n }))\n return Promise.resolve({ approved: true })\n }\n return new Promise<PendingActionResult>((resolve) => {\n pendingQueue = [...pendingQueue, { action: { ...action, destructive }, resolve }]\n set(headState())\n })\n },\n resolvePendingAction: (approved, values) => {\n const [head, ...rest] = pendingQueue\n if (!head) return\n pendingQueue = rest\n set(headState())\n // Resolved AFTER the store is updated: the resolver lets the tool loop\n // continue, and that loop can queue the next write synchronously.\n head.resolve({ approved, values })\n },\n resolveAllPending: (approved) => {\n // \"Same\" is the tool, not the arguments — six creates of six different\n // products is exactly the case worth answering once. A different tool in\n // the queue keeps its own card: approving \"cadastre um produto\" is not\n // approval to also delete something.\n const head = pendingQueue[0]\n if (!head) return\n const toolName = head.action.toolName\n const matched: PendingEntry[] = []\n const kept: PendingEntry[] = []\n for (const entry of pendingQueue) {\n (entry.action.toolName === toolName ? matched : kept).push(entry)\n }\n pendingQueue = kept\n set(headState())\n // Values are NOT carried across: they were edited for the head's record and\n // would overwrite five other products with the first one's name.\n for (const entry of matched) entry.resolve({ approved })\n },\n\n voiceReplies: readVoiceRepliesPref(),\n setVoiceReplies: (enabled) => {\n try {\n localStorage?.setItem(VOICE_REPLIES_KEY, enabled ? '1' : '0')\n } catch {\n /* preference stays session-only */\n }\n set({ voiceReplies: enabled })\n },\n draft: '',\n setDraft: (draft) => set({ draft }),\n\n attachments: [],\n addAttachments: async (files) => {\n const room = MAX_ATTACHMENTS - get().attachments.length\n if (room <= 0) return\n const staged = files.slice(0, room).map((file) => ({\n file,\n attachment: {\n id: registerAttachment(file),\n name: file.name,\n size: file.size,\n mime: file.type || 'application/octet-stream',\n kind: detectKind(file),\n // The picture IS the label for an image: \"image.png · 1.3 MB\" tells the\n // user nothing about which photo they just attached.\n ...(file.type.startsWith('image/') ? { previewUrl: URL.createObjectURL(file) } : {}),\n status: file.size > MAX_ATTACHMENT_BYTES ? 'error' : 'parsing',\n ...(file.size > MAX_ATTACHMENT_BYTES ? { error: 'tooLarge' } : {}),\n } as ChatAttachment,\n }))\n if (!staged.length) return\n set((s) => ({ attachments: [...s.attachments, ...staged.map((e) => e.attachment)] }))\n\n const settle = (id: string, patch: Partial<ChatAttachment>) =>\n set((s) => ({\n attachments: s.attachments.map((a) => (a.id === id ? { ...a, ...patch } : a)),\n }))\n\n await Promise.all(\n staged.map(async ({ file, attachment }) => {\n if (attachment.status === 'error') return\n try {\n settle(attachment.id, { status: 'ready', digest: await parseAttachment(file, attachment.id) })\n } catch {\n settle(attachment.id, { status: 'error', error: 'unreadable' })\n }\n }),\n )\n },\n removeAttachment: (id) => {\n releaseAttachment(id)\n revokePreview(get().attachments.find((a) => a.id === id)?.previewUrl)\n set((s) => ({ attachments: s.attachments.filter((a) => a.id !== id) }))\n },\n clearAttachments: () => {\n for (const attachment of get().attachments) {\n releaseAttachment(attachment.id)\n revokePreview(attachment.previewUrl)\n }\n set({ attachments: [] })\n },\n // Sending clears the CHIPS, never the registry. The file is what the rest of\n // the conversation is about: released here, \"ta certo isso ai\" two turns later\n // reached proposeImport as \"there is no readable file attached\". The registry\n // is emptied when the thread is — reset() and setMessages() do it.\n consumePendingAttachments: () => {\n const staged = get().attachments\n if (!staged.length) return []\n const digests = staged.flatMap((a) => (a.status === 'ready' && a.digest ? [a.digest] : []))\n set({ attachments: [] })\n return digests\n },\n\n composerMedia: null,\n attachMedia: (composerMedia) => set({ composerMedia }),\n clearComposerMedia: () => set({ composerMedia: null }),\n\n uploadRequest: null,\n requestUpload: (request) => {\n const id = `upload-${Date.now()}`\n // Opening the panel is part of the ask: a button nobody can see is not a\n // request, and the assistant has just said it is waiting for the file.\n set({ uploadRequest: { ...request, id, status: 'waiting', files: [] }, isOpen: true })\n return id\n },\n\n addUploadFiles: (files) => {\n const request = get().uploadRequest\n if (!request || files.length === 0) return\n const staged = (request.multiple ? files : files.slice(0, 1)).map((file) => ({\n file,\n previewUrl: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,\n }))\n const kept = request.multiple ? [...request.files, ...staged] : staged\n if (!request.multiple) for (const previous of request.files) revokePreview(previous.previewUrl)\n set({ uploadRequest: { ...request, files: kept } })\n },\n\n removeUploadFile: (index) => {\n const request = get().uploadRequest\n if (!request) return\n revokePreview(request.files[index]?.previewUrl)\n set({ uploadRequest: { ...request, files: request.files.filter((_, i) => i !== index) } })\n },\n\n submitUpload: async () => {\n const request = get().uploadRequest\n if (!request || request.files.length === 0) return\n set({ uploadRequest: { ...request, status: 'working' } })\n try {\n await request.onFiles(request.files.map((staged) => staged.file))\n } finally {\n for (const staged of request.files) revokePreview(staged.previewUrl)\n // The card goes either way: the handler owns the outcome from here, and\n // a button still sitting there reads as \"it did not take\".\n if (get().uploadRequest?.id === request.id) set({ uploadRequest: null })\n }\n },\n\n clearUploadRequest: () => {\n const request = get().uploadRequest\n if (request) for (const staged of request.files) revokePreview(staged.previewUrl)\n set({ uploadRequest: null })\n },\n\n receiveFiles: (files) => {\n if (files.length === 0) return\n const request = get().uploadRequest\n if (request && request.status !== 'working') {\n const wanted = files.filter((file) => matchesAccept(file, request.accept))\n if (wanted.length) {\n get().addUploadFiles(wanted)\n const rest = files.filter((file) => !wanted.includes(file))\n if (rest.length) void get().addAttachments(rest)\n return\n }\n }\n void get().addAttachments(files)\n },\n\n queuedPrompt: null,\n queuedFilesToRead: null,\n queuePrompt: (queuedPrompt, filesToRead) =>\n set({ queuedPrompt, queuedFilesToRead: filesToRead?.length ? filesToRead : null, isOpen: true }),\n consumeQueuedFilesToRead: () => {\n const files = get().queuedFilesToRead\n if (files) set({ queuedFilesToRead: null })\n return files\n },\n composerHint: null,\n setComposerHint: (composerHint) => set({ composerHint }),\n composerFocusRequested: false,\n requestComposerFocus: () => set({ composerFocusRequested: true }),\n consumeComposerFocus: () => set({ composerFocusRequested: false }),\n consumeQueuedPrompt: () => {\n const prompt = get().queuedPrompt\n if (prompt) set({ queuedPrompt: null })\n return prompt\n },\n // Open to begin with; collapsing sticks for the session.\n suggestionsOpen: true,\n setSuggestionsOpen: (suggestionsOpen) => set({ suggestionsOpen }),\n voiceStartRequested: false,\n requestVoiceStart: () => set({ isOpen: true, voiceStartRequested: true }),\n consumeVoiceStart: () => set({ voiceStartRequested: false }),\n\n activeTools: [],\n setActiveTools: (activeTools) => set({ activeTools }),\n appendToolCallsToLastAssistant: (calls) =>\n set((s) => {\n if (!calls.length) return s\n const msgs = [...s.messages]\n const lastIdx = msgs.length - 1\n if (lastIdx >= 0 && msgs[lastIdx].role === 'assistant') {\n msgs[lastIdx] = { ...msgs[lastIdx], toolCalls: [...(msgs[lastIdx].toolCalls ?? []), ...calls] }\n }\n return { messages: msgs }\n }),\n appendLinksToLastAssistant: (links) =>\n set((s) => {\n if (!links.length) return s\n const msgs = [...s.messages]\n const lastIdx = msgs.length - 1\n if (lastIdx >= 0 && msgs[lastIdx].role === 'assistant') {\n const seen = new Map((msgs[lastIdx].links ?? []).map((l) => [l.id, l]))\n for (const l of links) if (!seen.has(l.id)) seen.set(l.id, l)\n msgs[lastIdx] = { ...msgs[lastIdx], links: Array.from(seen.values()).slice(0, 3) }\n }\n return { messages: msgs }\n }),\n appendCardsToLastAssistant: (cards) =>\n set((s) => {\n if (!cards.length) return s\n const msgs = [...s.messages]\n const lastIdx = msgs.length - 1\n if (lastIdx >= 0 && msgs[lastIdx].role === 'assistant') {\n const seen = new Map((msgs[lastIdx].cards ?? []).map((c) => [chatCardKey(c), c]))\n for (const c of cards) if (!seen.has(chatCardKey(c))) seen.set(chatCardKey(c), c)\n msgs[lastIdx] = { ...msgs[lastIdx], cards: Array.from(seen.values()).slice(0, 4) }\n }\n return { messages: msgs }\n }),\n conversations: [],\n conversationsLoaded: false,\n setConversations: (conversations) => set({ conversations, conversationsLoaded: true }),\n setMessages: (messages) => {\n // Resuming another thread — whatever was staged for the abandoned one goes.\n clearAttachmentRegistry()\n set({ messages, attachments: [] })\n },\n}))\n"]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { getSupabaseClientOptional } from './chunk-KJELMULA.js';
|
|
2
|
+
import { useOrganizationStore } from './chunk-YKPPCV2Z.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
var DAY = 864e5;
|
|
6
|
+
function currentPeriod(now = /* @__PURE__ */ new Date()) {
|
|
7
|
+
return {
|
|
8
|
+
start: new Date(now.getFullYear(), now.getMonth(), 1),
|
|
9
|
+
end: new Date(now.getFullYear(), now.getMonth() + 1, 1)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function summarizeAiUsage(raw, included, now = /* @__PURE__ */ new Date(), names = {}) {
|
|
13
|
+
const { start, end } = currentPeriod(now);
|
|
14
|
+
const used = raw.byUser.reduce((sum, u) => sum + u.credits, 0);
|
|
15
|
+
const elapsedDays = Math.max(1, (now.getTime() - start.getTime()) / DAY);
|
|
16
|
+
const totalDays = Math.round((Date.UTC(end.getFullYear(), end.getMonth(), 1) - Date.UTC(start.getFullYear(), start.getMonth(), 1)) / DAY);
|
|
17
|
+
const projected = Math.round(used / elapsedDays * totalDays);
|
|
18
|
+
return {
|
|
19
|
+
used,
|
|
20
|
+
included,
|
|
21
|
+
remaining: Math.max(0, included - used),
|
|
22
|
+
ratio: included > 0 ? used / included : used > 0 ? Infinity : 0,
|
|
23
|
+
over: used > included,
|
|
24
|
+
overBy: Math.max(0, used - included),
|
|
25
|
+
periodStart: start,
|
|
26
|
+
periodEnd: end,
|
|
27
|
+
// Calendar days, so a daylight-saving hour never adds one.
|
|
28
|
+
daysLeft: Math.max(0, Math.round((Date.UTC(end.getFullYear(), end.getMonth(), end.getDate()) - Date.UTC(now.getFullYear(), now.getMonth(), now.getDate())) / DAY)),
|
|
29
|
+
projected,
|
|
30
|
+
projectedOver: projected > included,
|
|
31
|
+
byUser: [...raw.byUser].sort((a, b) => b.credits - a.credits).map((u) => ({ ...u, name: names[u.userId] ?? "Ex-integrante da equipe" })),
|
|
32
|
+
byFeature: [...raw.byFeature].sort((a, b) => b.credits - a.credits)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function useAiUsage(included) {
|
|
36
|
+
const tenantId = useOrganizationStore((s) => s.currentOrg?.id ?? null);
|
|
37
|
+
const members = useOrganizationStore((s) => s.members);
|
|
38
|
+
const [raw, setRaw] = React.useState(null);
|
|
39
|
+
React.useEffect(() => {
|
|
40
|
+
const supabase = getSupabaseClientOptional();
|
|
41
|
+
if (!supabase || !tenantId) {
|
|
42
|
+
setRaw("missing");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
let alive = true;
|
|
46
|
+
const { start, end } = currentPeriod();
|
|
47
|
+
void supabase.rpc("ai_usage_summary", { p_from: start.toISOString(), p_to: end.toISOString() }).then((res) => {
|
|
48
|
+
if (!alive) return;
|
|
49
|
+
if (res.error || !res.data) {
|
|
50
|
+
setRaw("missing");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
setRaw({
|
|
54
|
+
byUser: (res.data.by_user ?? []).map((u) => ({ userId: String(u.user_id), credits: Number(u.credits) || 0 })),
|
|
55
|
+
byFeature: (res.data.by_feature ?? []).map((f) => ({ feature: String(f.feature), credits: Number(f.credits) || 0 }))
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
return () => {
|
|
59
|
+
alive = false;
|
|
60
|
+
};
|
|
61
|
+
}, [tenantId]);
|
|
62
|
+
const names = React.useMemo(() => {
|
|
63
|
+
const out = {};
|
|
64
|
+
for (const m of members) out[m.userId] = m.user?.fullName || m.user?.email || "\u2014";
|
|
65
|
+
return out;
|
|
66
|
+
}, [members]);
|
|
67
|
+
if (raw === null) return { status: "loading" };
|
|
68
|
+
if (raw === "missing") return { status: "not-metered" };
|
|
69
|
+
const summary = summarizeAiUsage(raw, included, /* @__PURE__ */ new Date(), names);
|
|
70
|
+
return summary.used > 0 ? { status: "ok", summary } : { status: "empty", summary };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { currentPeriod, summarizeAiUsage, useAiUsage };
|
|
74
|
+
//# sourceMappingURL=chunk-M2CMDWUI.js.map
|
|
75
|
+
//# sourceMappingURL=chunk-M2CMDWUI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/billing/ai-usage.ts"],"names":[],"mappings":"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","file":"chunk-M2CMDWUI.js","sourcesContent":["import * as React from 'react'\nimport { getSupabaseClientOptional } from '../lib/supabase'\nimport { useOrganizationStore } from '../org/store'\n\n/** What the usage source reports for one period. */\nexport interface AiUsageRaw {\n /** Credits spent per member (this tenant only — the source is tenant-scoped). */\n byUser: Array<{ userId: string; credits: number }>\n /** Credits per feature key: `assistant`, `atendeai`, `scribe`… */\n byFeature: Array<{ feature: string; credits: number }>\n}\n\nexport interface AiUsageSummary {\n used: number\n included: number\n remaining: number\n /** 0–1+, used ÷ included. */\n ratio: number\n over: boolean\n /** Credits beyond the allowance. */\n overBy: number\n periodStart: Date\n periodEnd: Date\n daysLeft: number\n /** Credits at the end of the period at the current daily pace. */\n projected: number\n projectedOver: boolean\n byUser: Array<{ userId: string; name: string; credits: number }>\n byFeature: Array<{ feature: string; credits: number }>\n}\n\nconst DAY = 86_400_000\n\n/** The billing period: the calendar month `now` falls in. */\nexport function currentPeriod(now: Date = new Date()): { start: Date; end: Date } {\n return {\n start: new Date(now.getFullYear(), now.getMonth(), 1),\n end: new Date(now.getFullYear(), now.getMonth() + 1, 1),\n }\n}\n\n/** Pure: the period's numbers from what the source reported. */\nexport function summarizeAiUsage(\n raw: AiUsageRaw,\n included: number,\n now: Date = new Date(),\n names: Record<string, string> = {},\n): AiUsageSummary {\n const { start, end } = currentPeriod(now)\n const used = raw.byUser.reduce((sum, u) => sum + u.credits, 0)\n const elapsedDays = Math.max(1, (now.getTime() - start.getTime()) / DAY)\n const totalDays = Math.round((Date.UTC(end.getFullYear(), end.getMonth(), 1) - Date.UTC(start.getFullYear(), start.getMonth(), 1)) / DAY)\n const projected = Math.round((used / elapsedDays) * totalDays)\n return {\n used,\n included,\n remaining: Math.max(0, included - used),\n ratio: included > 0 ? used / included : used > 0 ? Infinity : 0,\n over: used > included,\n overBy: Math.max(0, used - included),\n periodStart: start,\n periodEnd: end,\n // Calendar days, so a daylight-saving hour never adds one.\n daysLeft: Math.max(0, Math.round((Date.UTC(end.getFullYear(), end.getMonth(), end.getDate()) - Date.UTC(now.getFullYear(), now.getMonth(), now.getDate())) / DAY)),\n projected,\n projectedOver: projected > included,\n byUser: [...raw.byUser]\n .sort((a, b) => b.credits - a.credits)\n // Someone who spent credits and has since left the team.\n .map((u) => ({ ...u, name: names[u.userId] ?? 'Ex-integrante da equipe' })),\n byFeature: [...raw.byFeature].sort((a, b) => b.credits - a.credits),\n }\n}\n\nexport type AiUsageState =\n | { status: 'loading' }\n /** No meter is recording credits yet: the screen says so, it shows no number. */\n | { status: 'not-metered' }\n /** Metered, nothing spent this period. */\n | { status: 'empty'; summary: AiUsageSummary }\n | { status: 'ok'; summary: AiUsageSummary }\n\n/**\n * This tenant's AI credit usage in the current period, read from the\n * `ai_usage_summary(p_from, p_to)` RPC — the seam the assistant broker writes\n * behind. Until that RPC exists the state is `not-metered`, never a guess.\n */\nexport function useAiUsage(included: number): AiUsageState {\n const tenantId = useOrganizationStore((s) => s.currentOrg?.id ?? null)\n const members = useOrganizationStore((s) => s.members)\n const [raw, setRaw] = React.useState<AiUsageRaw | null | 'missing'>(null)\n\n React.useEffect(() => {\n const supabase = getSupabaseClientOptional() as any\n if (!supabase || !tenantId) { setRaw('missing'); return }\n let alive = true\n const { start, end } = currentPeriod()\n void supabase.rpc('ai_usage_summary', { p_from: start.toISOString(), p_to: end.toISOString() })\n .then((res: { data: any; error: any }) => {\n if (!alive) return\n if (res.error || !res.data) { setRaw('missing'); return }\n setRaw({\n byUser: (res.data.by_user ?? []).map((u: any) => ({ userId: String(u.user_id), credits: Number(u.credits) || 0 })),\n byFeature: (res.data.by_feature ?? []).map((f: any) => ({ feature: String(f.feature), credits: Number(f.credits) || 0 })),\n })\n })\n return () => { alive = false }\n }, [tenantId])\n\n const names = React.useMemo(() => {\n const out: Record<string, string> = {}\n for (const m of members) out[m.userId] = m.user?.fullName || m.user?.email || '—'\n return out\n }, [members])\n\n if (raw === null) return { status: 'loading' }\n if (raw === 'missing') return { status: 'not-metered' }\n const summary = summarizeAiUsage(raw, included, new Date(), names)\n return summary.used > 0 ? { status: 'ok', summary } : { status: 'empty', summary }\n}\n"]}
|