UNPKG

@withpica/mcp-server

Version:

MCP Server for withPICA — enables AI assistants to interact with your withPICA music catalog

875 lines (777 loc) • 42.9 kB
// Copyright (c) 2025-2026 Withpica Ltd. All rights reserved. export class PromptRegistry { pica; constructor(pica) { this.pica = pica; } /** * List all available prompts */ listPrompts() { return [ { name: "analyze-catalog", description: "Analyze the music catalog and provide insights about completeness, quality, and opportunities", arguments: [], }, { name: "find-duplicates", description: "Find potential duplicate works or people that might need merging", arguments: [], }, { name: "enrich-metadata", description: "Suggest works and people that could be enriched from external sources (ISNI, MusicBrainz)", arguments: [], }, { name: "verify-works", description: "Review works and list what a person still needs to confirm on each — the human-made stamp, their own writer credits, their own master shares", arguments: [], }, { name: "assess-catalog-health", description: "Get the PICA Score and provide a detailed assessment of catalog health with prioritized improvement actions", arguments: [], }, { name: "audit-credits", description: "Find works with missing or incomplete credits and flag potential split issues", arguments: [], }, { name: "new-catalog-setup", description: "Get started with withPICA — import your music however you like (Spotify link, spreadsheet, screenshot, or just describe your songs)", arguments: [], }, { name: "close-the-loop", description: "Check what's missing on a specific work and offer to fix each gap", arguments: [ { name: "work_id", description: "The work ID to check completeness for", required: true, }, ], }, { name: "register-my-works", description: "Check registration status across PROs and help prepare works for registration", arguments: [], }, { name: "prepare-for-sync", description: "Get your catalog sync-ready — check audio analysis, mood tags, and completeness", arguments: [], }, { name: "workspace-autopilot", description: "Assess the workspace state and route to the right workflow — use this when you first connect or don't know where to start", arguments: [], }, { name: "log-a-session", description: "Log a studio session — guided capture of who was there, what work was being made, and what happened", arguments: [ { name: "work_id", description: "Optional work this session was for — pass to skip the discovery question", required: false, }, ], }, { name: "set-recording-splits", description: "Set master ownership splits on a recording — guided propose-and-confirm flow with verify gate", arguments: [ { name: "recording_id", description: "Optional recording to set splits on — pass to skip the discovery question", required: false, }, ], }, { name: "whats-new", description: "Daily-briefing funnel — surfaces what's changed since the user last looked", arguments: [], }, { name: "whats-wrong", description: "Attention funnel — surfaces what's broken, blocked, or needs the user's input", arguments: [], }, { name: "how-much-has-this-earned", description: "Earnings lookup for a specific work — falls back to a statement-import nudge when no royalties have been imported", arguments: [ { name: "work_id", description: "The work to summarise earnings for — required so the prompt can pull statements for the right song", required: true, }, ], }, { name: "who-owes-me-something", description: "Aggregates outstanding work owed to the user — pending invites, open disputes, and outstanding signatures — into a single 'what's outstanding' view", arguments: [], }, { name: "am-i-ready-to-register", description: "Alias of register-my-works — check registration status across PROs and prepare ready works for submission", arguments: [], }, { name: "pica_continue", description: "Pick up where you left off — surfaces specific open loops from your recent assistant_interactions (unconfirmed writes, mid-flow drop-offs, mutations with no follow-up read)", arguments: [], }, ]; } /** * Get a prompt by name */ async getPrompt(name, args) { switch (name) { case "analyze-catalog": return this.getAnalyzeCatalogPrompt(); case "find-duplicates": return this.getFindDuplicatesPrompt(); case "enrich-metadata": return this.getEnrichMetadataPrompt(); case "verify-works": return this.getVerifyWorksPrompt(); case "assess-catalog-health": return this.getAssessCatalogHealthPrompt(); case "audit-credits": return this.getAuditCreditsPrompt(); case "new-catalog-setup": return this.getNewCatalogSetupPrompt(); case "close-the-loop": return this.getCloseTheLoopPrompt(args?.work_id); case "register-my-works": return this.getRegisterMyWorksPrompt(); case "prepare-for-sync": return this.getPrepareForSyncPrompt(); case "workspace-autopilot": return this.getWorkspaceAutopilotPrompt(); case "log-a-session": return this.getLogASessionPrompt(args?.work_id); case "set-recording-splits": return this.getSetRecordingSplitsPrompt(args?.recording_id); case "whats-new": return this.getWhatsNewPrompt(); case "whats-wrong": return this.getWhatsWrongPrompt(); case "how-much-has-this-earned": return this.getHowMuchHasThisEarnedPrompt(args?.work_id); case "who-owes-me-something": return this.getWhoOwesMeSomethingPrompt(); case "am-i-ready-to-register": // Alias — falls through to the same handler so existing // `register-my-works` callers keep working. return this.getRegisterMyWorksPrompt(); case "pica_continue": return this.getPicaContinuePrompt(); default: throw new Error(`Prompt not found: ${name}`); } } /** * Analyze catalog prompt */ getAnalyzeCatalogPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `Please analyze my music catalog and provide a comprehensive report. If you're unsure where to start, read pica://docs/agent-guide for the workflow taxonomy. Use the following tools to gather data: 1. pica_catalog_stats - Get overall statistics 2. pica_works_query - Review works data 3. pica_people_query - Review people data 4. pica_recordings_query - Review recordings data Analyze and report on: - Overall catalog size and composition - Data quality (missing ISWCs, ISRCs, metadata) - Unconfirmed records (writer credits and master shares not yet confirmed by their holder) - Enrichment opportunities (people missing ISNI or MusicBrainz data) - Completeness (works with recordings, people with credits, etc.) - Recommendations for improvement Please provide specific, actionable insights.`, }, }, ], }; } /** * Find duplicates prompt */ getFindDuplicatesPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `Please help me find potential duplicate works and people in my catalog. Use these tools: 1. pica_works_query - Get all works 2. pica_people_query - Get all people Look for: - Works with very similar or identical titles - People with similar names (accounting for typos, different orderings) - Works with the same ISWC (if ISWC exists) - People with the same email address For each potential duplicate found: - List the IDs and key details - Explain why you think they might be duplicates - Suggest whether they should be merged or kept separate Be thorough but conservative - only flag items that have a strong likelihood of being duplicates.`, }, }, ], }; } /** * Enrich metadata prompt */ getEnrichMetadataPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `Please identify works and people that could benefit from metadata enrichment. Pre-flight: read pica://schemas/enrichment-resolve-required for the canonical resolve/propose shape (target_type, target_id, sources). If you're unsure where to start, read pica://docs/agent-guide for the workflow taxonomy. Use these tools: 1. pica_works_query - Get all works 2. pica_people_query - Get all people Identify: - People without ISNI identifiers who might have one - People without MusicBrainz IDs who might have one - Works missing ISWCs that could potentially be looked up - People missing biographical information For each enrichment opportunity: - List the person/work details - Explain what data is missing - Suggest potential sources (ISNI, MusicBrainz, etc.) - Prioritize the most impactful enrichment opportunities Focus on people and works where enrichment would add the most value. Before asking the user to paste facts, check your other connected MCPs (email, drive, notes, calendar) for evidence first. When you find it, cite the source via pica_enrichment_propose with a URI-style source (gmail://thread-id/..., gdrive://file-id/..., notion://page-id/..., file:///..., etc). PICA read tools surface gap_hints pointing at the exact capability + query shape to use — follow them when present.`, }, }, ], }; } /** * Verify works prompt */ getVerifyWorksPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `Please review my works and tell me what still needs a person to confirm it. PICA keeps no single "verified" flag on a work, so do not look for one and do not try to set one (pica_works_verify writes nothing). What a person confirms lives on specific records, and each is done by that person, signed in, on /inspect — an assistant cannot do it for them. Use these tools: 1. pica_works_query — list the works 2. pica_works_inspect with sections ["completeness", "provenance"] — what is missing and whether the work carries a human-made stamp 3. pica_recording_splits_list for each recording — whether each master share is confirmed (verified) For each work, report: - What metadata is missing (title, ISWC, writers, recordings) - Whether it is stamped human-made — if not, the owner can do it with "stamp as human-made" at /inspect/works/<id>#human-made - Which writer credits are still unconfirmed — each writer confirms their own in the credits table on /inspect/works/<id> - Which master shares are unconfirmed — each holder uses "confirm my share" at /inspect/recordings/<recording_id>#ownership-splits Group the works into: nothing left to confirm; waiting only on a person to confirm something; missing information. Say plainly which actions are the user's to take.`, }, }, ], }; } /** * Assess catalog health prompt */ getAssessCatalogHealthPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `Please assess the health of my music catalog using the PICA Score. If you're unsure where to start, read pica://docs/agent-guide for the workflow taxonomy. Use these tools: 1. pica_works_inspect({ sections: ["score"] }) - Get the PICA Score with pillar breakdown 2. pica_catalog_stats - Get raw catalog statistics for additional context 3. pica_works_query - Sample works data to understand quality Analyze: - The composite score and grade — is this catalog ready for commercial use? - Each pillar (income, ownership, asset, verification) — which is weakest? - The top actions — prioritize them by effort vs impact - Compare pillar scores to identify the biggest gaps Provide: - A plain-language summary of catalog health (suitable for a non-technical stakeholder) - The top 3 highest-impact improvements with specific steps - An estimate of where the score could reach after those improvements - Any urgent issues that need immediate attention (e.g., missing ownership data, unverified high-value works)`, }, }, ], }; } /** * Audit credits prompt */ getAuditCreditsPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `Please audit the credits across my music catalog to find gaps and issues. Pre-flight: if you propose adding a missing credit, read pica://schemas/work-credits-required for the canonical credit shape (role, splits totaling 100%). Use these tools: 1. pica_works_query - Get all works 2. pica_credits_list - Check credits for each work (sample at least 20 works, prioritizing recent ones) 3. pica_people_query - Cross-reference credited people Look for: - Works with no credits at all (no collaborators assigned) - Works where splits don't total 100% - Works with only one credited person (potential missing collaborators) - People who appear in multiple works — are their roles consistent? - Credits with generic roles (just "writer") vs specific roles ("composer", "lyricist") - People credited on works but missing key identifiers (IPI, ISNI) Report: - Summary statistics (% of works with credits, average collaborators per work) - List of works with no credits (highest priority to fix) - List of works with potential split issues - Recommendations for improving credit completeness - People who should be enriched with external identifiers for royalty collection`, }, }, ], }; } /** * New catalog setup prompt — first-time onboarding */ getNewCatalogSetupPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `I just connected to PICA. Help me get started with my music catalog. Pre-flight: read pica://schemas/work-required, pica://schemas/work-credits-required, and pica://schemas/audio-upload-required for the canonical create-work, add-credit, and upload-audio shapes. If you're unsure where to start, read pica://docs/agent-guide for the workflow taxonomy. First, read the workspace://context resource to understand my current catalog state (is it empty or do I already have works?). If the catalog is empty, welcome me and explain that I can get my music into PICA however I like: 1. Paste a Spotify or Deezer link — use pica_import_streaming_link to import tracks with ISRCs, credits, and artwork automatically 2. Share a spreadsheet or PRO statement — use pica_import_analyze to read columns and import in bulk 3. Send a screenshot of a track listing or release — you can read it and create the works from what you see 4. Just describe my songs — use pica_works_create to add them as we talk Ask me what I've got and help me get started with whichever method I choose. After importing, guide me through closing the loop on each work: - Who wrote these songs? (pica_credits_update — add writers and set splits) - Are there publishing details registered? (pica_resolve_work with sources: ['mlc'] — check MLC) - Do I have audio files to upload? (pica_audio_presigned_upload — for analysis) - What identifiers are missing? (pica_works_inspect with sections: ["enrichment"] — check coverage) Don't push — let me stop whenever I want. But always let me know what the next valuable step would be. If the catalog already has works, summarise what's there and ask what I'd like to do — import more, check health, or work on what's missing. Important: I can always log in at withpica.com to see, verify, and manage everything you create. The dashboard and MCP see the same catalog.`, }, }, ], }; } /** * Close the loop — check a specific work's gaps and offer to fix each one */ getCloseTheLoopPrompt(workId) { const workInstruction = workId ? `Check completeness for work ${workId} using pica_works_inspect({ id: "${workId || "..."}", sections: ["completeness"] }).` : `Ask me which work I'd like to check, or use pica_works_query({ health_filter: "low_completeness" }) to find works that need the most attention.`; return { messages: [ { role: "user", content: { type: "text", text: `Help me close the loop on a work — find what's missing and offer to fix each gap. Pre-flight: per gap class, read the relevant schema before writing — pica://schemas/work-credits-required (credits/splits), pica://schemas/audio-upload-required (master audio), pica://schemas/enrichment-resolve-required (ISWC/ISRC via cascade). If you're unsure where to start, read pica://docs/agent-guide for the workflow taxonomy. ${workInstruction} For the work, check every aspect of completeness: 1. pica_works_inspect with sections: ["completeness"] — get the full gap analysis 2. pica_credits_list — check if credits and splits are assigned 3. pica_works_inspect with sections: ["enrichment"] — check what enrichment sources have been tried For each gap found, offer a specific action: - Missing credits → "Want me to add writers? Who wrote this song?" - Splits not set → "What's the split between the writers?" - No ISWC → "Want me to check MLC for this work?" (pica_resolve_work with sources: ['mlc']) - No ISRC → "Do you have a recording with an ISRC for this?" - No audio → "Got a master recording to upload?" - Audio not analysed → "Want me to analyse the audio for BPM, key, and mood?" - No agreements → "Is there a publishing agreement for this work?" - Not registered → "Want me to check registration status?" (pica_works_inspect with sections: ["registration"]) Work through each gap one at a time. Let me skip any I'm not ready for. After each fix, check if the completeness score improved. Keep it conversational — don't list all gaps at once. Start with the most important one and work down.`, }, }, ], }; } /** * Register my works — check registration status and prepare for PRO submission */ getRegisterMyWorksPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `Help me check which of my works are registered and which still need registration. Pre-flight: read pica://schemas/enrichment-resolve-required (for resolving missing ISWCs via the cascade) and pica://schemas/export-required (for the registration-bundle export shape) before writing. Steps: 1. pica_works_query — list works, then use pica_works_inspect with sections: ["registration"] to check registration status across PROs (PRS, MCPS, PPL, MLC) 2. For works that aren't registered, use pica_works_inspect with sections: ["registration", "completeness"] on a sample to understand what's blocking registration 3. Identify common blockers: - Missing ISWC (needed for PRS/MCPS) → suggest pica_resolve_work with sources: ['mlc'] - Missing ISRC (needed for PPL) → check if recordings have ISRCs - Missing credits (needed for all) → suggest adding writers - Missing identifiers on people (IPI needed for PRO registration) → suggest enrichment Report: - How many works are fully registered vs partially vs not at all - The most common blocker and how to fix it - Which works are closest to being registration-ready (fewest gaps) Then offer: - "Want me to try enriching the works missing ISWCs from MLC?" - "Want me to export a registration bundle for the works that are ready?" (pica_export_song_registration — CSVs plus a CWR field-mapping guide; it is NOT a CWR file, so do not call it one) - "Want me to check registration status for a specific work?" Focus on practical next steps, not just reporting.`, }, }, ], }; } /** * Workspace autopilot — assess state and route to the right workflow */ getWorkspaceAutopilotPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `You've just connected to a PICA workspace. Assess its current state and guide me to the most valuable next action. If you're unsure where to start, read pica://docs/agent-guide for the workflow taxonomy. Step 1 — Orient: Read the workspace://context resource to get a snapshot of the catalog: org info, catalog size, health score, and top priorities. Step 2 — Route based on what you find: If the catalog is EMPTY (0 works): → Use the new-catalog-setup workflow. Welcome me, explain how to import music (Spotify link, spreadsheet, screenshot, or just describe songs), and get started. If the catalog has works but LOW completeness (PICA Score below 50 or many gaps): → Use pica_works_query({ health_filter: "low_completeness" }) to find the works that need the most attention. → Pick the top 3 and offer to run close-the-loop on them one at a time. → Tell me: "Your catalog has [N] works but many have gaps. Want me to start closing the loop on the ones that need the most attention?" If there are DUPLICATE signals (workspace context mentions duplicates): → Use the find-duplicates workflow. Tell me how many potential duplicates exist and offer to review them. If works are UNREGISTERED (registration coverage is low): → Use the register-my-works workflow. Tell me how many works aren't registered and what's blocking them. If the catalog is HEALTHY (score above 70, few gaps): → Use assess-catalog-health for a maintenance check. Tell me: "Your catalog looks solid. Here's a quick health check and what could still improve." If none of the above clearly applies: → Use pica_catalog_stats and pica_works_inspect({ sections: ["score"] }) to give me a quick summary, then ask what I'd like to focus on. Step 3 — Explain your routing: Tell me WHY you chose the workflow you did, in one sentence. Example: "Your catalog has 42 works but 28 are missing credits, so let's close those gaps first." Step 4 — Offer alternatives: After explaining, mention 1-2 other things I could do instead. Example: "Or if you'd rather import more music first, I can help with that too." Important: Don't run all workflows at once. Pick the single most valuable action and commit to it. You can always switch later.`, }, }, ], }; } /** * Prepare for sync — get catalog sync-ready */ getPrepareForSyncPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `Help me get my catalog ready for sync licensing — make sure my works are discoverable and attractive to music supervisors. Pre-flight: if you propose enriching from Spotify/MusicBrainz to fill genre/mood/identifier gaps, read pica://schemas/enrichment-resolve-required for the canonical resolve shape. Steps: 1. pica_catalog_stats — get an overview of the catalog 2. pica_works_query — sample works to check what's missing for sync readiness 3. For each work, check: - Has audio been uploaded? (supervisors need to hear it) - Has audio been analysed? (BPM, key, energy, mood — needed for search) - Are mood tags and genre set? (how supervisors search) - Is duration recorded? (supervisors filter by length) - Are lyrics transcribed? (for lyrical content search) - Is there a Spotify/YouTube link? (supervisors check existing presence) Report: - How many works have analysed audio vs unanalysed vs no audio at all - How many have mood tags and genre vs missing - The fastest path to sync-readiness (which works are closest) Then offer: - "Want me to trigger audio analysis on the tracks that have uploads but no analysis?" - "Want me to search for sync-ready tracks you already have?" (pica_search_for_sync) - "Want me to enrich works from Spotify to pull in genre and mood data?" The goal is making works findable by mood, BPM, key, and energy — that's what sync supervisors search by.`, }, }, ], }; } /** * Log a session — guided studio session capture */ getLogASessionPrompt(workId) { const workInstruction = workId ? `Log this session against work ${workId}.` : `Ask me which work this session was for. If I'm not sure or it doesn't have one yet, that's fine — work_id is optional.`; return { messages: [ { role: "user", content: { type: "text", text: `Help me log a studio session. Pre-flight: read pica://schemas/session-required for the canonical session shape (title, start_time, end_time required; session_type_id from pica_sessions_types; participants; optional work_id/recording_id). ${workInstruction} Walk me through it conversationally — don't dump a form. Capture each piece in turn: 1. What was the session for? — title and a short description (e.g. "vocal recording for track 3") 2. What kind of session? — call pica_sessions_types and let me pick (recording, mixing, mastering, writing, production, rehearsal). Pass the chosen type's UUID as session_type_id. 3. When was it? — start_time and end_time as ISO 8601. If I give you "yesterday afternoon", confirm a specific time. Default timezone Europe/London unless I say otherwise. If it was a full-day affair, set is_all_day: true. 4. Where? — studio name in location, or a virtual link in location_url for remote sessions. 5. Who was there? — collect participants as { personId?, userId?, role? }. If I name someone you don't recognise, offer to look them up via pica_people_query first; if they're not on PICA yet, offer pica_collaborators_invite to bring them in. 6. What happened? — capture as notes. This is the bit I'll thank you for later. Then call pica_sessions_create with the captured fields. Confirm what was logged and offer the natural next step: - "Want me to attach a multimedia file from this session?" (pica_multimedia_create) - "Want me to set master ownership splits for any new recording from this session?" (pica_recording_splits_create) - "Want me to log another session?" If I skip something, that's fine — only title, start_time, and end_time are strictly required. Don't badger me for the rest.`, }, }, ], }; } /** * Set recording splits — guided master ownership split assignment with verify gate */ getSetRecordingSplitsPrompt(recordingId) { const recordingInstruction = recordingId ? `Work on recording ${recordingId}.` : `Ask me which recording to set splits on. If I'm not sure of the ID, use pica_recordings_query to list recent ones and let me pick.`; return { messages: [ { role: "user", content: { type: "text", text: `Help me set master ownership splits on a recording — these are master-side royalty shares (distinct from writer splits, which are credits on a work). Pre-flight: read pica://schemas/recording-splits-required for the canonical split shape (recording_id, person_id, percentage, split_type, role). Active shares for one split_type + territory may not pass 100%, and a person holds at most one share there. ${recordingInstruction} Walk me through it carefully — splits flow into payments, so we want to get this right: 1. Show current state — call pica_recording_splits_list for the recording. If splits already exist, summarise them (who owns what %, total). Ask whether I want to add to them or just verify what's there. pica_recording_splits_create only adds — it refuses a second share for the same person (RECORDING_SPLIT_EXISTS). If a recorded share is wrong (an unknown holder, the wrong percentage or role), change that row with pica_recording_splits_update, or make it stop counting with pica_recording_splits_end — after I confirm. 2. Capture the proposed splits — for each split: - Who owns this share? (person_id from pica_people_query, or offer pica_collaborators_invite if they're not on PICA yet) - What percentage? (with the shares already recorded, at most 100% for that split_type + territory) - What kind? (split_type: master, mechanical, performance or sync — master ownership is master; role: owner, performer, producer, engineer, mixer, masterer, label or other) 3. Pre-commit dry-run — call pica_recording_splits_create with dry_run: true for each proposed split. It writes nothing and returns the shares already recorded plus likely_refusal. If likely_refusal is set, stop and tell me why; if existing_shares_checked is false, say the preview could not check. Each dry run only checks against what is recorded, so sum the proposed rows out loud too. If they would pass 100%, stop and ask me to adjust. 4. Confirm explicitly — say something like "I'm about to set [N] splits totalling 100% on this recording. Reply 'yes' to commit, or tell me what to change." Wait for my confirmation. Don't write without it. 5. Commit — call pica_recording_splits_create (dry_run omitted) for each split. Report each one back as it lands. 6. Verify — verification records which person agreed, so only each split holder can do it, signed in: "confirm my share" under ownership splits at /inspect/recordings/<recording_id>#ownership-splits. pica_recording_splits_verify refuses an agent — don't retry it; tell me (and, via pica_notify_person with the recording_id, any other holder) where to confirm. 7. Offer the natural next step: - "Want me to generate a split sheet PDF for signing?" (pica_split_sheet_generate) - "Want me to send the split sheet to the parties?" (pica_split_sheet_send) - "Want me to set splits on another recording?" If at any point the totals are off or I waver, stop and re-confirm. Splits are not the place to be ambitious — accuracy beats throughput.`, }, }, ], }; } /** * What's new — Atlas funnel for "what's new?", "any updates?", "catch me up". */ getWhatsNewPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `The user just asked "what's new?" or one of its synonyms (any updates, catch me up, what's been happening). Invoke pica_dashboard_briefing and surface the result. The briefing groups recent activity into deltas the user cares about — new credits, registration progress, statements imported, collaborators that joined, etc. If the briefing returns no items (empty or "nothing new"), say so explicitly. Don't fabricate updates and don't pad with stale information. A clean "nothing new since you last looked" is the right answer when the substrate is quiet. Format the response as a tight summary — group by category, lead with what changed, don't recapitulate the whole catalog. The user asked for the diff, not the snapshot.`, }, }, ], }; } /** * What's wrong — Atlas funnel for "what's wrong with my songs?", "what * needs fixing?", "anything urgent?", "what should I do?". */ getWhatsWrongPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `The user just asked "what's wrong with my songs?" or one of its synonyms (what's broken, what needs fixing, what should I do, anything urgent). Invoke pica_dashboard_attention and surface the result. The attention queue lists items that need the user's input or judgement — works missing required fields for registration, splits that don't total 100%, collaborators awaiting confirmation, disputes that have been open too long. For each attention item, include a one-line "why it matters" and the canonical next tool to fix it. Don't bury the action — the user asked what's wrong because they want to fix it. If the attention queue is empty, say so explicitly: "nothing urgent right now". That is itself a valuable answer — don't pad it.`, }, }, ], }; } /** * How much has this earned — Atlas-resolved royalties summary with * fallback nudge when no statements exist for the work. */ getHowMuchHasThisEarnedPrompt(workId) { const workInstruction = workId ? `The work in question is ${workId}.` : `Ask me which song I'm asking about. Use pica_search_all({ kind: 'work', query: <name> }) to resolve a name to a work_id if I name the song instead of giving you an ID.`; return { messages: [ { role: "user", content: { type: "text", text: `The user wants to know how much a specific song has earned. ${workInstruction} Step 1 — call pica_royalties_get({ work_id: <id> }) to pull cumulative earnings broken down by source (PRO, NRO, sync, mechanical, neighbouring rights). Step 2 — IF the summary returns zero rows or "no statements found": - Say so plainly. "I don't see any earnings statements for this song yet." - Offer the import path: "Would you like to import a recent PRO, NRO, or sync statement?" — this maps to pica_ingest_statement, which accepts CSV / PDF / JSON exports from PRS, BMI, ASCAP, MLC, Songtrust, distributors, and sync agencies. - Do NOT fabricate earnings. Empty is empty. Step 3 — IF the summary returns data: - Lead with the total figure in the user's preferred currency. - Break it down by source and by year (most recent year first). - Surface anomalies: a sudden drop, a missing quarter, a new platform that started paying. - Offer pica_statements_query as the natural next step if they want to drill into a specific statement. Step 4 — close with the right next action: - If earnings look wrong, suggest pica_disputes_create to flag it. - If earnings look right, suggest pica_export_industry_ready to generate a registration package that captures the work's earning history.`, }, }, ], }; } /** * Who owes me something — multi-tool orchestration aggregating pending * invites, outstanding signatures, and open disputes into one view. */ getWhoOwesMeSomethingPrompt() { return { messages: [ { role: "user", content: { type: "text", text: `The user just asked "who owes me something?" or one of its synonyms (who hasn't responded, what's outstanding, who owes me a reply). This is a multi-tool aggregation — there is no single "outstanding" tool. Pull from three substrates and merge into a single view: 1. pica_collaborators_invites_list — collaborators you invited who haven't accepted yet. Include sent_at so the user can see how long each has been outstanding. 2. pica_disputes_list — disputes that are open, awaiting the other party's response. Include opened_at and the disputed claim's summary. 3. Outstanding signatures — call pica_split_sheets_list (or pica_agreements_query if split sheets aren't applicable) to find documents that have been sent for signature but not all parties have signed. Surface each pending signer with the document title. Render the aggregated output grouped by person where possible — if "Alice Smith" appears in two of the three buckets (e.g., invited collaborator AND awaiting signature), collapse them into one entry under her name. Otherwise group by category. For each outstanding item, include: - Who owes the response - What they owe (accept invite, sign document, respond to dispute) - How long they've been sitting on it - The canonical next action: "Want me to nudge them?" → pica_collaborators_remind / pica_split_sheet_send (resend) / pica_disputes_nudge If all three buckets are empty: say "nothing outstanding right now — everyone you're waiting on has responded." That is the right answer; don't pad it.`, }, }, ], }; } /** * Pica continue — pick-up-where-you-left-off. Reads recent assistant_interactions * for the calling user, surfaces specific named open loops per ADR Decision 4 * example, falls through to a clean "nothing in flight" message when empty. */ async getPicaContinuePrompt() { let openLoops = []; let fetchFailed = false; if (this.pica) { try { openLoops = await this.pica.assistantInteractions.detectOpenLoops(); } catch { // Substrate read failed (network blip, RLS rejection, partition // missing). Render the static fallback so the prompt still works. fetchFailed = true; } } const intro = `The user wants to pick up where they left off. `; let body; if (fetchFailed || !this.pica) { body = `I couldn't read your recent activity right now. Ask the user what they were last working on, or invoke pica_my_recent_questions to refresh and try again.`; } else if (openLoops.length === 0) { body = `No recent open loops were found in your assistant_interactions history (last 3 days). ` + `Tell the user "nothing in flight right now — what would you like to start on?" and offer pica_dashboard_briefing as a starting point.`; } else { const offers = openLoops.slice(0, 5).map(formatOpenLoopOffer).join("\n"); const more = openLoops.length > 5 ? `\n\n_+${openLoops.length - 5} more open loops — ask the user if they want the full list._` : ""; body = `Here are the user's open loops from the last 3 days. Render each as a concrete continuation offer — name the entity, name the partial action, end with "Continue?".\n\n` + `${offers}${more}\n\n` + `Pick the most recent or highest-priority offer and lead with it. Do NOT dump all five at once — work one at a time. After the user picks one, invoke the canonical follow-up tool for that intent (e.g. pica_works_inspect to re-orient on a half-imported work, pica_credits_list to resume a credits sweep, pica_ingest_statement to resume a statement import).`; } return { messages: [ { role: "user", content: { type: "text", text: intro + body, }, }, ], }; } } function formatOpenLoopOffer(loop) { const reasonLabel = loop.reason === "unconfirmed_write" ? "awaiting your confirmation" : "stopped mid-flow"; const summary = loop.result_summary || loop.intent; const entity = loop.entity_type && loop.entity_ids && loop.entity_ids.length > 0 ? ` on ${loop.entity_type} ${loop.entity_ids[0]}` : ""; return `▎ "${summary}"${entity} — ${reasonLabel} (session ${loop.session_id.slice(0, 8)}, ${new Date(loop.created_at).toISOString().slice(0, 16)}). Continue?`; } //# sourceMappingURL=index.js.map