/** Host registry for model-visible, read-only Cordis capability queries. */ import { Service } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis' import type { Agent } from '@deepseek-ai/dsh-agent' import { snapshotJsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue } from '@deepseek-ai/dsh-session/types' import { assertSupportedJsonSchema, validateJsonSchemaValue } from '@deepseek-ai/dsh-tools' import type { JsonSchemaNode } from '@deepseek-ai/dsh-tools' import type { CordisInspectMethodManifest, CordisInspectPlatform, CordisInspectProviderManifest, CordisInspectProviderView, CordisInspectQueryRequest, CordisInspectQueryResolution, CordisInspectRequestId, CordisInspectResolveAck, } from './types.ts' /** Context supplied to a Host inspect query. */ export interface HostCordisInspectQueryContext { /** Tool-call cancellation. */ signal: AbortSignal /** Agent whose scoped runtime is being inspected. */ agent: Agent } /** Local registration paired with its serializable manifest. */ export interface HostCordisInspectProviderRegistration { /** Provider and explicit method directory. */ manifest: CordisInspectProviderManifest /** Execute one declared method. */ query(method: string, input: JsonValue | undefined, context: HostCordisInspectQueryContext): Promise } interface PendingClientQuery { request: CordisInspectQueryRequest method: CordisInspectMethodManifest settle(resolution: CordisInspectQueryResolution): void } declare module '@deepseek-ai/cordis' { interface Context { /** Host registry for Cordis inspect providers and Client manifest/query routing. */ cordisInspect: CordisInspectRegistryService } } /** Registry and cross-page router behind the two model-facing inspect tools. */ export class CordisInspectRegistryService extends Service { private readonly providers = new Map() private readonly pending = new Map() private clientManifest: readonly CordisInspectProviderManifest[] | undefined private nextRequest = 1 /** Register the process-global Host registry. */ constructor(ctx: Context) { super(ctx, 'cordisInspect') } /** * Register one Host provider. * @param registration - manifest and local query handler. * @returns idempotent disposer. */ register(registration: HostCordisInspectProviderRegistration): () => void { const manifest = validateManifest(registration.manifest) if (this.providers.has(manifest.id)) throw new Error(`Host Cordis inspect provider "${manifest.id}" is already registered`) const stored = { ...registration, manifest } this.providers.set(manifest.id, stored) return () => { if (this.providers.get(manifest.id) === stored) this.providers.delete(manifest.id) } } /** * Replace the mirrored Client provider directory. * @param providers - complete Client manifest snapshot. */ syncClientManifest(providers: readonly CordisInspectProviderManifest[]): void { const ids = new Set() const validated = providers.map((provider) => { const manifest = validateManifest(provider) if (ids.has(manifest.id)) throw new Error(`Client Cordis inspect manifest repeats provider "${manifest.id}"`) ids.add(manifest.id) return manifest }) this.clientManifest = Object.freeze(validated) } /** * Return the complete known Host and Client provider directory. * @returns Host providers followed by the Client providers. */ list(): CordisInspectProviderView[] { return [ ...[...this.providers.values()].map(provider => view('host', provider.manifest)), ...(this.clientManifest ?? []).map(provider => view('client', provider)), ] } /** * Execute one provider query on its owning platform. * @param platform - Host or Client runtime. * @param providerId - provider selected from {@link list}. * @param methodName - declared method name. * @param input - optional lossless JSON input. * @param agent - requesting Agent and scope. * @param signal - tool-call cancellation. * @returns provider JSON data. */ async query( platform: CordisInspectPlatform, providerId: string, methodName: string, input: JsonValue | undefined, agent: Agent, signal: AbortSignal, ): Promise { if (platform === 'host') { const registration = this.providers.get(providerId) if (registration === undefined) throw new Error(`Host Cordis inspect provider "${providerId}" is not registered`) const method = findMethod(registration.manifest, methodName) validateInput('Host', providerId, method, input) signal.throwIfAborted() const data = await registration.query(methodName, input, { agent, signal }) signal.throwIfAborted() return validateOutput('Host', providerId, method, data) } return await this.queryClient(providerId, methodName, input, agent, signal) } /** * Accept the first valid Client response for a pending query. * @param agent - Agent whose Session owns the query. * @param requestId - Pending Client query identity. * @param resolution - Client provider result or failure. * @returns whether this response settled the still-pending query. */ resolveClientQuery( agent: Agent, requestId: CordisInspectRequestId, resolution: CordisInspectQueryResolution, ): CordisInspectResolveAck { const pending = this.pending.get(requestId) if (pending === undefined || pending.request.agentId !== agent.id) return { accepted: false } if (!resolution.ok) return { accepted: false } try { resolution = { ok: true, data: validateOutput('Client', pending.request.provider, pending.method, resolution.data), } } catch { return { accepted: false } } this.pending.delete(requestId) pending.settle(resolution) this.ctx.emit('cordis/inspect-query-resolved', { requestId }) return { accepted: true } } private async queryClient( providerId: string, methodName: string, input: JsonValue | undefined, agent: Agent, signal: AbortSignal, ): Promise { const provider = this.clientManifest?.find(candidate => candidate.id === providerId) if (provider === undefined) throw new Error(`Client Cordis inspect provider "${providerId}" is not registered`) const method = findMethod(provider, methodName) validateInput('Client', providerId, method, input) signal.throwIfAborted() const requestId = `inspect-${this.nextRequest++}` as CordisInspectRequestId const request: CordisInspectQueryRequest = { requestId, agentId: agent.id, provider: providerId, method: methodName, ...input === undefined ? {} : { input }, } const result = new Promise((resolve) => { this.pending.set(requestId, { request, method, settle: resolve }) }) const onAbort = (): void => { const pending = this.pending.get(requestId) if (pending === undefined) return this.pending.delete(requestId) pending.settle({ ok: false, reason: 'cancelled', message: `Client inspect query ${providerId}.${methodName} was cancelled` }) this.ctx.emit('cordis/inspect-query-resolved', { requestId }) } signal.addEventListener('abort', onAbort, { once: true }) if (signal.aborted) onAbort() else this.ctx.emit('cordis/inspect-query', request) try { const resolution = await result if (!resolution.ok) throw new Error(`${providerId}.${methodName}: ${resolution.message}`) return resolution.data } finally { signal.removeEventListener('abort', onAbort) } } } function view(platform: CordisInspectPlatform, manifest: CordisInspectProviderManifest): CordisInspectProviderView { return { platform, ...manifest, methods: [...manifest.methods] } } function validateManifest(manifest: CordisInspectProviderManifest): CordisInspectProviderManifest { if (manifest.id.trim() === '') throw new Error('Cordis inspect provider id must not be empty') if (manifest.description.trim() === '') throw new Error(`Cordis inspect provider "${manifest.id}" needs a description`) const names = new Set() const methods = manifest.methods.map((method) => { if (method.name.trim() === '') throw new Error(`Cordis inspect provider "${manifest.id}" has an empty method name`) if (names.has(method.name)) throw new Error(`Cordis inspect provider "${manifest.id}" repeats method "${method.name}"`) if (method.description.trim() === '') throw new Error(`Cordis inspect method ${manifest.id}.${method.name} needs a description`) assertSupportedJsonSchema(method.inputSchema) assertSupportedJsonSchema(method.outputSchema) names.add(method.name) return Object.freeze({ ...method }) }) return Object.freeze({ ...manifest, methods: Object.freeze(methods) }) } function findMethod(manifest: CordisInspectProviderManifest, name: string): CordisInspectMethodManifest { const method = manifest.methods.find(candidate => candidate.name === name) if (method === undefined) throw new Error(`Cordis inspect provider "${manifest.id}" has no method "${name}"`) return method } function validateInput( platform: 'Host' | 'Client', provider: string, method: CordisInspectMethodManifest, input: JsonValue | undefined, ): void { const violations = validateJsonSchemaValue(method.inputSchema as JsonSchemaNode, input ?? {}, 'input') if (violations.length > 0) throw new Error(`${platform} Cordis inspect ${provider}.${method.name} rejected input: ${violations.join('; ')}`) } function validateOutput( platform: 'Host' | 'Client', provider: string, method: CordisInspectMethodManifest, data: JsonValue, ): JsonValue { const snapshot = snapshotJsonValue(data) if (snapshot === undefined) throw new Error(`${platform} Cordis inspect ${provider}.${method.name} returned a non-JSON value`) const violations = validateJsonSchemaValue(method.outputSchema as JsonSchemaNode, snapshot, 'output') if (violations.length > 0) throw new Error(`${platform} Cordis inspect ${provider}.${method.name} returned invalid output: ${violations.join('; ')}`) return snapshot }