/** * `ctx.zotero`: the stable research-domain boundary of the plugin. * * The service owns provider selection (a configured id must be registered, * with no cross-provider fallback and no request replay), capability gating, * and the domain methods the model-facing tools consume. The HTTP * transport and the Zotero object model stay below this boundary. * * The plugin is request-driven by design: loading it never touches Zotero * (no probes, no timers, no background work). The only request sources are * the tools, invoked because the user asked about their library, and * the `/zotero status` command the user invokes explicitly. * * The effective config is live: every schema field is `volatile`, so a * settings commit lands in the running fiber's references without remounting * and tools read it per request. Structural flips (transport fields, the * write capability) rebuild on `loader/volatile-update` on this same * instance, never as a service replacement. A violating commit is vetoed in * `internal/config` before it lands, so the live read only ever observes * values `resolveConfig` accepts. * @module dsh-zotero/service */ import { Service, type Context } from '@deepseek-ai/cordis'; import { DiagnosticsRecorder } from './diagnostics.js'; import type { ZoteroAttachmentLocationView } from './contract.js'; import { type Config, type Options, type ResolvedConfig } from './config.js'; import type { ZoteroAttachmentLocation, ZoteroBrowseRequest, ZoteroBrowseResult, ZoteroChangesRequest, ZoteroChangesResult, ZoteroChildPage, ZoteroChildPageRequest, ZoteroChildrenRequest, ZoteroChildrenResult, ZoteroCreateCollectionCommittedOutcome, ZoteroCreateCollectionRequest, ZoteroCreateItemCommittedOutcome, ZoteroCreateItemRequest, ZoteroCreateNoteRequest, ZoteroCreateNoteCommittedOutcome, ZoteroDeleteCollectionRequest, ZoteroDeleteCollectionResult, ZoteroDeleteLibraryTagsRequest, ZoteroDeleteLibraryTagsResult, ZoteroGetRequest, ZoteroItemDetail, ZoteroObjectRef, ZoteroExportRequest, ZoteroExportResult, ZoteroExportTextResult, ZoteroProgressEvent, ZoteroProvider, ZoteroRetrieveRequest, ZoteroRetrieveResult, ZoteroSearchOptions, ZoteroSearchRequest, ZoteroSearchResult, ZoteroStatus, ZoteroUpdateItemCollectionsRequest, ZoteroUpdateItemCollectionsResult, ZoteroUpdateItemRequest, ZoteroUpdateItemResult, ZoteroUpdateItemTagsRequest, ZoteroUpdateItemTagsResult, ZoteroUpdateNoteRequest, ZoteroUpdateNoteOutcome, ZoteroUpdateCollectionRequest, ZoteroUpdateCollectionOutcome, ZoteroRestoreItemsRequest, ZoteroRestoreItemsOutcome, ZoteroCreateSavedSearchRequest, ZoteroCreateSavedSearchOutcome, ZoteroUpdateSavedSearchRequest, ZoteroUpdateSavedSearchOutcome, ZoteroDeleteSavedSearchRequest, ZoteroDeleteSavedSearchOutcome, ZoteroBatchItemsOutcome, ZoteroBatchItemsRequest, ZoteroWriteCall, ZoteroWriteDeclined } from './types.js'; declare module '@deepseek-ai/cordis' { interface Context { zotero: ZoteroService; } } /** The fiber-ownership check lives in `service-lifecycle.ts`; it is re-exported for the unit lane. */ export { isOwnConfigFiber } from './service-lifecycle.js'; /** * The composed Zotero service every strict endpoint reads. Abort is checked * first, so a cancelled caller observes its own cancellation; a missing * service fails with the one stable provider-unavailable code both Remote * services (and the panel's write pipeline) answer with. * @param ctx - the endpoint's own context. * @param signal - caller cancellation, when the caller carries one. */ export declare function requireZoteroService(ctx: Context, signal?: AbortSignal): ZoteroService; export declare class ZoteroService extends Service { static inject: string[]; static Config: import("@deepseek-ai/schemastery").default; provider: import("@deepseek-ai/schemastery").default; timeoutMs: import("@deepseek-ai/schemastery").default; maxInFlightRequests: import("@deepseek-ai/schemastery").default; maxSearchResults: import("@deepseek-ai/schemastery").default; maxNoteScanRecords: import("@deepseek-ai/schemastery").default; searchConcurrency: import("@deepseek-ai/schemastery").default; maxEvidenceChars: import("@deepseek-ai/schemastery").default; maxEvidencePassages: import("@deepseek-ai/schemastery").default; maxDetailChars: import("@deepseek-ai/schemastery").default; maxNoteBodyChars: import("@deepseek-ai/schemastery").default; maxNoteChars: import("@deepseek-ai/schemastery").default; maxNoteRecords: import("@deepseek-ai/schemastery").default; maxAnnotationRecords: import("@deepseek-ai/schemastery").default; maxAttachmentRecords: import("@deepseek-ai/schemastery").default; fulltextChunkWords: import("@deepseek-ai/schemastery").default; maxFulltextChars: import("@deepseek-ai/schemastery").default; retrieveAttachmentCap: import("@deepseek-ai/schemastery").default; graphConcurrency: import("@deepseek-ai/schemastery").default; maxResponseBytes: import("@deepseek-ai/schemastery").default; maxExportChars: import("@deepseek-ai/schemastery").default; maxExportRefs: import("@deepseek-ai/schemastery").default; maxBrowseResults: import("@deepseek-ai/schemastery").default; maxChangesResults: import("@deepseek-ai/schemastery").default; scopeListingTtlMs: import("@deepseek-ai/schemastery").default; defaultStyle: import("@deepseek-ai/schemastery").default; defaultLocale: import("@deepseek-ai/schemastery").default; writeEnabled: import("@deepseek-ai/schemastery").default; writePersistKey: import("@deepseek-ai/schemastery").default; writeNoteMaxChars: import("@deepseek-ai/schemastery").default; writeListMaxItems: import("@deepseek-ai/schemastery").default; writeAuthorizeDeadlineMs: import("@deepseek-ai/schemastery").default; webEnabled: import("@deepseek-ai/schemastery").default; enableRunInBackground: import("@deepseek-ai/schemastery").default; promoteOnTimeout: import("@deepseek-ai/schemastery").default; foregroundWaitMs: import("@deepseek-ai/schemastery").default; debug: import("@deepseek-ai/schemastery").default; }>>, Schemastery.ObjectT; provider: import("@deepseek-ai/schemastery").default; timeoutMs: import("@deepseek-ai/schemastery").default; maxInFlightRequests: import("@deepseek-ai/schemastery").default; maxSearchResults: import("@deepseek-ai/schemastery").default; maxNoteScanRecords: import("@deepseek-ai/schemastery").default; searchConcurrency: import("@deepseek-ai/schemastery").default; maxEvidenceChars: import("@deepseek-ai/schemastery").default; maxEvidencePassages: import("@deepseek-ai/schemastery").default; maxDetailChars: import("@deepseek-ai/schemastery").default; maxNoteBodyChars: import("@deepseek-ai/schemastery").default; maxNoteChars: import("@deepseek-ai/schemastery").default; maxNoteRecords: import("@deepseek-ai/schemastery").default; maxAnnotationRecords: import("@deepseek-ai/schemastery").default; maxAttachmentRecords: import("@deepseek-ai/schemastery").default; fulltextChunkWords: import("@deepseek-ai/schemastery").default; maxFulltextChars: import("@deepseek-ai/schemastery").default; retrieveAttachmentCap: import("@deepseek-ai/schemastery").default; graphConcurrency: import("@deepseek-ai/schemastery").default; maxResponseBytes: import("@deepseek-ai/schemastery").default; maxExportChars: import("@deepseek-ai/schemastery").default; maxExportRefs: import("@deepseek-ai/schemastery").default; maxBrowseResults: import("@deepseek-ai/schemastery").default; maxChangesResults: import("@deepseek-ai/schemastery").default; scopeListingTtlMs: import("@deepseek-ai/schemastery").default; defaultStyle: import("@deepseek-ai/schemastery").default; defaultLocale: import("@deepseek-ai/schemastery").default; writeEnabled: import("@deepseek-ai/schemastery").default; writePersistKey: import("@deepseek-ai/schemastery").default; writeNoteMaxChars: import("@deepseek-ai/schemastery").default; writeListMaxItems: import("@deepseek-ai/schemastery").default; writeAuthorizeDeadlineMs: import("@deepseek-ai/schemastery").default; webEnabled: import("@deepseek-ai/schemastery").default; enableRunInBackground: import("@deepseek-ai/schemastery").default; promoteOnTimeout: import("@deepseek-ai/schemastery").default; foregroundWaitMs: import("@deepseek-ai/schemastery").default; debug: import("@deepseek-ai/schemastery").default; }>>, "plain">; private readonly providers; /** * The live entry config: Loader-delivered volatile references (commits * write those same refs) or a complete plain snapshot. Tools read through * {@link config} per request so settings edits apply without a restart. */ private readonly entry; /** Disposer of the currently registered `local` provider, released before a rebuild re-registers it. */ private providerDispose; /** Disposers of the conditionally registered write tools, tracked for the writeEnabled flip. */ private writeToolDisposes; /** Cached resolved config; invalidated on any loader/volatile-update. */ private cachedConfig?; /** * The plugin's own diagnostics record: ring, threshold, and the sink every * instrumented seam holds. Public so the remote endpoints (and the tools, * for degradation facts) can read it without a second service. */ readonly diagnostics: DiagnosticsRecorder; /** Last observed probe connectivity; `undefined` until the first probe, so the initial state always records. */ private lastProbeConnected; constructor(ctx: Context, config?: Config | Options); /** * The durable half of `/zotero`: the command handler appends a * `zotero/status` event and the card reads it back through this projection * instead of re-parsing the text it was handed. Registration resolves * asynchronously; the plugin publishes nothing downstream of it, so there * is no handle to retain — the registration itself is an effect on this * fiber, and disposal releases it. Optional seam: a composition without the * registry keeps the live probe as the only source, and the card says * nothing it cannot prove. When the seam is absent the inject fiber stays * pending (and `await binding` is skipped), so the plugin loads without the * projection. */ protected [Service.init](): Promise; /** * The currently effective configuration, read live from the entry's * volatile references on every access, so tools that call this per request * see settings edits without a restart. Commits only land after passing * {@link resolveConfig} (load gate + `internal/config` veto), so the read * observes accepted values. Schemastery is not re-applied here; see * {@link readResolvedConfig}. * @returns the live resolved config. */ get config(): ResolvedConfig; /** * Build the transport stack from the given config: HTTP client, write * client, write authorizer, and the `local` provider registration (the * previous registration is disposed first so the duplicate-id guard never * fires). A request already in flight finishes on the client it started * with; later calls resolve the fresh provider. * * The write transport and authorizer exist only while `writeEnabled` is * set: without them the provider declares no `write` capability, so the * gate answers before any network happens. */ private buildTransport; /** * Register or retire the write tools as `writeEnabled` flips. When the * flag is off the tools are absent from the model's surface entirely. A * tool that can only ever answer "write capability is disabled" would * invite the model to retry it. Runs once at construction and again on * every structural volatile-update. */ private reconcileWriteTools; /** * The host credentials seam, resolved lazily: a composition without a * credentials service keeps write grants in memory only, and the plugin * never hard-depends on the seam being mounted. */ private credentials; /** * Register a provider into the seam. Effect-scoped: unloading the * registering fiber removes the provider. * @throws {ZoteroError} `ZOTERO_PROVIDER_UNAVAILABLE` on a duplicate id. * @returns the registration disposer. */ registerProvider(provider: ZoteroProvider): () => void; /** * Connectivity probe: the only health check. Ordinary calls fail with typed errors instead. * @param signal - caller cancellation; forwarded to the provider. * @returns live connectivity facts for the configured provider. */ status(signal?: AbortSignal): Promise; /** * Discover candidates; the provider resolves scopes and serves the compact records. * @param request - the search request with scope, mode, filters, and pagination. * @param signal - caller cancellation; forwarded to the provider. * @param options - the caller's search policy (supplement, cache generation). * @returns the resolved scope plus the compact hit records and pagination facts. */ search(request: ZoteroSearchRequest, signal: AbortSignal | undefined, options: ZoteroSearchOptions): Promise; /** * Read one item's metadata plus optionally requested child content. * @param request - the item ref and the child kinds to include. * @param signal - caller cancellation; forwarded to the provider. * @returns the normalized item detail. */ get(request: ZoteroGetRequest, signal?: AbortSignal): Promise; /** * Explore one item's or attachment's child-object graph. * @param request - the item/attachment ref and the child kinds to return. * @param signal - caller cancellation; forwarded to the provider. * @returns the bounded child collections with their totals. */ children(request: ZoteroChildrenRequest, signal?: AbortSignal): Promise; /** * Read one page of a single child kind without re-reading the parent. * @param request - the item/attachment ref, kind, and page window. * @param signal - caller cancellation; forwarded to the provider. * @returns the normalized child page. */ childPage(request: ZoteroChildPageRequest, signal?: AbortSignal): Promise; /** * Resolve an attachment ref to its on-disk file or linked URL. * @param ref - the item or attachment ref to resolve. * @param signal - caller cancellation; forwarded to the provider. * @returns the verified file path or linked URL. */ attachment(ref: ZoteroObjectRef, signal?: AbortSignal): Promise; /** * Resolve an attachment ref string for the Typert Remote caller. * @param ref - the item or attachment ref string to resolve. * @param signal - caller cancellation; forwarded to the provider. * @returns the resolved location view with local file path or web URL. */ resolveAttachment(ref: string, signal?: AbortSignal): Promise; /** * Gather ranked evidence passages for one item across the requested sources. * @param request - the item ref, ranking query, sources, and passage cap. * @param signal - caller cancellation; forwarded to the provider. * @returns the bounded ranked evidence with a truncation flag. */ retrieve(request: ZoteroRetrieveRequest, signal?: AbortSignal): Promise; /** * Export citations, a bibliography, or translator formats for the requested items. * @param request - the item refs and the export format plus optional style/locale. * @param signal - caller cancellation; forwarded to the provider. * @param onProgress - optional progress reporter. * @returns per-ref citations or the joined export text. */ export(request: ZoteroExportRequest, signal?: AbortSignal, onProgress?: (progress: ZoteroProgressEvent) => void): Promise; /** * Export rendered text/citations for the requested items, without the * tool-side per-document itemization. This is the text arm the library * panel's export endpoint serves; the tool keeps {@link export}. * @param request - the item refs and the export format plus optional style/locale. * @param signal - caller cancellation; forwarded to the provider. * @param onProgress - optional progress reporter. * @returns per-ref citations or the rendered export text. */ exportText(request: ZoteroExportRequest, signal?: AbortSignal, onProgress?: (progress: ZoteroProgressEvent) => void): Promise; browse(request: ZoteroBrowseRequest, signal?: AbortSignal): Promise; /** * Diff the library against a local transaction version. * @param request - the baseline version and the resource kinds to diff. * @param signal - caller cancellation; forwarded to the provider. * @param onProgress - optional progress reporter. * @returns changed/deleted keys plus the library's current version. */ changes(request: ZoteroChangesRequest, signal?: AbortSignal, onProgress?: (progress: ZoteroProgressEvent) => void): Promise; /** * Shared dispatch for personal library write operations: capability check, * provider method presence assertion, user approval gate, and execution. */ private dispatchWrite; /** * Create a note item, either standalone or attached to an existing item. * * Write boundary: personal library only (`zotero://user/0/...`); capability * answers before any network; the plan-review the user approves happens * here, at the seam, so no caller can skip it. * @param request - the markdown body, optional parent, collections, tags, and sources. * @param call - the asking write call: its plan and its tool run. * @returns the created note's ref, version, and the saved collections/tags/relations, * or `declined` when the user did not approve the plan. */ createNote(request: ZoteroCreateNoteRequest, call: ZoteroWriteCall): Promise; /** * Update one item's tags (add/remove in one write). * @param request - the item ref and the tags to add/remove. * @param call - the asking write call: its plan and its tool run. * @returns the merged tag list, the actual additions/removals, and versions, * or `declined` when the user did not approve the plan. */ updateItemTags(request: ZoteroUpdateItemTagsRequest, call: ZoteroWriteCall): Promise; updateNote(request: ZoteroUpdateNoteRequest, call: ZoteroWriteCall): Promise; /** * Update one item's collection membership (add/remove in one write). * @param request - the item ref and the collections to add/remove. * @param call - the asking write call: its plan and its tool run. * @returns the resulting collections, the actual additions/removals, and versions, * or `declined` when the user did not approve the plan. */ updateItemCollections(request: ZoteroUpdateItemCollectionsRequest, call: ZoteroWriteCall): Promise; updateCollection(request: ZoteroUpdateCollectionRequest, call: ZoteroWriteCall): Promise; /** * Create a collection, optionally under a parent. * @param request - the name and the optional parent ref or name. * @param call - the asking write call: its plan and its tool run. * @returns the created collection, or `declined` when not approved. */ createCollection(request: ZoteroCreateCollectionRequest, call: ZoteroWriteCall): Promise; /** * Delete a collection by ref or name. * @param request - the collection ref or name. * @param call - the asking write call: its plan and its tool run. * @returns the deletion receipt, or `declined` when not approved. */ deleteCollection(request: ZoteroDeleteCollectionRequest, call: ZoteroWriteCall): Promise; /** * Create a bibliographic item from the closed field set. * @param request - the itemType plus title/url/date/doi/abstract/publicationTitle/creators. * @param call - the asking write call: its plan and its tool run. * @returns the created item, or `declined` when not approved. */ createItem(request: ZoteroCreateItemRequest, call: ZoteroWriteCall): Promise; /** * Update one item's scalar metadata fields. * @param request - the item ref and the closed-set field updates. * @param call - the asking write call: its plan and its tool run. * @returns the changed fields and versions, or `declined` when not approved. */ updateItem(request: ZoteroUpdateItemRequest, call: ZoteroWriteCall): Promise; /** * Delete tags library-wide. Irreversible. * @param request - the tag names to delete. * @param call - the asking write call: its plan and its tool run. * @returns the deletion receipt, or `declined` when not approved. */ deleteLibraryTags(request: ZoteroDeleteLibraryTagsRequest, call: ZoteroWriteCall): Promise; /** * Batch items operation: create/import, update tags/collections, delete. * @param request - the batch operations. * @param call - the asking write call: its plan and its tool run. * @returns the outcome describing created, updated, and deleted items, or declined. */ batchItems(request: ZoteroBatchItemsRequest, call: ZoteroWriteCall): Promise; restoreItems(request: ZoteroRestoreItemsRequest, call: ZoteroWriteCall): Promise; createSavedSearch(request: ZoteroCreateSavedSearchRequest, call: ZoteroWriteCall): Promise; updateSavedSearch(request: ZoteroUpdateSavedSearchRequest, call: ZoteroWriteCall): Promise; deleteSavedSearch(request: ZoteroDeleteSavedSearchRequest, call: ZoteroWriteCall): Promise; /** * The confirmation gates every write passes through, in order: the session * approval policy (`ctx.approval`, where `never` auto-rejects and the * request is audited), then the plan-review card. There is no opt-out and no * config field behind either: a write that cannot clear the policy or show * the user its plan does not happen. Both asks fail closed, and a missing * channel refuses the write rather than letting it through unapproved. * @param call - the asking write call. * @returns true when the write may proceed. */ private approveWrite; private resolveProvider; private requireCapability; /** * Second gate behind `requireCapability`: the method itself must exist. * A provider that declares a capability but leaves the method undefined is * miswired, so this fails with `ZOTERO_PROVIDER_UNAVAILABLE` (provider bug) * rather than the capability code (unsupported domain). The returned * function is bound to the provider so class-method `this` survives the * detachment. */ private requireMethod; } export default ZoteroService; //# sourceMappingURL=service.d.ts.map