@withpica/mcp-server 1.3.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/.claude/settings.local.json +5 -0
- package/CHANGELOG.md +1850 -0
- package/README.md +304 -0
- package/assets/fonts/GeistSans-Light.woff2 +0 -0
- package/assets/fonts/InstrumentSerif-Italic.woff2 +0 -0
- package/assets/fonts/InstrumentSerif-Regular.woff2 +0 -0
- package/dist/apps/download.d.ts +2 -0
- package/dist/apps/download.d.ts.map +1 -0
- package/dist/apps/download.js +125 -0
- package/dist/apps/download.js.map +1 -0
- package/dist/apps/generated/shared-bundle.d.ts +5 -0
- package/dist/apps/generated/shared-bundle.d.ts.map +1 -0
- package/dist/apps/generated/shared-bundle.js +7 -0
- package/dist/apps/generated/shared-bundle.js.map +1 -0
- package/dist/apps/release.d.ts +2 -0
- package/dist/apps/release.d.ts.map +1 -0
- package/dist/apps/release.js +69 -0
- package/dist/apps/release.js.map +1 -0
- package/dist/apps/shared.d.ts +15 -0
- package/dist/apps/shared.d.ts.map +1 -0
- package/dist/apps/shared.js +480 -0
- package/dist/apps/shared.js.map +1 -0
- package/dist/apps/upload.d.ts +2 -0
- package/dist/apps/upload.d.ts.map +1 -0
- package/dist/apps/upload.js +280 -0
- package/dist/apps/upload.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +73 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/index.d.ts +86 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +645 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/resources/agent-guide.d.ts +15 -0
- package/dist/resources/agent-guide.d.ts.map +1 -0
- package/dist/resources/agent-guide.js +284 -0
- package/dist/resources/agent-guide.js.map +1 -0
- package/dist/resources/index.d.ts +66 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +510 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/llms-primer.d.ts +2 -0
- package/dist/resources/llms-primer.d.ts.map +1 -0
- package/dist/resources/llms-primer.js +68 -0
- package/dist/resources/llms-primer.js.map +1 -0
- package/dist/resources/required-schemas.generated.d.ts +455 -0
- package/dist/resources/required-schemas.generated.d.ts.map +1 -0
- package/dist/resources/required-schemas.generated.js +1041 -0
- package/dist/resources/required-schemas.generated.js.map +1 -0
- package/dist/resources/required-schemas.source.d.ts +53 -0
- package/dist/resources/required-schemas.source.d.ts.map +1 -0
- package/dist/resources/required-schemas.source.js +493 -0
- package/dist/resources/required-schemas.source.js.map +1 -0
- package/dist/resources/welcome.d.ts +14 -0
- package/dist/resources/welcome.d.ts.map +1 -0
- package/dist/resources/welcome.js +26 -0
- package/dist/resources/welcome.js.map +1 -0
- package/dist/server-instructions.d.ts +60 -0
- package/dist/server-instructions.d.ts.map +1 -0
- package/dist/server-instructions.js +93 -0
- package/dist/server-instructions.js.map +1 -0
- package/dist/server.d.ts +52 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +334 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/access-simulate.d.ts +23 -0
- package/dist/tools/access-simulate.d.ts.map +1 -0
- package/dist/tools/access-simulate.js +162 -0
- package/dist/tools/access-simulate.js.map +1 -0
- package/dist/tools/agent-identity.d.ts +36 -0
- package/dist/tools/agent-identity.d.ts.map +1 -0
- package/dist/tools/agent-identity.js +274 -0
- package/dist/tools/agent-identity.js.map +1 -0
- package/dist/tools/agreement-types.d.ts +27 -0
- package/dist/tools/agreement-types.d.ts.map +1 -0
- package/dist/tools/agreement-types.js +281 -0
- package/dist/tools/agreement-types.js.map +1 -0
- package/dist/tools/agreements.d.ts +20 -0
- package/dist/tools/agreements.d.ts.map +1 -0
- package/dist/tools/agreements.js +282 -0
- package/dist/tools/agreements.js.map +1 -0
- package/dist/tools/analytics.d.ts +20 -0
- package/dist/tools/analytics.d.ts.map +1 -0
- package/dist/tools/analytics.js +130 -0
- package/dist/tools/analytics.js.map +1 -0
- package/dist/tools/app-tools.d.ts +15 -0
- package/dist/tools/app-tools.d.ts.map +1 -0
- package/dist/tools/app-tools.js +388 -0
- package/dist/tools/app-tools.js.map +1 -0
- package/dist/tools/assets.d.ts +25 -0
- package/dist/tools/assets.d.ts.map +1 -0
- package/dist/tools/assets.js +454 -0
- package/dist/tools/assets.js.map +1 -0
- package/dist/tools/audio-files.d.ts +20 -0
- package/dist/tools/audio-files.d.ts.map +1 -0
- package/dist/tools/audio-files.js +409 -0
- package/dist/tools/audio-files.js.map +1 -0
- package/dist/tools/audit.d.ts +19 -0
- package/dist/tools/audit.d.ts.map +1 -0
- package/dist/tools/audit.js +58 -0
- package/dist/tools/audit.js.map +1 -0
- package/dist/tools/auth.d.ts +22 -0
- package/dist/tools/auth.d.ts.map +1 -0
- package/dist/tools/auth.js +212 -0
- package/dist/tools/auth.js.map +1 -0
- package/dist/tools/bulk.d.ts +22 -0
- package/dist/tools/bulk.d.ts.map +1 -0
- package/dist/tools/bulk.js +164 -0
- package/dist/tools/bulk.js.map +1 -0
- package/dist/tools/calendar.d.ts +15 -0
- package/dist/tools/calendar.d.ts.map +1 -0
- package/dist/tools/calendar.js +68 -0
- package/dist/tools/calendar.js.map +1 -0
- package/dist/tools/collaborators.d.ts +21 -0
- package/dist/tools/collaborators.d.ts.map +1 -0
- package/dist/tools/collaborators.js +381 -0
- package/dist/tools/collaborators.js.map +1 -0
- package/dist/tools/comparisons.d.ts +22 -0
- package/dist/tools/comparisons.d.ts.map +1 -0
- package/dist/tools/comparisons.js +80 -0
- package/dist/tools/comparisons.js.map +1 -0
- package/dist/tools/credits.d.ts +39 -0
- package/dist/tools/credits.d.ts.map +1 -0
- package/dist/tools/credits.js +541 -0
- package/dist/tools/credits.js.map +1 -0
- package/dist/tools/custody-hints.d.ts +16 -0
- package/dist/tools/custody-hints.d.ts.map +1 -0
- package/dist/tools/custody-hints.js +27 -0
- package/dist/tools/custody-hints.js.map +1 -0
- package/dist/tools/custody.d.ts +38 -0
- package/dist/tools/custody.d.ts.map +1 -0
- package/dist/tools/custody.js +281 -0
- package/dist/tools/custody.js.map +1 -0
- package/dist/tools/dashboard.d.ts +22 -0
- package/dist/tools/dashboard.d.ts.map +1 -0
- package/dist/tools/dashboard.js +258 -0
- package/dist/tools/dashboard.js.map +1 -0
- package/dist/tools/directory.d.ts +15 -0
- package/dist/tools/directory.d.ts.map +1 -0
- package/dist/tools/directory.js +107 -0
- package/dist/tools/directory.js.map +1 -0
- package/dist/tools/discovery.d.ts +49 -0
- package/dist/tools/discovery.d.ts.map +1 -0
- package/dist/tools/discovery.js +851 -0
- package/dist/tools/discovery.js.map +1 -0
- package/dist/tools/disputes.d.ts +18 -0
- package/dist/tools/disputes.d.ts.map +1 -0
- package/dist/tools/disputes.js +62 -0
- package/dist/tools/disputes.js.map +1 -0
- package/dist/tools/documents.d.ts +15 -0
- package/dist/tools/documents.d.ts.map +1 -0
- package/dist/tools/documents.js +37 -0
- package/dist/tools/documents.js.map +1 -0
- package/dist/tools/duplicates.d.ts +25 -0
- package/dist/tools/duplicates.d.ts.map +1 -0
- package/dist/tools/duplicates.js +108 -0
- package/dist/tools/duplicates.js.map +1 -0
- package/dist/tools/enrichment.d.ts +56 -0
- package/dist/tools/enrichment.d.ts.map +1 -0
- package/dist/tools/enrichment.js +616 -0
- package/dist/tools/enrichment.js.map +1 -0
- package/dist/tools/exports.d.ts +19 -0
- package/dist/tools/exports.d.ts.map +1 -0
- package/dist/tools/exports.js +184 -0
- package/dist/tools/exports.js.map +1 -0
- package/dist/tools/feedback.d.ts +22 -0
- package/dist/tools/feedback.d.ts.map +1 -0
- package/dist/tools/feedback.js +100 -0
- package/dist/tools/feedback.js.map +1 -0
- package/dist/tools/forbidden-keywords.d.ts +62 -0
- package/dist/tools/forbidden-keywords.d.ts.map +1 -0
- package/dist/tools/forbidden-keywords.js +99 -0
- package/dist/tools/forbidden-keywords.js.map +1 -0
- package/dist/tools/gap-hints.d.ts +53 -0
- package/dist/tools/gap-hints.d.ts.map +1 -0
- package/dist/tools/gap-hints.js +245 -0
- package/dist/tools/gap-hints.js.map +1 -0
- package/dist/tools/groups.d.ts +29 -0
- package/dist/tools/groups.d.ts.map +1 -0
- package/dist/tools/groups.js +186 -0
- package/dist/tools/groups.js.map +1 -0
- package/dist/tools/import-documents.d.ts +21 -0
- package/dist/tools/import-documents.d.ts.map +1 -0
- package/dist/tools/import-documents.js +206 -0
- package/dist/tools/import-documents.js.map +1 -0
- package/dist/tools/import.d.ts +31 -0
- package/dist/tools/import.d.ts.map +1 -0
- package/dist/tools/import.js +610 -0
- package/dist/tools/import.js.map +1 -0
- package/dist/tools/index.d.ts +293 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +1182 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/integrations.d.ts +19 -0
- package/dist/tools/integrations.d.ts.map +1 -0
- package/dist/tools/integrations.js +120 -0
- package/dist/tools/integrations.js.map +1 -0
- package/dist/tools/labels.d.ts +20 -0
- package/dist/tools/labels.d.ts.map +1 -0
- package/dist/tools/labels.js +48 -0
- package/dist/tools/labels.js.map +1 -0
- package/dist/tools/licensing.d.ts +40 -0
- package/dist/tools/licensing.d.ts.map +1 -0
- package/dist/tools/licensing.js +436 -0
- package/dist/tools/licensing.js.map +1 -0
- package/dist/tools/memory.d.ts +21 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +120 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/metadata.d.ts +15 -0
- package/dist/tools/metadata.d.ts.map +1 -0
- package/dist/tools/metadata.js +1532 -0
- package/dist/tools/metadata.js.map +1 -0
- package/dist/tools/multimedia.d.ts +19 -0
- package/dist/tools/multimedia.d.ts.map +1 -0
- package/dist/tools/multimedia.js +371 -0
- package/dist/tools/multimedia.js.map +1 -0
- package/dist/tools/my-reported-issues.d.ts +32 -0
- package/dist/tools/my-reported-issues.d.ts.map +1 -0
- package/dist/tools/my-reported-issues.js +123 -0
- package/dist/tools/my-reported-issues.js.map +1 -0
- package/dist/tools/notes.d.ts +21 -0
- package/dist/tools/notes.d.ts.map +1 -0
- package/dist/tools/notes.js +115 -0
- package/dist/tools/notes.js.map +1 -0
- package/dist/tools/notifications.d.ts +28 -0
- package/dist/tools/notifications.d.ts.map +1 -0
- package/dist/tools/notifications.js +417 -0
- package/dist/tools/notifications.js.map +1 -0
- package/dist/tools/onboarding.d.ts +24 -0
- package/dist/tools/onboarding.d.ts.map +1 -0
- package/dist/tools/onboarding.js +81 -0
- package/dist/tools/onboarding.js.map +1 -0
- package/dist/tools/people.d.ts +254 -0
- package/dist/tools/people.d.ts.map +1 -0
- package/dist/tools/people.js +481 -0
- package/dist/tools/people.js.map +1 -0
- package/dist/tools/projects.d.ts +20 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +316 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/public-filter.d.ts +43 -0
- package/dist/tools/public-filter.d.ts.map +1 -0
- package/dist/tools/public-filter.js +356 -0
- package/dist/tools/public-filter.js.map +1 -0
- package/dist/tools/publishers.d.ts +30 -0
- package/dist/tools/publishers.d.ts.map +1 -0
- package/dist/tools/publishers.js +105 -0
- package/dist/tools/publishers.js.map +1 -0
- package/dist/tools/purchases.d.ts +27 -0
- package/dist/tools/purchases.d.ts.map +1 -0
- package/dist/tools/purchases.js +9 -0
- package/dist/tools/purchases.js.map +1 -0
- package/dist/tools/recording-attribution-hints.d.ts +24 -0
- package/dist/tools/recording-attribution-hints.d.ts.map +1 -0
- package/dist/tools/recording-attribution-hints.js +27 -0
- package/dist/tools/recording-attribution-hints.js.map +1 -0
- package/dist/tools/recordings.d.ts +96 -0
- package/dist/tools/recordings.d.ts.map +1 -0
- package/dist/tools/recordings.js +564 -0
- package/dist/tools/recordings.js.map +1 -0
- package/dist/tools/recovery-hints.d.ts +40 -0
- package/dist/tools/recovery-hints.d.ts.map +1 -0
- package/dist/tools/recovery-hints.js +827 -0
- package/dist/tools/recovery-hints.js.map +1 -0
- package/dist/tools/release-rich.d.ts +31 -0
- package/dist/tools/release-rich.d.ts.map +1 -0
- package/dist/tools/release-rich.js +245 -0
- package/dist/tools/release-rich.js.map +1 -0
- package/dist/tools/releases.d.ts +36 -0
- package/dist/tools/releases.d.ts.map +1 -0
- package/dist/tools/releases.js +649 -0
- package/dist/tools/releases.js.map +1 -0
- package/dist/tools/report-issue.d.ts +21 -0
- package/dist/tools/report-issue.d.ts.map +1 -0
- package/dist/tools/report-issue.js +101 -0
- package/dist/tools/report-issue.js.map +1 -0
- package/dist/tools/royalties.d.ts +23 -0
- package/dist/tools/royalties.d.ts.map +1 -0
- package/dist/tools/royalties.js +262 -0
- package/dist/tools/royalties.js.map +1 -0
- package/dist/tools/search.d.ts +30 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +115 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/send.d.ts +17 -0
- package/dist/tools/send.d.ts.map +1 -0
- package/dist/tools/send.js +188 -0
- package/dist/tools/send.js.map +1 -0
- package/dist/tools/sessions.d.ts +18 -0
- package/dist/tools/sessions.d.ts.map +1 -0
- package/dist/tools/sessions.js +153 -0
- package/dist/tools/sessions.js.map +1 -0
- package/dist/tools/settings.d.ts +23 -0
- package/dist/tools/settings.d.ts.map +1 -0
- package/dist/tools/settings.js +365 -0
- package/dist/tools/settings.js.map +1 -0
- package/dist/tools/share-links.d.ts +22 -0
- package/dist/tools/share-links.d.ts.map +1 -0
- package/dist/tools/share-links.js +188 -0
- package/dist/tools/share-links.js.map +1 -0
- package/dist/tools/signup.d.ts +26 -0
- package/dist/tools/signup.d.ts.map +1 -0
- package/dist/tools/signup.js +266 -0
- package/dist/tools/signup.js.map +1 -0
- package/dist/tools/split-sheets.d.ts +25 -0
- package/dist/tools/split-sheets.d.ts.map +1 -0
- package/dist/tools/split-sheets.js +309 -0
- package/dist/tools/split-sheets.js.map +1 -0
- package/dist/tools/storage-config.d.ts +13 -0
- package/dist/tools/storage-config.d.ts.map +1 -0
- package/dist/tools/storage-config.js +245 -0
- package/dist/tools/storage-config.js.map +1 -0
- package/dist/tools/subscription.d.ts +60 -0
- package/dist/tools/subscription.d.ts.map +1 -0
- package/dist/tools/subscription.js +440 -0
- package/dist/tools/subscription.js.map +1 -0
- package/dist/tools/sync-placements.d.ts +31 -0
- package/dist/tools/sync-placements.d.ts.map +1 -0
- package/dist/tools/sync-placements.js +431 -0
- package/dist/tools/sync-placements.js.map +1 -0
- package/dist/tools/team.d.ts +22 -0
- package/dist/tools/team.d.ts.map +1 -0
- package/dist/tools/team.js +144 -0
- package/dist/tools/team.js.map +1 -0
- package/dist/tools/telegram.d.ts +21 -0
- package/dist/tools/telegram.d.ts.map +1 -0
- package/dist/tools/telegram.js +144 -0
- package/dist/tools/telegram.js.map +1 -0
- package/dist/tools/uploads.d.ts +17 -0
- package/dist/tools/uploads.d.ts.map +1 -0
- package/dist/tools/uploads.js +165 -0
- package/dist/tools/uploads.js.map +1 -0
- package/dist/tools/works.d.ts +71 -0
- package/dist/tools/works.d.ts.map +1 -0
- package/dist/tools/works.js +694 -0
- package/dist/tools/works.js.map +1 -0
- package/package.json +54 -0
- package/scripts/build-required-schemas.ts +233 -0
- package/scripts/bundle-apps.ts +61 -0
- package/scripts/refresh-schema-mirror.ts +182 -0
- package/server.json +20 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
// Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
|
|
2
|
+
import { formatSuccess, formatStructuredList, withBillingGate, mapGapsToHints, pushSectionError, } from "@withpica/mcp-utils";
|
|
3
|
+
import { buildCustodyHints } from "./custody-hints.js";
|
|
4
|
+
import { computeWorkGapHints } from "./gap-hints.js";
|
|
5
|
+
/**
|
|
6
|
+
* Canonical write-schema for `works`. Every property below must
|
|
7
|
+
* correspond to a real column on `public.works` — verified against
|
|
8
|
+
* `lib/db/database.types.ts`. Keep this list tight: reintroducing a
|
|
9
|
+
* phantom field is the exact regression ADR-174 Phase 2 exists to
|
|
10
|
+
* prevent. The HTTP route at `app/api/admin/works/[id]/route.ts`
|
|
11
|
+
* carries a broader allow-list that includes admin-UI-posted satellite
|
|
12
|
+
* fields; this list is the narrower MCP-agent contract.
|
|
13
|
+
*/
|
|
14
|
+
export const WORK_WRITE_PROPERTIES = {
|
|
15
|
+
title: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Work title",
|
|
18
|
+
},
|
|
19
|
+
work_type: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Type of work",
|
|
22
|
+
enum: ["song", "instrumental", "library", "demo", "sample"],
|
|
23
|
+
},
|
|
24
|
+
work_status: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Work status (state machine validates transitions — demo → unreleased → released, etc.)",
|
|
27
|
+
enum: ["demo", "unreleased", "released", "pitched", "placed", "archived"],
|
|
28
|
+
},
|
|
29
|
+
iswc: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "International Standard Musical Work Code (composition)",
|
|
32
|
+
},
|
|
33
|
+
isrc: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "International Standard Recording Code. Bridges publishing (works) and masters (recordings).",
|
|
36
|
+
},
|
|
37
|
+
tunecode: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "PRS tunecode (e.g. 851348GT)",
|
|
40
|
+
},
|
|
41
|
+
prs_work_id: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "PRS work ID",
|
|
44
|
+
},
|
|
45
|
+
spotify_track_uri: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "Spotify track URI (e.g. spotify:track:0b8ZtRm9u3xSzRazABeHFF).",
|
|
48
|
+
},
|
|
49
|
+
duration_seconds: {
|
|
50
|
+
type: "number",
|
|
51
|
+
description: "Duration in seconds",
|
|
52
|
+
},
|
|
53
|
+
tempo: {
|
|
54
|
+
type: "number",
|
|
55
|
+
description: "BPM (beats per minute)",
|
|
56
|
+
},
|
|
57
|
+
key: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: "Musical key (e.g. 'C major', 'Am')",
|
|
60
|
+
},
|
|
61
|
+
genre: {
|
|
62
|
+
type: "array",
|
|
63
|
+
items: { type: "string" },
|
|
64
|
+
description: "Genre tags (e.g. ['R&B', 'Soul'])",
|
|
65
|
+
},
|
|
66
|
+
mood: {
|
|
67
|
+
type: "array",
|
|
68
|
+
items: { type: "string" },
|
|
69
|
+
description: "Mood tags (e.g. ['Uplifting', 'Reflective'])",
|
|
70
|
+
},
|
|
71
|
+
primary_artist: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "Primary artist name (freetext display value)",
|
|
74
|
+
},
|
|
75
|
+
primary_artist_id: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "Primary artist person ID (FK to people)",
|
|
78
|
+
},
|
|
79
|
+
display_artist: {
|
|
80
|
+
type: "string",
|
|
81
|
+
description: "Display-friendly artist string (e.g. 'Jane Doe ft. John Smith')",
|
|
82
|
+
},
|
|
83
|
+
publisher_name: {
|
|
84
|
+
type: "string",
|
|
85
|
+
description: "Publisher name (legacy freetext). Prefer linking via publisher identity where possible.",
|
|
86
|
+
},
|
|
87
|
+
lyrics: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "Lyrics text",
|
|
90
|
+
},
|
|
91
|
+
lyrics_language: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "Language of lyrics (ISO 639-1, e.g. 'en')",
|
|
94
|
+
},
|
|
95
|
+
published: {
|
|
96
|
+
type: "boolean",
|
|
97
|
+
description: "Whether the work is published on the public directory",
|
|
98
|
+
},
|
|
99
|
+
notes: {
|
|
100
|
+
type: "string",
|
|
101
|
+
description: "Internal notes",
|
|
102
|
+
},
|
|
103
|
+
// Declared jsonb extension points — ADR-174 Phase 2c inverse-phantom fix.
|
|
104
|
+
// These keys are not columns on public.works; the service routes them to
|
|
105
|
+
// the `work_licensing` satellite via extractSatelliteFields() (see
|
|
106
|
+
// lib/services/works/constants.ts WORK_SATELLITE_FIELDS). Same shape as
|
|
107
|
+
// the parallel fields on `pica_recordings_update` (recordings.ts:193).
|
|
108
|
+
ai_disclosure: {
|
|
109
|
+
type: "object",
|
|
110
|
+
description: "AI disclosure object (UK AI Copyright compliance). Fields: composition, performance, production, mastering ('none'|'assisted'|'generated'), tools_used (string[]), disclosure_date, disclosed_by.",
|
|
111
|
+
},
|
|
112
|
+
provenance_attestation: {
|
|
113
|
+
type: "object",
|
|
114
|
+
description: "Human creation proof. Fields: human_created (bool), attested_by, attested_at, attestation_method, supporting_evidence, ip_address, signature_hash, certificate_id.",
|
|
115
|
+
},
|
|
116
|
+
training_rights: {
|
|
117
|
+
type: "object",
|
|
118
|
+
description: "B2B ML licensing compliance object. Fields: available_for_training (bool), training_terms, minimum_fee, excluded_uses, permitted_uses, geographic_restrictions, requires_attribution, requires_notification, license_duration, updated_at, updated_by.",
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
export class WorksTools {
|
|
122
|
+
pica;
|
|
123
|
+
constructor(pica) {
|
|
124
|
+
this.pica = pica;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get all works tools
|
|
128
|
+
*/
|
|
129
|
+
getTools() {
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
definition: {
|
|
133
|
+
name: "pica_works_query",
|
|
134
|
+
description: "Find works by any criteria — text search, verified status, or health filter. " +
|
|
135
|
+
"→ then: pica_works_inspect (details for a specific work)",
|
|
136
|
+
workflows: "infrastructure",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: "object",
|
|
139
|
+
properties: {
|
|
140
|
+
query: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: "Search by title, ISWC, or artist name",
|
|
143
|
+
},
|
|
144
|
+
verified: {
|
|
145
|
+
type: "boolean",
|
|
146
|
+
description: "Filter by verification status",
|
|
147
|
+
},
|
|
148
|
+
health_filter: {
|
|
149
|
+
type: "string",
|
|
150
|
+
enum: ["needs_attention", "low_completeness"],
|
|
151
|
+
description: "Filter by health status — needs_attention (missing credits, no ISWC) or low_completeness",
|
|
152
|
+
},
|
|
153
|
+
limit: {
|
|
154
|
+
type: "number",
|
|
155
|
+
description: "Max results (default: 50)",
|
|
156
|
+
},
|
|
157
|
+
offset: {
|
|
158
|
+
type: "number",
|
|
159
|
+
description: "Pagination offset",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
executor: this.queryWorks.bind(this),
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
definition: {
|
|
168
|
+
name: "pica_works_inspect",
|
|
169
|
+
description: "Get details and quality data for a specific work. Omit sections for everything. " +
|
|
170
|
+
"Use sections=['enrichment'] to see cascade provenance (which source wrote each field), " +
|
|
171
|
+
"completeness_score, completeness_breakdown, AND live enrichment-job statuses (spotify, mlc, acrcloud, audio analysis) " +
|
|
172
|
+
"in one response. " +
|
|
173
|
+
"→ then: pica_credits_update (fix credits), pica_resolve_work (fill identifiers + metadata), pica_run_work_cascade (retry)",
|
|
174
|
+
workflows: "infrastructure",
|
|
175
|
+
inputSchema: {
|
|
176
|
+
type: "object",
|
|
177
|
+
properties: {
|
|
178
|
+
id: {
|
|
179
|
+
type: "string",
|
|
180
|
+
description: "Work ID",
|
|
181
|
+
},
|
|
182
|
+
sections: {
|
|
183
|
+
type: "array",
|
|
184
|
+
items: {
|
|
185
|
+
type: "string",
|
|
186
|
+
enum: [
|
|
187
|
+
"basic",
|
|
188
|
+
"credits",
|
|
189
|
+
"recordings",
|
|
190
|
+
"agreements",
|
|
191
|
+
"health",
|
|
192
|
+
"completeness",
|
|
193
|
+
"registration",
|
|
194
|
+
"score",
|
|
195
|
+
"provenance",
|
|
196
|
+
"enrichment",
|
|
197
|
+
"production_assets",
|
|
198
|
+
"releases",
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
description: 'Which data sections to include. Omit for all sections. Use ["basic"] for lightweight lookup.',
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
required: ["id"],
|
|
205
|
+
},
|
|
206
|
+
// ADR-199 — work status state machine mirrored from
|
|
207
|
+
// lib/utils/state-machine.ts workStatusMachine. Build-step
|
|
208
|
+
// assertion (Phase D) catches drift between this map and the
|
|
209
|
+
// DB CHECK constraint on works.status.
|
|
210
|
+
validTransitionsFrom: {
|
|
211
|
+
demo: ["unreleased", "pitched", "archived"],
|
|
212
|
+
unreleased: ["released", "pitched", "archived"],
|
|
213
|
+
pitched: ["placed", "unreleased", "released", "archived"],
|
|
214
|
+
placed: ["released", "archived"],
|
|
215
|
+
released: ["archived"],
|
|
216
|
+
archived: ["unreleased", "demo"],
|
|
217
|
+
},
|
|
218
|
+
nextSteps: [
|
|
219
|
+
{
|
|
220
|
+
tool: "pica_credits_update",
|
|
221
|
+
reason: "Fix or fill writer/composer credits when the inspect surfaces gaps.",
|
|
222
|
+
when: "on_success",
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
tool: "pica_resolve_work",
|
|
226
|
+
reason: "Fill identifier gaps (ISWC, ISRC) and metadata via the enrichment cascade.",
|
|
227
|
+
when: "on_success",
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
executor: this.inspectWork.bind(this),
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
definition: {
|
|
235
|
+
name: "pica_works_create",
|
|
236
|
+
description: "Create a new musical work. IMPORTANT: call pica_works_query first to check for duplicates — same-title works cause split and registration issues. " +
|
|
237
|
+
"Album and release metadata (album_name, album_art_url, track_number, upc, spotify_url) live on `pica_releases_create/update`, not here. " +
|
|
238
|
+
"For label metadata, set it on the release via pica_releases_*.label_organization_id (resolve the name via pica_labels_query first). " +
|
|
239
|
+
"After several mutations, surface a pica_dashboard_briefing to the user. " +
|
|
240
|
+
"→ before: pica_works_query (check duplicates) → then: pica_credits_update (add writers), pica_works_inspect (check gaps), pica_resolve_work (fill identifiers)",
|
|
241
|
+
workflows: ["work-required"],
|
|
242
|
+
inputSchema: {
|
|
243
|
+
type: "object",
|
|
244
|
+
properties: WORK_WRITE_PROPERTIES,
|
|
245
|
+
required: ["title", "work_type"],
|
|
246
|
+
},
|
|
247
|
+
nextSteps: [
|
|
248
|
+
{
|
|
249
|
+
tool: "pica_credits_update",
|
|
250
|
+
reason: "Add writer/composer credits to the new work — required before registration.",
|
|
251
|
+
when: "on_success",
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
tool: "pica_works_inspect",
|
|
255
|
+
reason: "Read back the created work to confirm fields persisted and surface any gaps.",
|
|
256
|
+
when: "on_success",
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
executor: withBillingGate(this.pica, "work creation", this.createWork.bind(this)),
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
definition: {
|
|
264
|
+
name: "pica_works_update",
|
|
265
|
+
description: "Update an existing musical work. Only real `works` columns are accepted — " +
|
|
266
|
+
"album and release metadata (album_name, album_art_url, track_number, upc, spotify_url) must go through pica_releases_create/update, not here. " +
|
|
267
|
+
"Audio-trait satellite fields (energy, vocal_type, available_for_licensing, danceability) are not exposed through MCP; set them through a release or recording path. " +
|
|
268
|
+
"Three jsonb compliance fields (ai_disclosure, provenance_attestation, training_rights) are satellite-routed to work_licensing and ARE set-able here. " +
|
|
269
|
+
"→ then: pica_works_inspect (verify changes persisted)",
|
|
270
|
+
workflows: ["work-required"],
|
|
271
|
+
inputSchema: {
|
|
272
|
+
type: "object",
|
|
273
|
+
properties: {
|
|
274
|
+
id: {
|
|
275
|
+
type: "string",
|
|
276
|
+
description: "Work ID (required)",
|
|
277
|
+
},
|
|
278
|
+
...WORK_WRITE_PROPERTIES,
|
|
279
|
+
},
|
|
280
|
+
required: ["id"],
|
|
281
|
+
},
|
|
282
|
+
nextSteps: [
|
|
283
|
+
{
|
|
284
|
+
tool: "pica_works_inspect",
|
|
285
|
+
reason: "Read back the work after update to confirm every field round-tripped — silent persistence drops are real.",
|
|
286
|
+
when: "on_success",
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
executor: withBillingGate(this.pica, "work update", this.updateWork.bind(this)),
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
definition: {
|
|
294
|
+
name: "pica_works_delete",
|
|
295
|
+
description: "Delete a musical work",
|
|
296
|
+
workflows: "infrastructure",
|
|
297
|
+
inputSchema: {
|
|
298
|
+
type: "object",
|
|
299
|
+
properties: {
|
|
300
|
+
id: {
|
|
301
|
+
type: "string",
|
|
302
|
+
description: "Work ID",
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
required: ["id"],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
executor: withBillingGate(this.pica, "work deletion", this.deleteWork.bind(this)),
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
definition: {
|
|
312
|
+
name: "pica_works_verify",
|
|
313
|
+
description: "Mark a work as verified",
|
|
314
|
+
workflows: "infrastructure",
|
|
315
|
+
inputSchema: {
|
|
316
|
+
type: "object",
|
|
317
|
+
properties: {
|
|
318
|
+
id: {
|
|
319
|
+
type: "string",
|
|
320
|
+
description: "Work ID",
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
required: ["id"],
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
executor: withBillingGate(this.pica, "work verification", this.verifyWork.bind(this)),
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
definition: {
|
|
330
|
+
name: "pica_works_bulk_delete",
|
|
331
|
+
description: "Delete multiple works at once",
|
|
332
|
+
workflows: "infrastructure",
|
|
333
|
+
inputSchema: {
|
|
334
|
+
type: "object",
|
|
335
|
+
properties: {
|
|
336
|
+
ids: {
|
|
337
|
+
type: "array",
|
|
338
|
+
description: "Array of work IDs to delete",
|
|
339
|
+
items: {
|
|
340
|
+
type: "string",
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
required: ["ids"],
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
executor: withBillingGate(this.pica, "bulk work deletion", this.bulkDeleteWorks.bind(this)),
|
|
348
|
+
},
|
|
349
|
+
];
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Query works — unified search, list, and health filter
|
|
353
|
+
*/
|
|
354
|
+
async queryWorks(args) {
|
|
355
|
+
const limit = args.limit || 50;
|
|
356
|
+
// Health filter routes to health/completeness APIs
|
|
357
|
+
if (args.health_filter === "needs_attention") {
|
|
358
|
+
const result = await this.pica.health.getWorksHealth();
|
|
359
|
+
const works = result?.works || result || [];
|
|
360
|
+
return formatStructuredList(Array.isArray(works) ? works : [works], "work", {
|
|
361
|
+
filter: "needs_attention",
|
|
362
|
+
total: Array.isArray(works) ? works.length : 1,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
if (args.health_filter === "low_completeness") {
|
|
366
|
+
const result = await this.pica.health.getLowScoreWorks();
|
|
367
|
+
const works = Array.isArray(result) ? result : result?.works || [];
|
|
368
|
+
return formatStructuredList(works, "work", {
|
|
369
|
+
filter: "low_completeness",
|
|
370
|
+
total: works.length,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
// Text search or list
|
|
374
|
+
if (args.query) {
|
|
375
|
+
const result = await this.pica.works.search({ query: args.query, limit });
|
|
376
|
+
let items = result.data || result;
|
|
377
|
+
if (!Array.isArray(items))
|
|
378
|
+
items = [];
|
|
379
|
+
return formatStructuredList(items, "work", {
|
|
380
|
+
query: args.query,
|
|
381
|
+
total: result.total || items.length,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
// Default: list with filters
|
|
385
|
+
const result = await this.pica.works.search({ query: args.search, limit });
|
|
386
|
+
let items = result.data || result;
|
|
387
|
+
if (!Array.isArray(items))
|
|
388
|
+
items = [];
|
|
389
|
+
// Client-side verified filter
|
|
390
|
+
if (args.verified !== undefined) {
|
|
391
|
+
items = items.filter((work) => work.is_verified === args.verified);
|
|
392
|
+
}
|
|
393
|
+
return formatStructuredList(items, "work", {
|
|
394
|
+
total: result.total || items.length,
|
|
395
|
+
limit,
|
|
396
|
+
hasMore: result.hasMore,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Inspect work — unified detail view with selectable sections
|
|
401
|
+
*/
|
|
402
|
+
async inspectWork(args) {
|
|
403
|
+
const sections = args.sections;
|
|
404
|
+
const allSections = !sections || sections.length === 0;
|
|
405
|
+
// ADR-201 closeout (2026-04-27): `health` and `score` are org-wide
|
|
406
|
+
// metrics that don't take a workId and cost ~25s and ~2s respectively
|
|
407
|
+
// on staging. Inspect-one-work shouldn't pay that — agents that want
|
|
408
|
+
// org-wide metrics call them explicitly. Keep them off the default
|
|
409
|
+
// `allSections` surface but still callable via explicit `sections`.
|
|
410
|
+
const ORG_WIDE_OPT_IN = new Set(["health", "score"]);
|
|
411
|
+
const has = (s) => allSections ? !ORG_WIDE_OPT_IN.has(s) : sections.includes(s);
|
|
412
|
+
const result = {};
|
|
413
|
+
const hints = [];
|
|
414
|
+
// ADR-224 — section-level failures push to errors[] and the host
|
|
415
|
+
// log pipeline (via pushSectionError) regardless of flag state.
|
|
416
|
+
// The flag controls whether the public response advertises errors[]
|
|
417
|
+
// and whether failed sections set the field to null vs the legacy
|
|
418
|
+
// empty-fallback. Default OFF in Phase 1; flips to default-on at
|
|
419
|
+
// Phase 4 after the soak window catches any caller depending on
|
|
420
|
+
// the empty-array shape.
|
|
421
|
+
const useStructured = process.env.ADR_224_STRUCTURED_ERRORS === "1";
|
|
422
|
+
const errors = [];
|
|
423
|
+
const TOOL_NAME = "pica_works_inspect";
|
|
424
|
+
// Basic — always fetched (either via entityContext.getWorkFull or works.get)
|
|
425
|
+
if (allSections) {
|
|
426
|
+
const full = await this.pica.entityContext.getWorkFull(args.id);
|
|
427
|
+
const data = full;
|
|
428
|
+
result.work = data?.work || data;
|
|
429
|
+
result.credits = data?.credits;
|
|
430
|
+
result.recordings = data?.recordings;
|
|
431
|
+
result.agreements = data?.agreements;
|
|
432
|
+
}
|
|
433
|
+
else if (has("basic")) {
|
|
434
|
+
const work = await this.pica.works.get(args.id);
|
|
435
|
+
result.work = work;
|
|
436
|
+
// Enrich with ISRC prefix intelligence if work has ISRC
|
|
437
|
+
if (work?.isrc) {
|
|
438
|
+
try {
|
|
439
|
+
result.prefix_intel = await this.pica.recordings.prefixLookup(work.isrc);
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
/* best-effort */
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// ADR-201 closeout (2026-04-27): the section sub-calls used to run
|
|
447
|
+
// sequentially. With ~8 calls each taking ~1s and one (works-health)
|
|
448
|
+
// sometimes 25s+, the function would frequently exceed Vercel's 30s
|
|
449
|
+
// maxDuration on the default `allSections` path. Fan out everything
|
|
450
|
+
// in parallel — each branch still owns its own try/catch fallback so
|
|
451
|
+
// a single failing sub-call doesn't poison the whole inspect.
|
|
452
|
+
const sectionTasks = [];
|
|
453
|
+
// Targeted basic sections — only when caller listed them explicitly.
|
|
454
|
+
if (!allSections) {
|
|
455
|
+
if (has("credits") && !result.credits) {
|
|
456
|
+
sectionTasks.push((async () => {
|
|
457
|
+
try {
|
|
458
|
+
result.credits = await this.pica.credits.listForWork(args.id);
|
|
459
|
+
}
|
|
460
|
+
catch (e) {
|
|
461
|
+
result.credits = useStructured ? null : [];
|
|
462
|
+
pushSectionError(errors, TOOL_NAME, "credits", e, args.id);
|
|
463
|
+
}
|
|
464
|
+
})());
|
|
465
|
+
}
|
|
466
|
+
if (has("recordings") && !result.recordings) {
|
|
467
|
+
sectionTasks.push((async () => {
|
|
468
|
+
try {
|
|
469
|
+
result.recordings = await this.pica.recordings.getByWork(args.id);
|
|
470
|
+
}
|
|
471
|
+
catch (e) {
|
|
472
|
+
result.recordings = useStructured ? null : [];
|
|
473
|
+
pushSectionError(errors, TOOL_NAME, "recordings", e, args.id);
|
|
474
|
+
}
|
|
475
|
+
})());
|
|
476
|
+
}
|
|
477
|
+
if (has("agreements") && !result.agreements) {
|
|
478
|
+
sectionTasks.push((async () => {
|
|
479
|
+
try {
|
|
480
|
+
result.agreements = await this.pica.agreements.list();
|
|
481
|
+
}
|
|
482
|
+
catch (e) {
|
|
483
|
+
result.agreements = useStructured ? null : [];
|
|
484
|
+
pushSectionError(errors, TOOL_NAME, "agreements", e, args.id);
|
|
485
|
+
}
|
|
486
|
+
})());
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Quality sections — fan out in parallel.
|
|
490
|
+
if (has("health")) {
|
|
491
|
+
sectionTasks.push((async () => {
|
|
492
|
+
try {
|
|
493
|
+
result.health = await this.pica.health.getWorksHealth();
|
|
494
|
+
}
|
|
495
|
+
catch (e) {
|
|
496
|
+
result.health = null;
|
|
497
|
+
pushSectionError(errors, TOOL_NAME, "health", e, args.id);
|
|
498
|
+
}
|
|
499
|
+
})());
|
|
500
|
+
}
|
|
501
|
+
if (has("completeness")) {
|
|
502
|
+
sectionTasks.push((async () => {
|
|
503
|
+
try {
|
|
504
|
+
const completeness = await this.pica.health.getWorkCompleteness(args.id);
|
|
505
|
+
result.completeness = completeness;
|
|
506
|
+
hints.push(...mapGapsToHints(completeness?.gaps || completeness?.missing || []));
|
|
507
|
+
}
|
|
508
|
+
catch (e) {
|
|
509
|
+
result.completeness = null;
|
|
510
|
+
pushSectionError(errors, TOOL_NAME, "completeness", e, args.id);
|
|
511
|
+
}
|
|
512
|
+
})());
|
|
513
|
+
}
|
|
514
|
+
if (has("registration")) {
|
|
515
|
+
sectionTasks.push((async () => {
|
|
516
|
+
try {
|
|
517
|
+
result.registration =
|
|
518
|
+
await this.pica.registration.getWorkCascadeStatus(args.id);
|
|
519
|
+
}
|
|
520
|
+
catch (e) {
|
|
521
|
+
result.registration = null;
|
|
522
|
+
pushSectionError(errors, TOOL_NAME, "registration", e, args.id);
|
|
523
|
+
}
|
|
524
|
+
})());
|
|
525
|
+
}
|
|
526
|
+
if (has("score")) {
|
|
527
|
+
sectionTasks.push((async () => {
|
|
528
|
+
try {
|
|
529
|
+
result.score = await this.pica.picaScore.get();
|
|
530
|
+
}
|
|
531
|
+
catch (e) {
|
|
532
|
+
result.score = null;
|
|
533
|
+
pushSectionError(errors, TOOL_NAME, "score", e, args.id);
|
|
534
|
+
}
|
|
535
|
+
})());
|
|
536
|
+
}
|
|
537
|
+
if (has("provenance")) {
|
|
538
|
+
sectionTasks.push((async () => {
|
|
539
|
+
try {
|
|
540
|
+
result.provenance = await this.pica.analytics.provenanceWork(args.id);
|
|
541
|
+
}
|
|
542
|
+
catch (e) {
|
|
543
|
+
result.provenance = null;
|
|
544
|
+
pushSectionError(errors, TOOL_NAME, "provenance", e, args.id);
|
|
545
|
+
}
|
|
546
|
+
})());
|
|
547
|
+
}
|
|
548
|
+
if (has("enrichment")) {
|
|
549
|
+
sectionTasks.push((async () => {
|
|
550
|
+
try {
|
|
551
|
+
result.enrichment =
|
|
552
|
+
await this.pica.enrichment.getWorkEnrichmentStatus(args.id);
|
|
553
|
+
}
|
|
554
|
+
catch (e) {
|
|
555
|
+
result.enrichment = null;
|
|
556
|
+
pushSectionError(errors, TOOL_NAME, "enrichment", e, args.id);
|
|
557
|
+
}
|
|
558
|
+
})());
|
|
559
|
+
}
|
|
560
|
+
// ADR-156 op #3 Session B Task 5 — surface linked physical assets
|
|
561
|
+
// so the inspect tool is discoverable from the work side.
|
|
562
|
+
if (has("production_assets")) {
|
|
563
|
+
sectionTasks.push((async () => {
|
|
564
|
+
try {
|
|
565
|
+
result.production_assets =
|
|
566
|
+
await this.pica.works.listProductionAssets(args.id);
|
|
567
|
+
}
|
|
568
|
+
catch (e) {
|
|
569
|
+
result.production_assets = useStructured ? null : [];
|
|
570
|
+
pushSectionError(errors, TOOL_NAME, "production_assets", e, args.id);
|
|
571
|
+
}
|
|
572
|
+
})());
|
|
573
|
+
}
|
|
574
|
+
// ADR-173 Decision 2 — every release this work appears on, with
|
|
575
|
+
// its track position. Distinct from the `recordings` section,
|
|
576
|
+
// which lists recordings regardless of release context.
|
|
577
|
+
if (has("releases")) {
|
|
578
|
+
sectionTasks.push((async () => {
|
|
579
|
+
try {
|
|
580
|
+
result.releases = await this.pica.works.listReleases(args.id);
|
|
581
|
+
}
|
|
582
|
+
catch (e) {
|
|
583
|
+
result.releases = useStructured ? null : [];
|
|
584
|
+
pushSectionError(errors, TOOL_NAME, "releases", e, args.id);
|
|
585
|
+
}
|
|
586
|
+
})());
|
|
587
|
+
}
|
|
588
|
+
await Promise.all(sectionTasks);
|
|
589
|
+
// ADR-193 Surface 2 — proactive peer-MCP hints when the flag is on
|
|
590
|
+
// and a real gap exists. Additive on top of the CompletionHint path.
|
|
591
|
+
const gap_hints = computeWorkGapHints(result);
|
|
592
|
+
const withHints = gap_hints.length > 0 ? { ...result, gap_hints } : result;
|
|
593
|
+
// ADR-224 — append errors[] to the public response only when the
|
|
594
|
+
// structured-errors flag is on. In legacy mode, section failures are
|
|
595
|
+
// still pushed to the errors[] array (so stderr observability fires
|
|
596
|
+
// via pushSectionError) but the array is dropped from the response
|
|
597
|
+
// shape so the breaking change ([] → null on failed sections) doesn't
|
|
598
|
+
// ship until Phase 4's flip.
|
|
599
|
+
const enriched = useStructured ? { ...withHints, errors } : withHints;
|
|
600
|
+
return formatSuccess(`Work details${sections ? ` (sections: ${sections.join(", ")})` : ""}`, enriched, hints);
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Create work
|
|
604
|
+
*/
|
|
605
|
+
async createWork(args) {
|
|
606
|
+
const work = await this.pica.works.create(args);
|
|
607
|
+
// Generate completion hints for the newly created work
|
|
608
|
+
const hints = [];
|
|
609
|
+
if (work?.id) {
|
|
610
|
+
try {
|
|
611
|
+
const completeness = await this.pica.health.getWorkCompleteness(work.id);
|
|
612
|
+
hints.push(...mapGapsToHints(completeness?.gaps || completeness?.missing || []));
|
|
613
|
+
}
|
|
614
|
+
catch {
|
|
615
|
+
// Best-effort — don't fail the create
|
|
616
|
+
}
|
|
617
|
+
// Hint about AI disclosure if not set
|
|
618
|
+
if (!args.ai_disclosure) {
|
|
619
|
+
hints.push({
|
|
620
|
+
gap: "no_ai_disclosure",
|
|
621
|
+
suggestion: "Set AI disclosure — is this work human-made, AI-assisted, or AI-generated? (pica_works_update with ai_disclosure: none/assisted/generated)",
|
|
622
|
+
severity: "important",
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
// ADR-158 Plan C Phase 2 — append custody-claim hints so every new work
|
|
627
|
+
// prompts the rights conversation by default.
|
|
628
|
+
hints.push(...buildCustodyHints(work));
|
|
629
|
+
return formatSuccess("Work created successfully", work, hints);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Update work
|
|
633
|
+
*/
|
|
634
|
+
async updateWork(args) {
|
|
635
|
+
const { id, ...updates } = args;
|
|
636
|
+
const work = await this.pica.works.update(id, updates);
|
|
637
|
+
return formatSuccess("Work updated successfully", work);
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Delete work
|
|
641
|
+
*/
|
|
642
|
+
async deleteWork(args) {
|
|
643
|
+
await this.pica.works.delete(args.id);
|
|
644
|
+
return formatSuccess(`Work ${args.id} deleted successfully`);
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Verify work
|
|
648
|
+
*/
|
|
649
|
+
async verifyWork(args) {
|
|
650
|
+
const work = await this.pica.works.verify(args.id);
|
|
651
|
+
// ADR-146: Also check export readiness
|
|
652
|
+
let exportReadiness;
|
|
653
|
+
try {
|
|
654
|
+
const baseUrl = this.pica.works["baseUrl"];
|
|
655
|
+
const apiKey = this.pica.works["apiKey"];
|
|
656
|
+
const res = await fetch(`${baseUrl}/admin/export-readiness/${args.id}`, {
|
|
657
|
+
headers: {
|
|
658
|
+
Authorization: `Bearer ${apiKey}`,
|
|
659
|
+
"Content-Type": "application/json",
|
|
660
|
+
},
|
|
661
|
+
});
|
|
662
|
+
if (res.ok) {
|
|
663
|
+
const json = (await res.json());
|
|
664
|
+
exportReadiness = json.data ?? json;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
catch {
|
|
668
|
+
// Readiness check is best-effort — don't fail verification
|
|
669
|
+
}
|
|
670
|
+
const summaryParts = ["Work verified successfully"];
|
|
671
|
+
if (exportReadiness) {
|
|
672
|
+
summaryParts.push(`Export readiness: ${exportReadiness.ready ? "ready" : "not ready"}`, `Evidence: ${exportReadiness.evidenceStrength}`, `Attestation: ${exportReadiness.attestationCoverage}%`);
|
|
673
|
+
if (Array.isArray(exportReadiness.blockers) &&
|
|
674
|
+
exportReadiness.blockers.length > 0) {
|
|
675
|
+
summaryParts.push(`Blockers (${exportReadiness.blockers.length}):`);
|
|
676
|
+
for (const b of exportReadiness.blockers) {
|
|
677
|
+
summaryParts.push(` ${b.type}: ${b.resolutionHint}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return formatSuccess(summaryParts.join("\n"), {
|
|
682
|
+
...work,
|
|
683
|
+
exportReadiness,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Bulk delete works
|
|
688
|
+
*/
|
|
689
|
+
async bulkDeleteWorks(args) {
|
|
690
|
+
await this.pica.works.bulkDelete(args.ids);
|
|
691
|
+
return formatSuccess(`${args.ids.length} works deleted successfully`);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
//# sourceMappingURL=works.js.map
|