import { HttpTransport } from "../http.js"; import type { Transport } from "../client.js"; import type { V3AtomicCountRequest, V3AtomicDeleteData, V3AtomicDeleteRequest, V3AtomicQueryData, V3AtomicQueryRequest, V3AtomicSearchData, V3AtomicSearchRequest, V3AtomicUpdateData, V3AtomicUpdateRequest, V3ConversationAddData, V3ConversationAddRequest, V3ConversationCountRequest, V3ConversationDeleteData, V3ConversationDeleteRequest, V3ConversationQueryData, V3ConversationQueryRequest, V3ConversationSearchData, V3ConversationSearchRequest, V3CoreFile, V3CoreReadRequest, V3CoreWriteData, V3CoreWriteRequest, V3CountData, V3IsolationContext, V3IsolationOverrides, V3MemoryClientConfig, V3ScenarioFile, V3ScenarioListData, V3ScenarioListRequest, V3ScenarioReadRequest, V3ScenarioRmRequest, V3ScenarioCountRequest, V3ScenarioWriteData, V3ScenarioWriteRequest, } from "./types.js"; const V3 = "/v3"; function stripUndefined(obj: Record): Record { return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)); } function requireNonEmpty(name: string, value: string | undefined): string { if (!value) throw new Error(`v3 MemoryClient requires non-empty ${name}`); return value; } class IsolationContext { constructor( readonly teamId: string, readonly agentId: string, readonly userId: string, readonly sessionId?: string, readonly taskId?: string, ) { requireNonEmpty("teamId", teamId); requireNonEmpty("agentId", agentId); requireNonEmpty("userId", userId); } baseBody(): V3IsolationContext { return stripUndefined({ team_id: this.teamId, agent_id: this.agentId, user_id: this.userId, task_id: this.taskId, }) as unknown as V3IsolationContext; } resolveSession(override?: string): string | undefined { return override ?? this.sessionId; } with(overrides: V3IsolationOverrides): IsolationContext { return new IsolationContext( overrides.teamId ?? this.teamId, overrides.agentId ?? this.agentId, overrides.userId ?? this.userId, overrides.sessionId === null ? undefined : overrides.sessionId ?? this.sessionId, overrides.taskId === null ? undefined : overrides.taskId ?? this.taskId, ); } } /** * v3 strict-isolation data-plane client. * * Constructor requires teamId / agentId / userId. sessionId is optional: * - provided: L0/L1 calls are scoped to one conversation session; * - omitted or overridden to undefined/null: L0/L1 query/search/count aggregate across sessions for the same team+agent+user; * - L2/L3 are team+agent profile data and do not consume sessionId. */ export class MemoryClient { private readonly http: Transport; private readonly iso: IsolationContext; constructor(config: V3MemoryClientConfig); constructor(transport: Transport, isolation: V3IsolationContext); constructor(configOrTransport: V3MemoryClientConfig | Transport, isolation?: V3IsolationContext) { if ("post" in configOrTransport) { if (!isolation) throw new Error("v3 MemoryClient transport constructor requires isolation context"); this.http = configOrTransport; this.iso = new IsolationContext( isolation.team_id, isolation.agent_id, isolation.user_id, isolation.session_id, isolation.task_id, ); return; } const cfg = configOrTransport; this.http = new HttpTransport({ endpoint: cfg.endpoint, apiKey: cfg.apiKey, serviceId: cfg.serviceId, timeout: cfg.timeout, rejectUnauthorized: cfg.rejectUnauthorized, }); this.iso = new IsolationContext(cfg.teamId, cfg.agentId, cfg.userId, cfg.sessionId, cfg.taskId); } withIsolation(overrides: V3IsolationOverrides): MemoryClient { const next = this.iso.with(overrides); return new MemoryClient(this.http, { team_id: next.teamId, agent_id: next.agentId, user_id: next.userId, session_id: next.sessionId, task_id: next.taskId, }); } // -- L0 Conversation --------------------------------------------------- addConversation(params: V3ConversationAddRequest): Promise { return this.http.post(`${V3}/conversation/add`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), messages: params.messages, })); } queryConversation(params: V3ConversationQueryRequest = {}): Promise { return this.http.post(`${V3}/conversation/query`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), limit: params.limit, offset: params.offset, time_start: params.time_start, time_end: params.time_end, })); } searchConversation(params: V3ConversationSearchRequest): Promise { return this.http.post(`${V3}/conversation/search`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), query: params.query, limit: params.limit, time_start: params.time_start, time_end: params.time_end, })); } deleteConversation(params: V3ConversationDeleteRequest = {}): Promise { return this.http.post(`${V3}/conversation/delete`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), message_ids: params.message_ids, })); } countConversation(params: V3ConversationCountRequest = {}): Promise { return this.http.post(`${V3}/conversation/count`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), time_start: params.time_start, time_end: params.time_end, })); } // -- L1 Atomic --------------------------------------------------------- updateAtomic(params: V3AtomicUpdateRequest): Promise { return this.http.post(`${V3}/atomic/update`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), id: params.id, content: params.content, background: params.background, })); } queryAtomic(params: V3AtomicQueryRequest = {}): Promise { return this.http.post(`${V3}/atomic/query`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), type: params.type, limit: params.limit, offset: params.offset, time_start: params.time_start, time_end: params.time_end, })); } searchAtomic(params: V3AtomicSearchRequest): Promise { return this.http.post(`${V3}/atomic/search`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), query: params.query, limit: params.limit, type: params.type, time_start: params.time_start, time_end: params.time_end, })); } deleteAtomic(params: V3AtomicDeleteRequest): Promise { return this.http.post(`${V3}/atomic/delete`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), ids: params.ids, })); } countAtomic(params: V3AtomicCountRequest = {}): Promise { return this.http.post(`${V3}/atomic/count`, stripUndefined({ ...this.iso.baseBody(), session_id: this.iso.resolveSession(params.session_id), type: params.type, time_start: params.time_start, time_end: params.time_end, })); } // -- L2 Scenario ------------------------------------------------------- listScenarios(params: V3ScenarioListRequest = {}): Promise { return this.http.post(`${V3}/scenario/ls`, stripUndefined({ ...this.iso.baseBody(), path_prefix: params.path_prefix })); } readScenario(params: V3ScenarioReadRequest): Promise { return this.http.post(`${V3}/scenario/read`, stripUndefined({ ...this.iso.baseBody(), path: params.path })); } writeScenario(params: V3ScenarioWriteRequest): Promise { return this.http.post(`${V3}/scenario/write`, stripUndefined({ ...this.iso.baseBody(), path: params.path, content: params.content, summary: params.summary, })); } rmScenario(params: V3ScenarioRmRequest): Promise { return this.http.post(`${V3}/scenario/rm`, stripUndefined({ ...this.iso.baseBody(), path: params.path })); } countScenario(params: V3ScenarioCountRequest = {}): Promise { return this.http.post(`${V3}/scenario/count`, stripUndefined({ ...this.iso.baseBody(), path_prefix: params.path_prefix })); } // -- L3 Core ------------------------------------------------------------ readCore(_params: V3CoreReadRequest = {}): Promise { return this.http.post(`${V3}/core/read`, this.iso.baseBody() as unknown as Record); } writeCore(params: V3CoreWriteRequest): Promise { return this.http.post(`${V3}/core/write`, stripUndefined({ ...this.iso.baseBody(), content: params.content })); } countCore(): Promise { return this.http.post(`${V3}/core/count`, this.iso.baseBody() as unknown as Record); } }