66 * existing deterministic server tools (`entries_list`, `entries_search`,
77 * `diary_tags`). Zone materialization/validation wraps the existing packs tools
88 * (`packs_create`, `packs_update`, `packs_list`) so a zone becomes an unpinned
9- * draft pack carrying its search provenance in `params`, then a pinned pack once
10- * the human validates it.
9+ * draft pack carrying its search provenance in `params` (`packs_create`), then a
10+ * pinned pack once the human validates it (`packs_update`). The pack UUID is
11+ * resolved from the create CID via `packs_provenance`.
1112 *
1213 * Tool results arrive as `{ content: [{ type:'text', text }], structuredContent }`;
1314 * {@link parseToolJson} reads either shape (mirrors libs/task-mcp-app).
@@ -176,28 +177,27 @@ export class McpDiaryAdapter implements DiaryDataAdapter {
176177 const json = parseToolJson ( result ) ;
177178 const packCid = typeof json . packCid === 'string' ? json . packCid : '' ;
178179 // packs_create returns only packCid (CustomPackResult), not the pack UUID,
179- // but packs_update (pin) needs the UUID. Resolve it from packs_list by CID.
180- const packId = packCid
181- ? await this . resolvePackIdByCid ( input . diaryId , packCid )
182- : '' ;
180+ // but packs_update (pin) needs the UUID. Resolve it deterministically from
181+ // the CID via packs_provenance (its metadata.rootPackId IS the UUID) —
182+ // never via packs_list, which paginates (default limit 20) and could miss a
183+ // freshly-created pack in a diary with many packs.
184+ const packId = packCid ? await this . resolvePackIdByCid ( packCid ) : '' ;
183185 return { packId, packCid, pinned : false } ;
184186 }
185187
186- /** Resolve a pack's UUID from its CID via packs_list (needed to pin later). */
187- private async resolvePackIdByCid (
188- diaryId : string ,
189- packCid : string ,
190- ) : Promise < string > {
188+ /**
189+ * Resolve a pack's UUID from its CID via packs_provenance, whose
190+ * `metadata.rootPackId` is the pack UUID. Deterministic and pagination-free
191+ * (packs_update needs the UUID to pin). Returns '' if it can't be resolved.
192+ */
193+ private async resolvePackIdByCid ( packCid : string ) : Promise < string > {
191194 const result = await this . app . callServerTool ( {
192- name : 'packs_list ' ,
193- arguments : { diary_id : diaryId } ,
195+ name : 'packs_provenance ' ,
196+ arguments : { pack_cid : packCid , depth : 0 } ,
194197 } ) ;
195198 const json = parseToolJson ( result ) ;
196- const items = Array . isArray ( json . items ) ? json . items : [ ] ;
197- const match = items
198- . map ( ( item ) => asRecord ( item ) )
199- . find ( ( item ) => item . packCid === packCid ) ;
200- return match && typeof match . id === 'string' ? match . id : '' ;
199+ const metadata = asRecord ( json . metadata ) ;
200+ return typeof metadata . rootPackId === 'string' ? metadata . rootPackId : '' ;
201201 }
202202
203203 /** Pin (validate) or unpin a zone's draft pack. */
@@ -214,27 +214,4 @@ export class McpDiaryAdapter implements DiaryDataAdapter {
214214 } ) ,
215215 } ) ;
216216 }
217-
218- /** List the diary's draft zone packs (unpinned, kind=diary-map-zone). */
219- async listZonePacks (
220- diaryId : string ,
221- ) : Promise < Array < { packId : string ; label : string ; pinned : boolean } > > {
222- const result = await this . app . callServerTool ( {
223- name : 'packs_list' ,
224- arguments : { diary_id : diaryId } ,
225- } ) ;
226- const json = parseToolJson ( result ) ;
227- const items = Array . isArray ( json . items ) ? json . items : [ ] ;
228- return items
229- . map ( ( item ) => asRecord ( item ) )
230- . filter ( ( item ) => asRecord ( item . params ) . kind === 'diary-map-zone' )
231- . map ( ( item ) => ( {
232- packId : typeof item . id === 'string' ? item . id : '' ,
233- label :
234- typeof asRecord ( item . params ) . label === 'string'
235- ? ( asRecord ( item . params ) . label as string )
236- : 'Zone' ,
237- pinned : item . pinned === true ,
238- } ) ) ;
239- }
240217}
0 commit comments