UNPKG

@copilotkit/runtime

Version:

<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />

1 lines 14.9 kB
{"version":3,"file":"memories.cjs","names":["PlatformRequestError","errorResponse","isIntelligenceRuntime","resolveIntelligenceUser","isHandlerResponse"],"sources":["../../../../../src/v2/runtime/handlers/intelligence/memories.ts"],"sourcesContent":["import type { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { isIntelligenceRuntime } from \"../../core/runtime\";\nimport { logger } from \"@copilotkit/shared\";\nimport { errorResponse, isHandlerResponse } from \"../shared/json-response\";\nimport { resolveIntelligenceUser } from \"../shared/resolve-intelligence-user\";\nimport { PlatformRequestError } from \"../../intelligence-platform/client\";\n\ninterface MemoriesHandlerParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n}\n\ninterface MemoryMutationParams extends MemoriesHandlerParams {\n memoryId: string;\n}\n\nconst MISSING_INTELLIGENCE_MESSAGE =\n \"Missing CopilotKitIntelligence configuration. Memory operations require a CopilotKitIntelligence instance to be provided in CopilotRuntime options.\";\n\n/** Allowed `kind` vocabulary the platform's memory endpoints accept. */\nconst MEMORY_KINDS: ReadonlySet<string> = new Set([\n \"topical\",\n \"episodic\",\n \"operational\",\n]);\n/** Allowed `scope` vocabulary the platform's memory endpoints accept. */\nconst MEMORY_SCOPES: ReadonlySet<string> = new Set([\"user\", \"project\"]);\n\n/**\n * Maps a thrown error to a `Response`.\n *\n * For a {@link PlatformRequestError}, forward only client-actionable **4xx**\n * statuses verbatim (e.g. 404 missing/wrong-scope memory, 409 conflict, 422\n * unprocessable) so a `useMemories` consumer can branch on them — a flat 500\n * would erase that distinction. A platform **5xx** (or any non-4xx / malformed\n * status) means the runtime is healthy but its dependency failed, so it surfaces\n * as `502 Bad Gateway` rather than echoing the upstream status as if the runtime\n * itself broke — and this also avoids a `new Response(..., { status })`\n * `RangeError` on an out-of-range status. Non-platform throws stay 500.\n */\nfunction memoryErrorResponse(error: unknown, message: string): Response {\n if (error instanceof PlatformRequestError) {\n const { status } = error;\n if (Number.isInteger(status) && status >= 400 && status <= 499) {\n return errorResponse(message, status);\n }\n return errorResponse(message, 502);\n }\n return errorResponse(message, 500);\n}\n\nasync function parseJsonBody(\n request: Request,\n): Promise<Record<string, unknown> | Response> {\n try {\n return (await request.json()) as Record<string, unknown>;\n } catch (error) {\n logger.error({ err: error }, \"Malformed JSON in memory request body\");\n return errorResponse(\"Invalid request body\", 400);\n }\n}\n\n/**\n * Extracts and validates the create/supersede body fields the platform's\n * memory endpoints require. Returns a `Response` (400) on invalid input.\n */\nfunction parseMemoryBody(body: Record<string, unknown>):\n | {\n content: string;\n kind: string;\n scope?: string;\n sourceThreadIds?: string[];\n }\n | Response {\n const { content, kind, scope, sourceThreadIds } = body;\n if (typeof content !== \"string\" || typeof kind !== \"string\") {\n return errorResponse(\"Memory requires string `content` and `kind`\", 400);\n }\n // `kind` must be one of the platform's known kinds. Reject an out-of-vocabulary\n // value here rather than forwarding it for the platform to reject.\n if (!MEMORY_KINDS.has(kind)) {\n return errorResponse(\n \"Memory `kind` must be one of: topical, episodic, operational\",\n 400,\n );\n }\n // `scope` is optional: when omitted the platform applies its default\n // (`\"user\"`). Only reject a present-but-wrong-typed scope.\n if (scope !== undefined && typeof scope !== \"string\") {\n return errorResponse(\"Memory `scope` must be a string when provided\", 400);\n }\n // When `scope` is present, it must be one of the known scopes.\n if (typeof scope === \"string\" && !MEMORY_SCOPES.has(scope)) {\n return errorResponse(\"Memory `scope` must be one of: user, project\", 400);\n }\n // `sourceThreadIds` is optional, but when present it must be a string array.\n // Validate every element so non-string ids are not forwarded to the platform.\n if (\n sourceThreadIds !== undefined &&\n (!Array.isArray(sourceThreadIds) ||\n !sourceThreadIds.every((id) => typeof id === \"string\"))\n ) {\n return errorResponse(\n \"Memory `sourceThreadIds` must be an array of strings when provided\",\n 400,\n );\n }\n return {\n content,\n kind,\n ...(typeof scope === \"string\" ? { scope } : {}),\n ...(Array.isArray(sourceThreadIds)\n ? { sourceThreadIds: sourceThreadIds as string[] }\n : {}),\n // `sourceThreadIds` elements are validated as strings above; the cast is safe.\n };\n}\n\n/**\n * Lists the resolved user's long-term memories via the Intelligence platform.\n *\n * Mirrors {@link handleListThreads}: requires a `CopilotKitIntelligence`\n * runtime, resolves the user with `identifyUser` (never trusting a\n * client-supplied id), and proxies to the platform's `GET /api/memories`\n * with the project API key + resolved user. The `?includeInvalidated=true`\n * query is forwarded so callers can opt into retired rows. The response is\n * the platform's `{ memories }` envelope, which the client memory store\n * consumes directly.\n */\nexport async function handleListMemories({\n runtime,\n request,\n}: MemoriesHandlerParams): Promise<Response> {\n if (isIntelligenceRuntime(runtime)) {\n try {\n const url = new URL(request.url);\n const includeInvalidated =\n url.searchParams.get(\"includeInvalidated\") === \"true\";\n\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n const data = await runtime.intelligence.listMemories({\n userId: user.id,\n ...(includeInvalidated ? { includeInvalidated: true } : {}),\n });\n\n // The client memory store consumes the `{ memories: [...] }` envelope\n // directly. Assert the shape before forwarding so a platform contract\n // violation surfaces as a clear 502 (the runtime is healthy but its\n // dependency returned the wrong shape) instead of a 200 the client will\n // choke on.\n if (\n data == null ||\n typeof data !== \"object\" ||\n !Array.isArray((data as { memories?: unknown }).memories)\n ) {\n logger.error(\n { data },\n \"listMemories: platform returned a response without a `memories` array\",\n );\n return errorResponse(\n \"Memory platform returned an invalid list response\",\n 502,\n );\n }\n\n return Response.json(data);\n } catch (error) {\n logger.error({ err: error }, \"Error listing memories\");\n return memoryErrorResponse(error, \"Failed to list memories\");\n }\n }\n\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n}\n\n/**\n * Mints memory-realtime join credentials (platform `POST\n * /api/memories/subscribe`). Mirrors {@link handleSubscribeToThreads}: requires\n * a `CopilotKitIntelligence` runtime and resolves the user with `identifyUser`\n * (never a client-supplied id). Returns `{ joinToken, joinCode }` — memory needs\n * the `joinCode` here (unlike threads, where it rides the thread-list response)\n * because the client builds the `user_meta:memories:<joinCode>` channel topic\n * from it.\n */\nexport async function handleSubscribeToMemories({\n runtime,\n request,\n}: MemoriesHandlerParams): Promise<Response> {\n if (isIntelligenceRuntime(runtime)) {\n try {\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n const credentials = await runtime.intelligence.ɵsubscribeToMemories({\n userId: user.id,\n });\n\n return Response.json({\n joinToken: credentials.joinToken,\n joinCode: credentials.joinCode,\n });\n } catch (error) {\n logger.error({ err: error }, \"Error subscribing to memories\");\n return memoryErrorResponse(error, \"Failed to subscribe to memories\");\n }\n }\n\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n}\n\n/**\n * Creates a memory for the resolved user (platform `POST /api/memories`).\n * Identity comes from `identifyUser`, never the request body. Returns 201\n * with the stored memory (the client store applies it server-authoritatively).\n */\nexport async function handleCreateMemory({\n runtime,\n request,\n}: MemoriesHandlerParams): Promise<Response> {\n if (!isIntelligenceRuntime(runtime)) {\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n }\n try {\n const body = await parseJsonBody(request);\n if (isHandlerResponse(body)) return body;\n const fields = parseMemoryBody(body);\n if (isHandlerResponse(fields)) return fields;\n\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n const data = await runtime.intelligence.createMemory({\n userId: user.id,\n ...fields,\n });\n return Response.json(data, { status: 201 });\n } catch (error) {\n logger.error({ err: error }, \"Error creating memory\");\n return memoryErrorResponse(error, \"Failed to create memory\");\n }\n}\n\n/**\n * Supersedes a memory (platform `PATCH /api/memories/:id`): retires `:id` and\n * inserts the new content atomically; the response carries `retiredId`.\n */\nexport async function handleUpdateMemory({\n runtime,\n request,\n memoryId,\n}: MemoryMutationParams): Promise<Response> {\n if (!isIntelligenceRuntime(runtime)) {\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n }\n try {\n const body = await parseJsonBody(request);\n if (isHandlerResponse(body)) return body;\n const fields = parseMemoryBody(body);\n if (isHandlerResponse(fields)) return fields;\n\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n const data = await runtime.intelligence.updateMemory({\n userId: user.id,\n id: memoryId,\n ...fields,\n });\n return Response.json(data);\n } catch (error) {\n logger.error({ err: error }, \"Error updating memory\");\n return memoryErrorResponse(error, \"Failed to update memory\");\n }\n}\n\n/**\n * Retires (forgets) a memory (platform `DELETE /api/memories/:id`). Non-lossy\n * on the platform side; returns 204.\n */\nexport async function handleRemoveMemory({\n runtime,\n request,\n memoryId,\n}: MemoryMutationParams): Promise<Response> {\n if (!isIntelligenceRuntime(runtime)) {\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n }\n try {\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n await runtime.intelligence.removeMemory({ userId: user.id, id: memoryId });\n return new Response(null, { status: 204 });\n } catch (error) {\n logger.error({ err: error }, \"Error removing memory\");\n return memoryErrorResponse(error, \"Failed to remove memory\");\n }\n}\n"],"mappings":"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"}