/** * TencentDB Agent Memory v2 TypeScript SDK — `MemoryClient`. * * 14 methods mapping 1:1 to the v2 data-plane API. */ import { HttpTransport, type HttpTransportOptions } from "./http.js"; import { MemoryFileReader, createMemoryFileReader } from "./cos.js"; import type { AtomicUpdateData, AtomicUpdateRequest, AtomicCountRequest, AtomicDeleteData, AtomicDeleteRequest, AtomicQueryData, AtomicQueryRequest, AtomicSearchData, AtomicSearchRequest, ConversationAddData, ConversationAddRequest, ConversationCountRequest, ConversationDeleteData, ConversationDeleteRequest, ConversationQueryData, ConversationQueryRequest, ConversationSearchData, ConversationSearchRequest, CoreCountRequest, CoreFile, CoreReadRequest, CoreWriteData, CoreWriteRequest, CountData, OffloadCompactData, OffloadCompactRequest, OffloadIngestData, OffloadIngestRequest, OffloadQueryMmdData, OffloadQueryMmdRequest, ScenarioCountRequest, ScenarioFile, ScenarioListData, ScenarioListRequest, ScenarioReadRequest, ScenarioRmRequest, ScenarioWriteData, ScenarioWriteRequest, } from "./types.js"; const V2 = "/v2"; const V3 = "/v3"; function stripUndefined(obj: Record): Record { return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)); } export interface MemoryClientConfig { /** Base URL, e.g. `https://memory.tencentyun.com` */ endpoint: string; /** Bearer token */ apiKey: string; /** Memory instance ID (sent via `x-tdai-service-id` header). */ serviceId: string; /** Request timeout in ms (default 30 000). */ timeout?: number; /** Whether to reject invalid TLS certificates. Default: false (self-signed friendly). */ rejectUnauthorized?: boolean; } /** * Transport interface for testing — inject a mock that satisfies this. */ export interface Transport { post(path: string, body?: Record): Promise; } export class MemoryClient { private readonly http: Transport; private readonly config: MemoryClientConfig | null; constructor(config: MemoryClientConfig); constructor(transport: Transport); constructor(configOrTransport: MemoryClientConfig | Transport) { if ("post" in configOrTransport) { this.http = configOrTransport; this.config = null; } else { const cfg = configOrTransport; if (!cfg.serviceId) throw new Error("serviceId must be provided"); this.config = cfg; this.http = new HttpTransport({ endpoint: cfg.endpoint, apiKey: cfg.apiKey, serviceId: cfg.serviceId, timeout: cfg.timeout, rejectUnauthorized: cfg.rejectUnauthorized, }); } } // -- L0 Conversation --------------------------------------------------- addConversation(params: ConversationAddRequest): Promise { return this.http.post(`${V2}/conversation/add`, stripUndefined(params as unknown as Record)); } queryConversation(params: ConversationQueryRequest = {}): Promise { return this.http.post(`${V2}/conversation/query`, stripUndefined(params as unknown as Record)); } searchConversation(params: ConversationSearchRequest): Promise { return this.http.post(`${V2}/conversation/search`, stripUndefined(params as unknown as Record)); } deleteConversation(params: ConversationDeleteRequest): Promise { return this.http.post(`${V2}/conversation/delete`, stripUndefined(params as unknown as Record)); } countConversation(params: ConversationCountRequest = {}): Promise { return this.http.post(`${V2}/conversation/count`, stripUndefined(params as unknown as Record)); } // -- L1 Atomic --------------------------------------------------------- updateAtomic(params: AtomicUpdateRequest): Promise { return this.http.post(`${V2}/atomic/update`, stripUndefined(params as unknown as Record)); } queryAtomic(params: AtomicQueryRequest = {}): Promise { return this.http.post(`${V2}/atomic/query`, stripUndefined(params as unknown as Record)); } searchAtomic(params: AtomicSearchRequest): Promise { return this.http.post(`${V2}/atomic/search`, stripUndefined(params as unknown as Record)); } deleteAtomic(params: AtomicDeleteRequest): Promise { return this.http.post(`${V2}/atomic/delete`, stripUndefined(params as unknown as Record)); } countAtomic(params: AtomicCountRequest = {}): Promise { return this.http.post(`${V2}/atomic/count`, stripUndefined(params as unknown as Record)); } // -- L2 Scenario ------------------------------------------------------- listScenarios(params: ScenarioListRequest = {}): Promise { return this.http.post(`${V2}/scenario/ls`, stripUndefined(params as unknown as Record)); } readScenario(params: ScenarioReadRequest): Promise { return this.http.post(`${V2}/scenario/read`, stripUndefined(params as unknown as Record)); } writeScenario(params: ScenarioWriteRequest): Promise { return this.http.post(`${V2}/scenario/write`, stripUndefined(params as unknown as Record)); } rmScenario(params: ScenarioRmRequest): Promise { return this.http.post(`${V2}/scenario/rm`, stripUndefined(params as unknown as Record)); } countScenario(params: ScenarioCountRequest = {}): Promise { return this.http.post(`${V2}/scenario/count`, stripUndefined(params as unknown as Record)); } // -- L3 Core ------------------------------------------------------------ readCore(params: CoreReadRequest = {}): Promise { return this.http.post(`${V2}/core/read`, stripUndefined(params as unknown as Record)); } writeCore(params: CoreWriteRequest): Promise { return this.http.post(`${V2}/core/write`, stripUndefined(params as unknown as Record)); } countCore(params: CoreCountRequest = {}): Promise { return this.http.post(`${V3}/core/count`, stripUndefined(params as unknown as Record)); } // -- Offload (Compaction + Ingest) ------------------------------------ /** * Send tool pairs (+ optional context) to offload server for L1 processing. * Fire-and-forget usage: caller can `.catch()` without blocking. */ offloadIngest(params: OffloadIngestRequest): Promise { return this.http.post(`${V2}/offload/ingest`, stripUndefined(params as unknown as Record)); } /** * Request server-side context compaction. * Returns compacted messages + report, or throws on failure. */ offloadCompact(params: OffloadCompactRequest): Promise { return this.http.post(`${V2}/offload/compact`, stripUndefined(params as unknown as Record)); } /** * Query MMD task graphs for a session. * limit=1 returns only the current active MMD (fast path). */ offloadQueryMmd(params: OffloadQueryMmdRequest): Promise { return this.http.post(`${V2}/offload/query-mmd`, stripUndefined(params as unknown as Record)); } // -- File read (memory pipeline artifacts) ---------------------------- /** * Read a memory pipeline artifact (e.g. `persona.md`, `scene_blocks/*.md`) * by relative path. * * @param path Relative path within the memory space, e.g. * `"scene_blocks/cooking-recipes.md"` or `"persona.md"`. * @returns File content as string. */ async readFile(path: string): Promise { if (!this.fileReader) { if (!this.config) { throw new Error("readFile requires MemoryClient to be constructed with config (endpoint/apiKey/serviceId), not a raw Transport"); } this.fileReader = createMemoryFileReader({ endpoint: this.config.endpoint, apiKey: this.config.apiKey, serviceId: this.config.serviceId!, }); } return this.fileReader.read(path); } private fileReader: MemoryFileReader | null = null; }