@withpica/mcp-sdk 3.7.1 → 3.9.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/CHANGELOG.md CHANGED
@@ -9,7 +9,60 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
  > matching entry here in the same commit. See the project's "npm publish
10
10
  > discipline" memory entry for the enforcement rationale.
11
11
 
12
- ## [Unreleased]
12
+ ## [3.9.0] - 2026-07-24
13
+
14
+ ### Added
15
+
16
+ - **`AudioFilesResource.bulkAssignSuggested()`** — one-call attach of ALL
17
+ orphan audio whose suggested match is ISRC-high and non-ambiguous
18
+ (`POST /admin/audio-files/bulk-assign-suggested`; pairings recomputed
19
+ server-side, `possible` never bulk-attached). Returns the
20
+ `BulkAssignSuggestedResult` counts + `attached_ids`. Backs the new
21
+ `pica_audio_bulk_assign_suggested` MCP tool (agent feedback 20f592ff —
22
+ an agent hand-looped `pica_audio_assign` over a 74-file queue).
23
+ - **`AudioFilesResource.dismissMatch(id, recordingId)`** — reject a
24
+ suggested audio→recording pairing so it never resurfaces (repeat
25
+ dismissals are server-side no-op successes). Backs the new
26
+ `pica_audio_dismiss_match` MCP tool.
27
+
28
+ ## [3.8.0] - 2026-07-24
29
+
30
+ ### Added
31
+
32
+ - **`PaginatedResult.recovery_hint`** — `requestPaginated` now carries the
33
+ server's zero-result guidance through instead of silently dropping it.
34
+ `GET /admin/recordings/search` has emitted a `recovery_hint` on empty
35
+ result sets since the unified-catalog-search work (e.g. "N recordings
36
+ have a null primary artist — try ISRC or title"), but the SDK envelope
37
+ unwrap discarded it, so agents saw a bare empty list and read it as "no
38
+ recordings exist" (ops_issue 689a8e9a — an agent nearly created duplicate
39
+ recordings). Optional field; only present when the server sent one.
40
+
41
+ ## [3.7.2] - 2026-07-16
42
+
43
+ ### Fixed
44
+
45
+ - **`UsersResource` — all six methods resolved `undefined` on the live wire**
46
+ (`findByHandle`, `setHandle`, `getMyProfile`, `setBio`, `setAvatarUrl`,
47
+ `importBioFromPerson`). Each declared its response type as the
48
+ `{ data: X }` envelope and returned `res.data`, but `BaseResource.request()`
49
+ already unwraps the envelope — the double-unwrap made every method resolve
50
+ `undefined`, so `pica_users_get_my_profile` (and siblings) crashed with
51
+ `Cannot read properties of undefined (reading 'public_handle')` for every
52
+ caller. Methods are now typed against the unwrapped reality (ADR-265
53
+ lesson). Regression locked by `sdk-users-resource.test.ts` in mcp-server.
54
+ - **`RecordingsResource.prefixLookup` always returned `null`** — same
55
+ double-unwrap class, different idiom (`result.data ?? null`). A registry
56
+ hit unwraps to the row (so `.data` was undefined); a miss returns
57
+ `data: null`, which `request()`'s `data.data || data` falls through to the
58
+ full envelope on — fixed with the envelope-discriminating guard
59
+ (AccessSimulate precedent). `prefix_intel` on works/recordings inspect
60
+ works again.
61
+ - **`WorksResource.listReleases` always returned `[]`** when the work was
62
+ actually on releases (`result?.data || []` after the auto-unwrap) —
63
+ blanking the `releases` section of `pica_works_inspect`. Now mirrors the
64
+ recordings-side `Array.isArray` guard (cea22b5e). Both locked by
65
+ `sdk-double-unwrap-sweep.test.ts` in mcp-server.
13
66
 
14
67
  ## [3.7.1] - 2026-07-14
15
68
 
package/dist/index.d.ts CHANGED
@@ -259,6 +259,15 @@ interface PaginatedResult<T> {
259
259
  data: T[];
260
260
  total: number;
261
261
  hasMore: boolean;
262
+ /**
263
+ * Server-provided next-step guidance when a search legitimately returns
264
+ * zero rows (e.g. GET /admin/recordings/search explains that N recordings
265
+ * have no primary artist and suggests ISRC/title search). Optional — only
266
+ * routes that build one emit it, and only on empty result sets. Dropping
267
+ * this on the wire was ops_issue 689a8e9a: an agent read a silent empty as
268
+ * "no recordings exist" and nearly created duplicates.
269
+ */
270
+ recovery_hint?: string;
262
271
  }
263
272
  /**
264
273
  * Society registration record returned on work and recording read results
@@ -402,6 +411,23 @@ interface PicaScore {
402
411
  topActions: string[];
403
412
  calculatedAt: string;
404
413
  }
414
+ /**
415
+ * Outcome of a server-recomputed bulk attach of high-confidence orphan-audio
416
+ * matches (POST /admin/audio-files/bulk-assign-suggested). Mirrors
417
+ * `BulkAssignResult` in the app's audio-match service; `attached_ids` always
418
+ * has exactly `attached` entries (shape-locked by the api-audio-bulk-assign
419
+ * holdout).
420
+ */
421
+ export interface BulkAssignSuggestedResult {
422
+ attached: number;
423
+ skipped_ambiguous: number;
424
+ skipped_possible: number;
425
+ failed: number;
426
+ attached_ids: string[];
427
+ /** Present and true when the run was a dry-run preview (nothing persisted;
428
+ * `attached`/`attached_ids` mean WOULD-attach). */
429
+ dry_run?: boolean;
430
+ }
405
431
  interface AudioFile {
406
432
  id: string;
407
433
  work_id: string | null;
@@ -1366,6 +1392,32 @@ declare class AudioFilesResource extends BaseResource {
1366
1392
  limit?: number;
1367
1393
  }): Promise<AudioFile[]>;
1368
1394
  get(id: string): Promise<AudioFile>;
1395
+ /**
1396
+ * Attach ALL orphan audio files whose suggested match is ISRC-high and
1397
+ * non-ambiguous, in one call. Pairings are recomputed server-side at
1398
+ * execution time — nothing is sent, so stale suggestions can't be applied
1399
+ * and dismissed pairs stay excluded; `possible` (title-tier) matches are
1400
+ * never bulk-attached. Wraps POST /admin/audio-files/bulk-assign-suggested
1401
+ * (the route behind the /inspect "attach all high" strip; agent feedback
1402
+ * 20f592ff asked for the same action tool-side).
1403
+ *
1404
+ * NB the route responds `{ success, result }` with no `data` key, so
1405
+ * `request()`'s `.data || whole` unwrap yields the full envelope — read
1406
+ * `.result` here rather than typing against a payload that isn't there
1407
+ * (the ADR-265 double-unwrap lesson).
1408
+ */
1409
+ bulkAssignSuggested(params?: {
1410
+ /** Preview: same server-side classification, nothing persisted;
1411
+ * `attached`/`attached_ids` mean WOULD-attach. */
1412
+ dry_run?: boolean;
1413
+ }): Promise<BulkAssignSuggestedResult>;
1414
+ /**
1415
+ * Reject a suggested (audio_file → recording) match so the pair never
1416
+ * resurfaces in suggestions or bulk attach. The file stays an orphan —
1417
+ * only THIS pairing is suppressed. Repeat dismissals are no-op successes
1418
+ * server-side. Wraps POST /admin/audio-files/{id}/dismiss-match.
1419
+ */
1420
+ dismissMatch(id: string, recordingId: string): Promise<void>;
1369
1421
  update(id: string, body: {
1370
1422
  work_id?: string | null;
1371
1423
  recording_id?: string | null;
@@ -3400,6 +3452,12 @@ declare class CollaborationsResource extends BaseResource {
3400
3452
  * Cross-org discovery surface that lets the sender's agent invite a
3401
3453
  * collaborator by `@handle` without ever learning the recipient's
3402
3454
  * email (resolution happens server-side).
3455
+ *
3456
+ * Typed against the UNWRAPPED reality (ADR-265 lesson): BaseResource.request
3457
+ * auto-unwraps the `{success,data}` envelope, so methods see `data` itself.
3458
+ * Declaring `T = { data: X }` and returning `res.data` double-unwraps and
3459
+ * resolves `undefined` on the live wire (FIX_LOG 2026-07-16 — the
3460
+ * get_my_profile "reading 'public_handle'" crash).
3403
3461
  */
3404
3462
  declare class UsersResource extends BaseResource {
3405
3463
  /**