Pippa · AI pomocník

Pippa opens from the bird icon immediately after Přehled in the company detail menu. The icon has the accessible name and tooltip Pippa · AI pomocník. Both the conversation list and dialogue reuse the company header and action menu. Pippa is marked as current on the list; inside a dialogue, the bird links back to the list. Actions follow the same company permissions as the overview, edit, merge and closure pages. Ordinary company conversations belong to party_id; lead/supplier/customer roles do not change that identity. The company list of conversations shows their original questions as regular-weight links, authors and dates. Reading a conversation never calls OpenAI.

My conversations

Konverzace s Pippou in the top hamburger menu opens /pippa/conversations. The existing bird icon uses the same 16 × 16 px size as the other menu icons. This page lists all conversations created by the current user: company chats, onboarding and supplier scouting, ordered by latest activity first (updated_at DESC, id DESC). Each link opens the corresponding saved dialogue. The authenticated GET /api/pippa/conversations endpoint derives the author from the current user; it accepts no author override and retains each conversation's current access rules. Conversations for companies that are no longer fully accessible are omitted. Reading the list never triggers AI work.

Access and persistence

Pippa requires mcp.access and a valid personal Pippa token. Full company-profile access defines its scope over company data; Summary access alone is insufficient. The old company.ai_assist grant is no longer required. Lists and saved history check the token and company access without loading or authorizing each original source. Stored snapshots retain the conversation's company scope even if an original source is removed. Admin history retains its separate admin gate.

New context uses company-scoped records, including all related tasks, and AI tools resolve source IDs only within that server-built manifest. Company access is checked before and after background generation. Live attachments still depend on the existing Google/Jira integrations; Jira uses the acting user's OAuth grant. Unavailable live files are reported without hiding saved conversations. Direct source screens and business mutations retain their existing permissions. See Permissions.

ai_company_conversation stores company, creator, timestamps, title and summary. ai_company_turn stores each question and answer together, in ID order, with its author, processing state, immutable context snapshot, citations, findings, provider response IDs/model/usage and source-use records. Reply Pilot owns the history; Responses requests use store: false and do not depend on provider conversation retention. Text source snapshots preserve the actual supplied content. Native attachments retain source identity, timestamp, byte length and SHA-256 of submitted bytes; the original binary remains in Google/Jira storage. The source cannot be reconstructed from that hash if its owner later deletes it.

Opening Nová konverzace (/companies/<party_id>/pippa/new) renders an unsaved draft and does not create a conversation or start polling. Submitting its first question (or starting basic research) atomically saves the conversation and first turn, then redirects to the saved dialogue. Validation or transaction failure leaves no empty conversation. Creation and question submission use stable request UUIDs. A database row lock and partial unique index admit one queued/processing question per conversation. Repeated submissions return the existing question. Retry reuses the last failed read-only turn and cannot reorder an older turn after newer ones. A turn that began a contact/note write or opportunity closure cannot be retried, including after a restart; inspect the company profile and continue with a new question. The guard is saved before mutation in result.write_attempted; completed tool results are retained in result.actions.

Accepted questions are persisted before background execution. The existing single backend instance owns one executor; it processes queued questions even if the browser closes. On backend restart it resumes queued turns and marks interrupted processing as failed with an explicit recovery message. Completed answers survive unchanged. Before running multiple backend instances, replace startup recovery with database leases; do not run several consumers using this single-instance recovery contract.

Context and limits

Every question assembles current company profile, identifiers, roles, contacts, relationships, CME data, requirements/evidence, linked lead import records, notes, activities, scoped tasks, full imported email messages and email/task attachment inventories. Email bytes are retrieved through existing Google-module HTTP services; the backend never reads Google cache paths or credentials. Jira attachments retain delegated access. No OCR, format conversion or extraction service is introduced.

The whole source inventory and text snapshots are available through read-only list_sources and read_source functions. Inventory search includes full text, returns 30 entries per page and exposes the next offset. Source text is read in 16,000-character pages, so a long email is not silently cut off. Native files are loaded on demand. The UI distinguishes available, loaded and cited sources. Unsupported, unavailable and oversized files remain visible with a reason. Limits encountered during generation also appear below the answer.

Supported native file inputs include PDF, text/code, Word/ODT/RTF/Pages, PowerPoint/Keynote, Excel/CSV/TSV/IIF, email and calendar/contact text files. PippaContextService lists the supported extensions from the provider's file-input guide. PNG, JPEG, WebP and GIF use native image inputs (animated/invalid images may be rejected by the provider). Files and cumulative file input per turn are limited to less than 49,000,000 bytes, below the provider's 50 MB request limit. Spreadsheet input is a native limited row preview, not a guarantee of full spreadsheet analysis. Non-PDF document input may omit embedded visuals. Unavailable/oversized files and files the model reports as unreadable carry per-answer source states and reasons; they cannot be labelled as cited sources. Provider rejection produces a retained failed question, never a false answer.

The most recent six turns and saved summary form the initial working dialogue; the full transcript remains available through paginated read_history. A 240,000-character working-input ceiling stops with an explicit recoverable error rather than silently deleting history. Provider token/file parsing limits can be reached sooner, especially for PDFs; these failures also retain the question. Narrowing the question or starting another conversation is supported.

The model, instructions and enabled tools are configured in reply-pilot-be/conf/pippa.yaml, with a shipped default-conf/pippa.yaml fallback. BE must restart after config changes. The existing OpenAI API URL/key and request timeout are reused. Web search runs when needed; no public search is triggered by simply opening the screen. The base research prompt separates products from variants/SKU, headquarters from markets, and verified facts from estimates and missing information. Summary timestamps do not represent new verification dates. Findings do not automatically change company fields. On an explicit user request, Pippa can create linked people or add company email/phone contacts through the shared Java tools and existing write permissions. Pippa can also save a company note with add_company_note, preserving the user's text and recording the authenticated author. Czech “poznámka” and “komentář” mean this company note; English “comment” means a Jira comment. A combined request to close a company and add a note performs both operations separately: the closure's Jira audit comment is not a saved company note. Previous assistant claims are not proof of a note; Pippa checks actual note: sources or the successful tool result. Pippa can also close a company opportunity and all its open Jira tickets on an explicit request with a mandatory reason. It reads current Supplier Onboarding Result options, maps the user's explanation (for example “nemá dost produktů” to Nezaveden → Maly sortiment), and asks when the mapping or target is ambiguous. The onboarding result must succeed first; Pippa reports confirmed closures and any partial failures. Chat tools cannot send emails or create tasks. The dedicated onboarding forms below provide separate, explicit approval actions. See MCP for tools and retry behavior.

Create a company with Pippa

Pippa understands “přehoď/přeřaď dodavatele na uživatele” as changing Výchozí obchodník pro emailovou komunikaci and assigning all open company tickets to that user. It resolves the name/email through list_assignment_users, asks about ambiguous names, and executes reassign_company only on an explicit request. Live Jira Done-category tickets are unchanged; returned failures mean partial completion, not success for every ticket. The operation includes general and meeting tickets as well as onboarding and canonically single-company email tickets; meeting assignment still requires the caller skill. Existing CME sync ownership rules continue to apply. When the current supplier source has a CME salesperson, Pippa names that salesperson and explains that synchronization may restore the CME value; a lasting change requires updating CME. Conflicting or missing salesperson details must not be guessed.

Before company creation, the requested user is stored in proposal.company.default_email_sales_user_id and shown in the approval form. Zero or an omitted field preserves the current-user default. Approval creates the company and onboarding ticket for the selected user; the authenticated actor stays the author. Creating for another user requires full global company access and company-write permission. After creation, Pippa uses the same reassignment tool in the onboarding dialogue; editing the proposal does not mutate an existing company. The configured model instructions cover both accented and unaccented Czech wording.

The Založit společnost s Pippou text link on /signpost opens an unsaved conversation at /pippa/companies/new. The bird icon uses the existing asset. The first message must contain a public company website. /pippa/companies lists the current user's saved onboarding conversations; each candidate has its own /pippa/companies/<id> conversation. No company or ticket exists at this point.

The backend checks the website against existing companies before research, then checks the proposed legal name, Czech IČO and normalized foreign identifiers after research and immediately before approval. Existing company matches stop creation. Details and links require full company access; otherwise only the existence of a duplicate is disclosed. The recorded merchant is labelled as merchant, never creator. Creator is shown only where an onboarding audit records it; older company records explicitly say that this information is unavailable.

Active CME supplier registration is a hard stop for onboarding and initial outreach. The shared get_company tool and authorized onboarding match results expose cme_registration with the active status, CME salespeople and blocking reason. The rule uses current DODAVATEL sources with missing_since IS NULL, including suppliers without an assigned salesperson; reservations and historical missing sources do not count. Restricted matches disclose only duplicate existence. Pippa's instructions require checking this status and explaining the stop with the existing profile link. If research discovers a match, its findings are kept but the first-email proposal is cleared. Research and explicitly requested profile enrichment remain possible in the existing company's conversation.

Approval rechecks duplicates before company creation and current CME status before ticket creation (including resumed workflows) and initial email sending. A stop preserves already-created records. The onboarding source includes current CME status after a company is attached. This does not block ordinary supplier correspondence or change company identity matching. No schema migration is needed for the CME rule. Update an existing server-owned conf/pippa.yaml as described in MCP configuration; a BE restart loads the instructions.

Research uses existing Responses web search and a strict structured proposal. Before company creation this mode exposes source/history reading, public web search and the read-only list_assignment_users lookup, and rejects company mutations. After creation it also exposes reassign_company, get_company, get_company_closure, close_company_opportunity, add_company_note, add_company_contact, create_company_person and update_company_person_role, scoped to the attached company. Note/contact writes require an explicit user request and retain the shared company authorization and durable write/retry guard (plus existing contact duplicate checks), including after the first email has been sent. Email sending still uses the approval form. A closed onboarding ticket blocks initial outreach, including the backend send approval even when an earlier draft still exists. Instructions cover legal identity, IČO/DIČ and official register links, contacts with sources, portfolio, products versus variants/SKU, estimation method and B2B portal versus feed/API/EDI. Missing and estimated values must be labelled. The reviewer edits the fields, selects contacts and identifiers, and reviews the research note with its sources. Selected named contacts create linked people; unnamed rows create company contacts. Contact research is automatic: Pippa checks public contact, team and B2B pages and must populate proposal.contacts with source URLs, rather than leave contacts only in prose. The current structured proposal is included in the model's opening context so follow-up questions can preserve contacts for the same company. A ready proposal with no contacts gets at most one automatic read-only research follow-up per turn. If the list remains empty, the form warns that approval will create a company without contacts; the reviewer can still proceed. No names or addresses are inferred from generic mailboxes.

Research uses the current proposed website. If the user selects a different website host, old scouting research is omitted from the current context; approval detaches the original candidate instead of marking it as the newly created company. After creation, the approved company and live profile define the scope, including for older conversations whose original scouting metadata names another company. Historical questions and source snapshots are retained unchanged. Identity validation reuses the existing CZ/SK/PL/DE policies. Research without a dedicated structured field is retained in the company note and onboarding ticket. Pippa's configured instructions require a country/type/value check before marking a proposal ready. Czech IČO belongs only in company_registration_number, with registration_country_code=CZ and identifiers=[]; Czech court/register details belong in research_note. This check also covers proposals carried over from scouting or earlier turns. Foreign identifiers use only the supported SK IČO, PL KRS/REGON and DE HRA/HRB combinations. These are model instructions; the existing backend validation remains authoritative, and saved proposals are not rewritten by a configuration change.

Schválit a založit společnost performs these steps in order:

  1. Create the company for the selected user (yourself by default) with the Lead role and attach the conversation.
  2. Immediately create its Supplier Onboarding ticket using the author's Jira grant.
  3. Save the selected contacts, foreign identifiers and research note.
  4. Open the editable first email, showing the actual shared sender velkoobchody@internet-handel.cz.

Schválit a odeslat e-mail sends exactly the approved recipient, subject and plain text and HTML through the existing BivojEmailService send/import/task workflow. The recipient must resolve unambiguously to this company. Success requires a confirmed send, imported email and communication ticket. The final state shows the sent message and both task links. Saying “yes” in chat does not create or send. Saving a proposal or email draft does not approve an external action. Polling does not overwrite unsaved edits; an explicit button loads a new AI proposal. Rejected form values and inline errors survive polling too. Approval controls are disabled while research runs, if write access is revoked, or if another tab advances the workflow; loading the latest proposal displays the current step.

The onboarding workspace places the shared rich email composer on the left and a narrower Pippa conversation on the right. Small screens switch between the proposal and chat. The draft opens automatically after company creation, with a recommended contact, its reason/source where available, and alternative saved contacts. The recommendation describes relevance, not a predicted response rate. Finishing company/contact creation atomically queues the initial email turn with the ready editor state, including after resuming Jira setup. No extra chat request is needed. Repeated creation submissions do not generate another initial turn; normal queue recovery and failed-turn retry apply. An empty generated subject or body is reported as a retryable failure rather than a successful empty draft. The first active ai_prompt with prompt_type_id = 2, ordered by id, supplies the outreach brief (the type code is resolved from ID 2). Its ID, name and text are included in the turn's context snapshot and first model request. A missing active prompt produces a visible error and can be retried after configuration is fixed. The saved company's registration country selects Czech (CZ, SK) or English (all other countries) for the first email, overriding the prompt's language and examples. Pippa's dialogue remains Czech. Later revisions preserve the user's current draft, including language, unless asked to change it. The editor has the usual formatting, attachment and profile-signature controls; the signature can be inserted with “Podpis na konec” without being duplicated on reload. “Odeslat e-mail” is the explicit send approval.

An email revision question includes the currently edited recipient, subject, plain text and HTML. The backend saves these together with the queued question in one transaction; duplicate question requests cannot overwrite the draft. Pippa uses this current draft and preserves unrelated edits and signatures. Returned revisions update the editor automatically only when no newer local edits exist. Otherwise “Použít nový návrh Pippy” applies them explicitly; “Vrátit předchozí návrh” restores the previous editor contents. Polling retains selected files, editor focus and inline validation. Attachments are sent only on explicit send and are not persisted with saved drafts; the form states this limitation.

Scouting suggests only new companies. Exact matches by existing domain/name/ verified registration identifiers (including restricted companies) are excluded before saving candidates. Saved scouting lists recheck matches on every read, so a company created since the original search also disappears from suggestions. Existing candidate records/history are retained. Approximate full-text matches still require identity verification before exclusion.

ai_company_conversation.onboarding retains the workflow state, approved proposal, draft and confirmed result IDs. A row lock claims each phase before side effects; duplicate submissions cannot replay creation or send. Questions and approvals cannot run simultaneously. A confirmed company or ticket is retained if a later step fails. Known missing Jira authorization can resume after connection. An uncertain creation or send is blocked for operator reconciliation, including interrupted in-progress phases after restart. Confirmed sent mail with incomplete import/task creation is labelled separately and is never resent. This first version does not automatically repair those uncertain/partial external operations. Onboarding audit conversations cannot be deleted through ordinary chat deletion.

Standalone conversations are private to their author with company-write rights. After company creation they appear in its Pippa history and inherit full company read access. Only the original author with current company-write access can edit, ask follow-up questions or approve actions. See Permissions.

Native API contracts verified against official documentation: File inputs, Web search, Function calling.

UI and assets

Správa konverzací Pippy on /signpost opens /admin/pippa for the active Administrator role (admin) only. The backend independently checks that role on /api/admin/pippa and its detail/delete endpoints. This global administration view includes company, standalone onboarding and supplier-scouting conversations from every author, ordered by latest activity and ID, with exactly 100 items per full page. The user filter selects the conversation's creator, including authors of older conversations. Pagination and return links preserve that filter. Opening a conversation shows saved history without generating a response or offering workflow approvals. Every row labels its type: Firemní konverzace, Onboarding or Hledání dodavatelů. The list, detail header and shared dialogue display Czech dates (dd.mm.yyyy HH:MM) in the configured application timezone (Europe/Prague in production), including daylight-saving changes. Original timestamps remain in dialogue time attributes.

Each row reuses the company list's delete control and native confirmation dialog. Admin deletion includes onboarding history and cascades to turns and saved context; it does not delete companies, Jira tickets or sent mail. Queued/processing questions and active company-creation/email-send phases return a conflict until finished. The check and deletion share the onboarding row lock. Ordinary company-list deletion continues to exclude onboarding history. The global view needs no schema migration.

Only an active Administrator (admin) with full company access sees the small red cross at the right of each conversation. It opens a native confirmation dialog; cancellation makes no request. Confirming sends a CSRF-protected delete request. The backend checks the administrator role independently and deletes the conversation with all its turns and snapshots through the existing cascade. Queued work cannot recreate a deleted conversation; an already-running provider request may finish, but cannot save its answer into deleted history.

The dialogue scrolls above the fixed composer. Enter inserts a newline; Ctrl/Cmd+Enter submits. Polling preserves the reader's position and announces a new response without forcing a jump. Forms retain failed input and use inline validation. Responses use escaped Markdown (paragraphs, headings, lists, bold, code and links); model HTML is never trusted.

static/pippa/icon.svg and avatar.svg are independent vector adaptations of the approved PIP bird: teal body, coral face, cream belly. Each has transparent 256/512 px PNG exports. The icon-only navigation link has descriptive image alt text; duplicate decorative images use empty alt text. No animation or floating mascot is used.

Verification

Run backend tests with mvn -o -f reply-pilot-be/pom.xml test and frontend tests with PYTHONPATH=reply-pilot-app .venv/bin/python -m pytest reply-pilot-app/tests/test_pippa.py. Include reply-pilot-app/tests/test_pippa_onboarding.py for the standalone flow. Include reply-pilot-app/tests/test_pippa_admin.py for administrator access, author filtering, pagination, deletion confirmation/CSRF and saved-history browsing. Run node --test reply-pilot-app/tests/pippa_polling.test.cjs for draft preservation and approval controls during polling (no additional dependencies). PippaOnboardingServiceTest verifies approval order, author/scope checks, duplicate blocking, and confirmed/uncertain send behavior with mocked external services. Run bash reply-pilot-db/tests/test-migration-classification.sh for the nullability versus destructive-drop classification check. The PostgreSQL repository integration test additionally takes PIPPA_TEST_DB_URL, PIPPA_TEST_DB_USER, PIPPA_TEST_DB_PASSWORD; it creates and drops an isolated random schema. It verifies persistence, cross-company isolation, admin pagination/filtering/deletion, duplicate submissions, retries, restart recovery and competing transactions. The Liquibase validate/update/rollback-count 1/update cycle is required before release. Deploy the DB expand migration before the backend and frontend; no Google-module change is needed.

The opt-in PippaAiServiceTest#nativeFileToolsAndPublicSearchWorkWithTheRealProvider uses PIPPA_LIVE_API_KEY and the configured Pippa model. It sends only public test text, checks native file content returned through a function result, retrieval beyond the first 16,000 characters, web search and citations. It makes paid API calls and is skipped in normal test runs.

MCP tools

cz.replypilot.be.mcp.CompanyTools defines the company/contact and opportunity closure tools shared by Pippa and BE's /mcp endpoint. Pippa calls that endpoint over loopback HTTP with the current user's newest active token of type pippa. Opening a Pippa page reuses that token or, with current mcp.access permission, automatically creates one named Pippa with no expiration. During impersonation it belongs to the effective user. Concurrent opens do not create duplicates. It is listed in Profile → Tokens like a manual token. Revoked and expired tokens stay invalid; a later page entry can create a replacement. Submissions, polling and queued work only validate existing tokens. Missing permission or broken token encryption still refuses access. The server identifies the actor from the bearer hash and checks current permissions. Pippa supplies the selected company/conversation ID itself; the model cannot change it. Original tokens are decrypted only in BE and never sent to the model. See MCP.

Conversational supplier scouting

Hledat dodavatele s Pippou on /signpost opens /pippa/scouting/new. Agree on a country, assortment and requirements in chat; provide public catalog URLs and refine the search in later messages. The existing web research tools read public sources. This version accepts resource links, not uploaded catalog files. Scouting can cover any country; the existing company-creation form currently supports CZ, SK, PL and DE, and Pippa must explain that limitation for other countries.

Saved candidates appear in the right-hand panel with relevance, B2B evidence, sources and eligibility. Moje průzkumy lists the author's saved conversations and filters by the latest agreed search country. Company search supplies possible matches; current database duplicate/CME checks and permanent decisions control onboarding eligibility. Missing search results are not proof of a new legal entity.

Zahájit onboarding opens /pippa/companies/new?candidate_id=… in a separate tab. GET only checks eligibility and prefills a draft. First submission creates and links a child conversation atomically, passing stored research as dated evidence to verify. Later clicks resume that conversation. Another author's in-progress onboarding cannot be taken over. The existing company, Jira and email approvals remain in force.

Vyřadit / vynechat opens a native dialog with a required reason and explicit scope: permanent rejection across all searches, or skip only this search. Both can be explicitly reversed with a reason. Pippa can make the same decision through its tools only on an explicit user request; research findings alone do not authorize rejection. A created company uses the existing opportunity/ticket closure flow. Permanent decisions survive deleting chats and block later onboarding, including ordinary onboarding started directly from the company's website.

Release requires DB migration 0091, BE and APP, plus updated scouting instructions and enabled tools in any server-owned conf/pippa.yaml. No extra runtime service is introduced. The shared MCP authentication additionally requires migration 0092, the BE encryption key and a personal Pippa token. See MCP.