From cff71659de92fd07917d416beaf16457b5e0709c Mon Sep 17 00:00:00 2001 From: David Szarzynski Date: Wed, 28 Jan 2026 10:44:42 -0800 Subject: [PATCH 1/6] feat: Add memory tools for AI coding assistants Add hyperspell_recall and hyperspell_remember MCP tools optimized for AI coding assistants like Claude Code and Cursor. - hyperspell_recall: Search memories and connected documents - hyperspell_remember: Store conversations with session deduplication These tools call the new /memories/recall and /memories/remember endpoints in the Hyperspell API. Co-Authored-By: Claude Opus 4.5 --- packages/mcp-server/src/memory-tools.ts | 220 ++++++++++++++++++++++++ packages/mcp-server/src/options.ts | 19 +- packages/mcp-server/src/server.ts | 4 + 3 files changed, 238 insertions(+), 5 deletions(-) create mode 100644 packages/mcp-server/src/memory-tools.ts diff --git a/packages/mcp-server/src/memory-tools.ts b/packages/mcp-server/src/memory-tools.ts new file mode 100644 index 0000000..01cafd2 --- /dev/null +++ b/packages/mcp-server/src/memory-tools.ts @@ -0,0 +1,220 @@ +/** + * Memory tools for AI coding assistants. + * + * These tools provide long-term memory capabilities optimized for coding workflows, + * enabling unified search across stored memories AND connected documents (Notion, + * Google Drive, Slack, etc.). + */ + +import Hyperspell from 'hyperspell'; +import { Tool } from '@modelcontextprotocol/sdk/types.js'; +import { Metadata, McpTool, asTextContentResult, asErrorResult, ToolCallResult } from './types'; + +// ============================================================================ +// hyperspell_recall - Search memories and documents +// ============================================================================ + +const recallMetadata: Metadata = { + resource: 'memories', + operation: 'read', + tags: ['memory', 'search', 'recall'], + httpMethod: 'post', + httpPath: '/memories/recall', +}; + +const recallTool: Tool = { + name: 'hyperspell_recall', + description: `Search your memories and connected documents for relevant context. + +Use this tool BEFORE starting complex tasks to find relevant context from: +- Previous conversations and decisions +- Connected documents (Notion, Google Drive, Slack, Gmail) +- Stored insights and learnings + +This enables "unified search" - searching everything at once, not just memories.`, + inputSchema: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'Natural language query to search for relevant context.', + }, + include_documents: { + type: 'boolean', + description: + 'If true (default), search connected documents (Notion, Drive, etc.) in addition to memories.', + default: true, + }, + limit: { + type: 'number', + description: 'Maximum number of results to return (1-50, default 10).', + default: 10, + }, + session_id: { + type: 'string', + description: 'Optional session ID to boost results from the current session.', + }, + }, + required: ['query'], + }, + annotations: { + readOnlyHint: true, + }, +}; + +const recallHandler = async ( + client: Hyperspell, + args: Record | undefined, +): Promise => { + if (!args?.query) { + return asErrorResult('Query is required'); + } + + const query = String(args.query); + const includeDocuments = args.include_documents !== false; + const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 50); + + try { + // Use the new /memories/recall endpoint + const response = await client.post('/memories/recall', { + body: { + query, + include_documents: includeDocuments, + limit, + session_id: args.session_id ? String(args.session_id) : undefined, + }, + }); + + // Format results for readability + const results = (response as any).results || []; + const sourcesSearched = (response as any).sources_searched || []; + + const formatted = { + query_id: (response as any).query_id, + sources_searched: sourcesSearched, + result_count: results.length, + results: results.map((r: any) => ({ + title: r.title || r.resource_id, + source: r.source, + score: r.score, + summary: r.summary || r.text?.substring(0, 200), + resource_id: r.resource_id, + })), + }; + + return asTextContentResult(formatted); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return asErrorResult(`Failed to recall: ${message}`); + } +}; + +export const recallMcpTool: McpTool = { + metadata: recallMetadata, + tool: recallTool, + handler: recallHandler, +}; + +// ============================================================================ +// hyperspell_remember - Store a memory for future recall +// ============================================================================ + +const rememberMetadata: Metadata = { + resource: 'memories', + operation: 'write', + tags: ['memory', 'store', 'remember'], + httpMethod: 'post', + httpPath: '/memories/remember', +}; + +const rememberTool: Tool = { + name: 'hyperspell_remember', + description: `Store a conversation or insight for future recall. + +Use this tool AFTER completing significant work to preserve: +- Important decisions and their reasoning +- Debugging insights and solutions +- Project-specific patterns and conventions +- User preferences and context + +The stored memory will be searchable via hyperspell_recall alongside connected documents.`, + inputSchema: { + type: 'object', + properties: { + content: { + type: 'string', + description: + 'The content to remember. Can be a conversation, insight, decision, or any text worth preserving.', + }, + title: { + type: 'string', + description: 'Optional title for the memory.', + }, + tags: { + type: 'array', + items: { type: 'string' }, + description: 'Optional tags for categorization (max 10).', + }, + session_id: { + type: 'string', + description: + 'Optional session ID for deduplication. Same content in same session will not be stored twice.', + }, + }, + required: ['content'], + }, +}; + +const rememberHandler = async ( + client: Hyperspell, + args: Record | undefined, +): Promise => { + if (!args?.content) { + return asErrorResult('Content is required'); + } + + const content = String(args.content); + if (content.length < 10) { + return asErrorResult('Content must be at least 10 characters'); + } + + try { + // Use the new /memories/remember endpoint + const response = await client.post('/memories/remember', { + body: { + content, + title: args.title ? String(args.title) : undefined, + tags: Array.isArray(args.tags) ? args.tags.map(String).slice(0, 10) : undefined, + session_id: args.session_id ? String(args.session_id) : undefined, + }, + }); + + const result = response as any; + + return asTextContentResult({ + success: true, + resource_id: result.resource_id, + status: result.status, + deduplicated: result.deduplicated || false, + message: + result.deduplicated ? + 'Content was already stored in this session (deduplicated)' + : 'Memory stored successfully and will be available for recall once indexed', + }); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return asErrorResult(`Failed to remember: ${message}`); + } +}; + +export const rememberMcpTool: McpTool = { + metadata: rememberMetadata, + tool: rememberTool, + handler: rememberHandler, +}; + +// ============================================================================ +// Export both tools +// ============================================================================ + +export default [recallMcpTool, rememberMcpTool]; diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts index c66ad8c..d4340f2 100644 --- a/packages/mcp-server/src/options.ts +++ b/packages/mcp-server/src/options.ts @@ -11,6 +11,7 @@ export type CLIOptions = McpOptions & { export type McpOptions = { includeDocsTools?: boolean | undefined; + includeMemoryTools?: boolean | undefined; }; export function parseCLIOptions(): CLIOptions { @@ -18,13 +19,13 @@ export function parseCLIOptions(): CLIOptions { .option('tools', { type: 'string', array: true, - choices: ['code', 'docs'], + choices: ['code', 'docs', 'memory'], description: 'Use dynamic tools or all tools', }) .option('no-tools', { type: 'string', array: true, - choices: ['code', 'docs'], + choices: ['code', 'docs', 'memory'], description: 'Do not use any dynamic or all tools', }) .option('transport', { @@ -45,17 +46,19 @@ export function parseCLIOptions(): CLIOptions { const argv = opts.parseSync(); - const shouldIncludeToolType = (toolType: 'code' | 'docs') => + const shouldIncludeToolType = (toolType: 'code' | 'docs' | 'memory') => argv.noTools?.includes(toolType) ? false : argv.tools?.includes(toolType) ? true : undefined; const includeDocsTools = shouldIncludeToolType('docs'); + const includeMemoryTools = shouldIncludeToolType('memory'); const transport = argv.transport as 'stdio' | 'http'; return { ...(includeDocsTools !== undefined && { includeDocsTools }), + ...(includeMemoryTools !== undefined && { includeMemoryTools }), transport, port: argv.port, socket: argv.socket, @@ -72,8 +75,8 @@ const coerceArray = (zodType: T) => ); const QueryOptions = z.object({ - tools: coerceArray(z.enum(['code', 'docs'])).describe('Specify which MCP tools to use'), - no_tools: coerceArray(z.enum(['code', 'docs'])).describe('Specify which MCP tools to not use.'), + tools: coerceArray(z.enum(['code', 'docs', 'memory'])).describe('Specify which MCP tools to use'), + no_tools: coerceArray(z.enum(['code', 'docs', 'memory'])).describe('Specify which MCP tools to not use.'), tool: coerceArray(z.string()).describe('Include tools matching the specified names'), }); @@ -86,7 +89,13 @@ export function parseQueryOptions(defaultOptions: McpOptions, query: unknown): M : queryOptions.tools?.includes('docs') ? true : defaultOptions.includeDocsTools; + let memoryTools: boolean | undefined = + queryOptions.no_tools && queryOptions.no_tools?.includes('memory') ? false + : queryOptions.tools?.includes('memory') ? true + : defaultOptions.includeMemoryTools; + return { ...(docsTools !== undefined && { includeDocsTools: docsTools }), + ...(memoryTools !== undefined && { includeMemoryTools: memoryTools }), }; } diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 2965bd0..ce838a6 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -11,6 +11,7 @@ import { ClientOptions } from 'hyperspell'; import Hyperspell from 'hyperspell'; import { codeTool } from './code-tool'; import docsSearchTool from './docs-search-tool'; +import memoryTools from './memory-tools'; import { McpOptions } from './options'; import { HandlerFunction, McpTool } from './types'; @@ -116,6 +117,9 @@ export function selectTools(options?: McpOptions): McpTool[] { if (options?.includeDocsTools ?? true) { includedTools.push(docsSearchTool); } + if (options?.includeMemoryTools ?? true) { + includedTools.push(...memoryTools); + } return includedTools; } From 5e46f098d3c9638e903f06543acada304534efac Mon Sep 17 00:00:00 2001 From: David Szarzynski Date: Thu, 29 Jan 2026 11:00:28 -0600 Subject: [PATCH 2/6] feat: Add forget and profile memory tools Add two new MCP tools to match the Hyperspell API: - hyperspell_forget - Delete memories by ID or semantic search - hyperspell_profile - Get recent + semantically similar memories Co-Authored-By: Claude Opus 4.5 --- packages/mcp-server/src/memory-tools.ts | 166 +++++++++++++++++++++++- 1 file changed, 164 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/src/memory-tools.ts b/packages/mcp-server/src/memory-tools.ts index 01cafd2..ccca5eb 100644 --- a/packages/mcp-server/src/memory-tools.ts +++ b/packages/mcp-server/src/memory-tools.ts @@ -214,7 +214,169 @@ export const rememberMcpTool: McpTool = { }; // ============================================================================ -// Export both tools +// hyperspell_forget - Delete a memory // ============================================================================ -export default [recallMcpTool, rememberMcpTool]; +const forgetMetadata: Metadata = { + resource: 'memories', + operation: 'write', + tags: ['memory', 'delete', 'forget'], + httpMethod: 'post', + httpPath: '/memories/forget', +}; + +const forgetTool: Tool = { + name: 'hyperspell_forget', + description: `Delete a memory by ID or semantic search. + +Use this tool to remove outdated or incorrect memories: +- Delete by memory_id for precise removal +- Delete by query to find and remove the closest matching memory + +At least one of memory_id or query must be provided.`, + inputSchema: { + type: 'object', + properties: { + memory_id: { + type: 'string', + description: 'The resource_id of the memory to delete.', + }, + query: { + type: 'string', + description: 'Semantic search query to find and delete the closest matching memory.', + }, + }, + }, +}; + +const forgetHandler = async ( + client: Hyperspell, + args: Record | undefined, +): Promise => { + if (!args?.memory_id && !args?.query) { + return asErrorResult('Either memory_id or query must be provided'); + } + + try { + const response = await client.post('/memories/forget', { + body: { + memory_id: args.memory_id ? String(args.memory_id) : undefined, + query: args.query ? String(args.query) : undefined, + }, + }); + + const result = response as any; + + return asTextContentResult({ + success: result.success, + deleted_memory_id: result.deleted_memory_id, + chunks_deleted: result.chunks_deleted, + message: result.message, + }); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return asErrorResult(`Failed to forget: ${message}`); + } +}; + +export const forgetMcpTool: McpTool = { + metadata: forgetMetadata, + tool: forgetTool, + handler: forgetHandler, +}; + +// ============================================================================ +// hyperspell_profile - Get memory profile +// ============================================================================ + +const profileMetadata: Metadata = { + resource: 'memories', + operation: 'read', + tags: ['memory', 'profile', 'context'], + httpMethod: 'post', + httpPath: '/memories/profile', +}; + +const profileTool: Tool = { + name: 'hyperspell_profile', + description: `Get a summary of recent and relevant memories. + +Use this tool to understand what context is available: +- recent_memories: Most recently stored memories +- similar_memories: Semantically similar memories (if query provided) + +Useful before responding to understand what you know about the user/project.`, + inputSchema: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'Optional focus query to find semantically similar memories.', + }, + recent_limit: { + type: 'number', + description: 'Maximum number of recent memories to return (1-50, default 10).', + default: 10, + }, + similar_limit: { + type: 'number', + description: 'Maximum number of similar memories to return (1-50, default 10).', + default: 10, + }, + }, + }, + annotations: { + readOnlyHint: true, + }, +}; + +const profileHandler = async ( + client: Hyperspell, + args: Record | undefined, +): Promise => { + const recentLimit = Math.min(Math.max(Number(args?.recent_limit) || 10, 1), 50); + const similarLimit = Math.min(Math.max(Number(args?.similar_limit) || 10, 1), 50); + + try { + const response = await client.post('/memories/profile', { + body: { + query: args?.query ? String(args.query) : undefined, + recent_limit: recentLimit, + similar_limit: similarLimit, + }, + }); + + const result = response as any; + + const formatted = { + recent_memories: (result.recent_memories || []).map((r: any) => ({ + title: r.title || r.resource_id, + source: r.source, + resource_id: r.resource_id, + })), + similar_memories: (result.similar_memories || []).map((r: any) => ({ + title: r.title || r.resource_id, + source: r.source, + score: r.score, + resource_id: r.resource_id, + })), + }; + + return asTextContentResult(formatted); + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error'; + return asErrorResult(`Failed to get profile: ${message}`); + } +}; + +export const profileMcpTool: McpTool = { + metadata: profileMetadata, + tool: profileTool, + handler: profileHandler, +}; + +// ============================================================================ +// Export all tools +// ============================================================================ + +export default [recallMcpTool, rememberMcpTool, forgetMcpTool, profileMcpTool]; From add6acd0aa071c1e2d61a6141d8686d7e5ece26e Mon Sep 17 00:00:00 2001 From: David Szarzynski Date: Thu, 29 Jan 2026 12:20:51 -0600 Subject: [PATCH 3/6] fix: use bracket notation for index signature properties in memory-tools TypeScript's noPropertyAccessFromIndexSignature requires bracket notation for Record property access. Co-Authored-By: Claude Opus 4.5 --- packages/mcp-server/src/memory-tools.ts | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/mcp-server/src/memory-tools.ts b/packages/mcp-server/src/memory-tools.ts index ccca5eb..33ef380 100644 --- a/packages/mcp-server/src/memory-tools.ts +++ b/packages/mcp-server/src/memory-tools.ts @@ -66,13 +66,13 @@ const recallHandler = async ( client: Hyperspell, args: Record | undefined, ): Promise => { - if (!args?.query) { + if (!args?.['query']) { return asErrorResult('Query is required'); } - const query = String(args.query); - const includeDocuments = args.include_documents !== false; - const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 50); + const query = String(args['query']); + const includeDocuments = args['include_documents'] !== false; + const limit = Math.min(Math.max(Number(args['limit']) || 10, 1), 50); try { // Use the new /memories/recall endpoint @@ -81,7 +81,7 @@ const recallHandler = async ( query, include_documents: includeDocuments, limit, - session_id: args.session_id ? String(args.session_id) : undefined, + session_id: args['session_id'] ? String(args['session_id']) : undefined, }, }); @@ -169,11 +169,11 @@ const rememberHandler = async ( client: Hyperspell, args: Record | undefined, ): Promise => { - if (!args?.content) { + if (!args?.['content']) { return asErrorResult('Content is required'); } - const content = String(args.content); + const content = String(args['content']); if (content.length < 10) { return asErrorResult('Content must be at least 10 characters'); } @@ -183,9 +183,9 @@ const rememberHandler = async ( const response = await client.post('/memories/remember', { body: { content, - title: args.title ? String(args.title) : undefined, - tags: Array.isArray(args.tags) ? args.tags.map(String).slice(0, 10) : undefined, - session_id: args.session_id ? String(args.session_id) : undefined, + title: args['title'] ? String(args['title']) : undefined, + tags: Array.isArray(args['tags']) ? args['tags'].map(String).slice(0, 10) : undefined, + session_id: args['session_id'] ? String(args['session_id']) : undefined, }, }); @@ -253,15 +253,15 @@ const forgetHandler = async ( client: Hyperspell, args: Record | undefined, ): Promise => { - if (!args?.memory_id && !args?.query) { + if (!args?.['memory_id'] && !args?.['query']) { return asErrorResult('Either memory_id or query must be provided'); } try { const response = await client.post('/memories/forget', { body: { - memory_id: args.memory_id ? String(args.memory_id) : undefined, - query: args.query ? String(args.query) : undefined, + memory_id: args['memory_id'] ? String(args['memory_id']) : undefined, + query: args['query'] ? String(args['query']) : undefined, }, }); @@ -334,13 +334,13 @@ const profileHandler = async ( client: Hyperspell, args: Record | undefined, ): Promise => { - const recentLimit = Math.min(Math.max(Number(args?.recent_limit) || 10, 1), 50); - const similarLimit = Math.min(Math.max(Number(args?.similar_limit) || 10, 1), 50); + const recentLimit = Math.min(Math.max(Number(args?.['recent_limit']) || 10, 1), 50); + const similarLimit = Math.min(Math.max(Number(args?.['similar_limit']) || 10, 1), 50); try { const response = await client.post('/memories/profile', { body: { - query: args?.query ? String(args.query) : undefined, + query: args?.['query'] ? String(args['query']) : undefined, recent_limit: recentLimit, similar_limit: similarLimit, }, From fd34b8cf01f9410fcb9f93dc65889b6b51ba697d Mon Sep 17 00:00:00 2001 From: David Szarzynski Date: Sat, 31 Jan 2026 19:46:50 -0600 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20Add=20@hyperspell/openclaw=20packag?= =?UTF-8?q?e=20=E2=80=94=20hook=20+=20skill=20for=20OpenClaw=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new package that provides automatic Hyperspell memory integration for OpenClaw personal AI assistants: - Hook (agent:bootstrap): saves previous session conversations and recalls relevant memories + connected sources into the agent context - Skill (SKILL.md): instructs the agent when to use recall/remember/forget/profile - CLI installer: `npx @hyperspell/openclaw install` for one-command setup Co-Authored-By: Claude Opus 4.5 --- packages/openclaw/build | 26 + packages/openclaw/package.json | 63 ++ packages/openclaw/src/hook.ts | 277 +++++ packages/openclaw/src/index.ts | 184 ++++ packages/openclaw/src/skill.ts | 23 + packages/openclaw/src/templates/HOOK.md | 38 + packages/openclaw/src/templates/SKILL.md | 46 + packages/openclaw/tsconfig.build.json | 18 + packages/openclaw/tsconfig.json | 36 + packages/openclaw/yarn.lock | 1176 ++++++++++++++++++++++ 10 files changed, 1887 insertions(+) create mode 100755 packages/openclaw/build create mode 100644 packages/openclaw/package.json create mode 100644 packages/openclaw/src/hook.ts create mode 100644 packages/openclaw/src/index.ts create mode 100644 packages/openclaw/src/skill.ts create mode 100644 packages/openclaw/src/templates/HOOK.md create mode 100644 packages/openclaw/src/templates/SKILL.md create mode 100644 packages/openclaw/tsconfig.build.json create mode 100644 packages/openclaw/tsconfig.json create mode 100644 packages/openclaw/yarn.lock diff --git a/packages/openclaw/build b/packages/openclaw/build new file mode 100755 index 0000000..13ae1e5 --- /dev/null +++ b/packages/openclaw/build @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -exuo pipefail + +rm -rf dist; mkdir dist + +# Copy src to dist/src and build from dist/src into dist, so that +# the source map for index.js.map will refer to ./src/index.ts etc +cp -rp src dist + +for file in LICENSE; do + if [ -e "../../${file}" ]; then cp "../../${file}" dist; fi +done + +for file in README.md CHANGELOG.md; do + if [ -e "${file}" ]; then cp "${file}" dist; fi +done + +# this converts the export map paths for the dist directory +PKG_JSON_PATH=../../packages/openclaw/package.json node ../../scripts/utils/make-dist-package-json.cjs > dist/package.json + +# build to .js/.mjs/.d.ts files +./node_modules/.bin/tsc-multi + +chmod +x dist/index.js + +DIST_PATH=./dist PKG_IMPORT_PATH=@hyperspell/openclaw/ node ../../scripts/utils/postprocess-files.cjs diff --git a/packages/openclaw/package.json b/packages/openclaw/package.json new file mode 100644 index 0000000..064b014 --- /dev/null +++ b/packages/openclaw/package.json @@ -0,0 +1,63 @@ +{ + "name": "@hyperspell/openclaw", + "version": "0.1.0", + "description": "Hyperspell memory integration for OpenClaw — hook + skill for automatic long-term memory", + "author": "Hyperspell ", + "types": "dist/index.d.ts", + "main": "dist/index.js", + "type": "commonjs", + "repository": { + "type": "git", + "url": "git+https://github.com/hyperspell/node-sdk.git", + "directory": "packages/openclaw" + }, + "homepage": "https://github.com/hyperspell/node-sdk/tree/main/packages/openclaw#readme", + "license": "MIT", + "packageManager": "yarn@1.22.22", + "private": false, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "bash ./build", + "prepack": "echo 'to pack, run yarn build && (cd dist; yarn pack)' && exit 1", + "prepublishOnly": "echo 'to publish, run yarn build && (cd dist; yarn publish)' && exit 1", + "format": "prettier --write --cache --cache-strategy metadata . !dist", + "lint": "eslint --ext ts,js .", + "fix": "eslint --fix --ext ts,js ." + }, + "dependencies": { + "typescript": "5.8.3", + "yargs": "^17.7.2" + }, + "bin": { + "hyperspell-openclaw": "dist/index.js" + }, + "devDependencies": { + "@types/yargs": "^17.0.8", + "@types/node": "^20.0.0", + "@typescript-eslint/eslint-plugin": "8.31.1", + "@typescript-eslint/parser": "8.31.1", + "eslint": "^8.49.0", + "prettier": "^3.0.0", + "ts-node": "^10.5.0", + "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz", + "tsconfig-paths": "^4.0.0" + }, + "imports": { + "@hyperspell/openclaw": ".", + "@hyperspell/openclaw/*": "./src/*" + }, + "exports": { + ".": { + "require": "./dist/index.js", + "default": "./dist/index.mjs" + }, + "./*.mjs": "./dist/*.mjs", + "./*.js": "./dist/*.js", + "./*": { + "require": "./dist/*.js", + "default": "./dist/*.mjs" + } + } +} diff --git a/packages/openclaw/src/hook.ts b/packages/openclaw/src/hook.ts new file mode 100644 index 0000000..980e871 --- /dev/null +++ b/packages/openclaw/src/hook.ts @@ -0,0 +1,277 @@ +/** + * Hyperspell memory hook for OpenClaw. + * + * Listens to `agent:bootstrap` events to: + * 1. Save the previous session's conversation to Hyperspell memory + * 2. Recall relevant memories and inject them as a bootstrap context file + * + * Requires HYPERSPELL_API_KEY and HYPERSPELL_USER_ID environment variables. + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +const HYPERSPELL_API_BASE = 'https://api.hyperspell.com'; +const MARKER_FILE = '.hyperspell-last-saved-session'; +const MAX_CONTENT_LENGTH = 50000; +const RECALL_LIMIT = 10; + +// OpenClaw hook event types (inlined to avoid runtime dependency) +interface WorkspaceBootstrapFile { + path: string; + content: string; +} + +interface SessionMessage { + role: 'user' | 'assistant' | 'system'; + content: string; +} + +interface SessionEntry { + id: string; + messages?: SessionMessage[]; +} + +interface HookContext { + sessionEntry?: SessionEntry; + sessionId?: string; + workspaceDir?: string; + bootstrapFiles?: WorkspaceBootstrapFile[]; +} + +interface HookEvent { + type: string; + action: string; + messages: string[]; + context: HookContext; +} + +type HookHandler = (event: HookEvent) => Promise; + +async function hyperspellFetch( + endpoint: string, + body: Record, + apiKey: string, + userId: string, +): Promise { + const response = await fetch(`${HYPERSPELL_API_BASE}${endpoint}`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${apiKey}`, + 'X-As-User': userId, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + const text = await response.text().catch(() => 'unknown error'); + throw new Error(`Hyperspell API error ${response.status}: ${text}`); + } + + return response.json(); +} + +function getMarkerPath(workspaceDir: string): string { + return path.join(workspaceDir, MARKER_FILE); +} + +function getLastSavedSessionId(workspaceDir: string): string | null { + const markerPath = getMarkerPath(workspaceDir); + try { + return fs.readFileSync(markerPath, 'utf-8').trim(); + } catch { + return null; + } +} + +function setLastSavedSessionId(workspaceDir: string, sessionId: string): void { + const markerPath = getMarkerPath(workspaceDir); + fs.writeFileSync(markerPath, sessionId, 'utf-8'); +} + +/** + * Extract a summary of the session transcript suitable for saving as a memory. + * Takes the last N messages and formats them as a conversation. + */ +function extractSessionContent(session: SessionEntry): string | null { + const messages = session.messages; + if (!messages || messages.length === 0) return null; + + // Take up to the last 20 messages to stay within size limits + const recent = messages.slice(-20); + + const formatted = recent + .map((msg) => { + const role = msg.role === 'user' ? 'User' : msg.role === 'assistant' ? 'Assistant' : 'System'; + return `${role}: ${msg.content}`; + }) + .join('\n\n'); + + if (formatted.length < 20) return null; + + return formatted.slice(0, MAX_CONTENT_LENGTH); +} + +/** + * Derive a recall query from the current context. + * Uses the most recent user message or bootstrap file content. + */ +function deriveRecallQuery(event: HookEvent): string | null { + // Check for recent user messages in the event + if (event.messages.length > 0) { + const lastMessage = event.messages[event.messages.length - 1]; + if (lastMessage && lastMessage.trim().length > 0) { + return lastMessage.trim().slice(0, 500); + } + } + + // Fall back to reading the most recent bootstrap file content + const bootstrapFiles = event.context.bootstrapFiles; + if (bootstrapFiles && bootstrapFiles.length > 0) { + // Look for USER.md or SOUL.md for personality/context hints + const userFile = bootstrapFiles.find( + (f) => f.path === 'USER.md' || f.path === 'IDENTITY.md', + ); + if (userFile && userFile.content.trim().length > 0) { + return userFile.content.trim().slice(0, 500); + } + } + + return null; +} + +interface RecallResult { + title?: string; + source?: string; + chunks?: Array<{ content?: string }>; +} + +interface RecallResponse { + results?: RecallResult[]; +} + +/** + * Format recall results as Markdown for injection into the system prompt. + */ +function formatRecallResults(response: RecallResponse): string | null { + const results = response.results; + if (!results || results.length === 0) return null; + + const sections = results.map((result, i) => { + const title = result.title || `Result ${i + 1}`; + const source = result.source || 'memory'; + const content = + result.chunks?.map((c) => c.content).join('\n') || '(no content)'; + return `### ${title}\n*Source: ${source}*\n\n${content}`; + }); + + return [ + '# Relevant Memories & Context', + '', + 'The following memories and connected content were recalled from Hyperspell.', + 'Use this context to inform your responses.', + '', + ...sections, + ].join('\n'); +} + +/** + * Save the previous session's conversation to Hyperspell memory. + */ +async function savePreviousSession( + context: HookContext, + apiKey: string, + userId: string, +): Promise { + const session = context.sessionEntry; + const workspaceDir = context.workspaceDir; + if (!session || !workspaceDir) return; + + // Check if we already saved this session + const lastSaved = getLastSavedSessionId(workspaceDir); + if (lastSaved === session.id) return; + + const content = extractSessionContent(session); + if (!content) return; + + try { + await hyperspellFetch( + '/memories/remember', + { + content, + session_id: session.id, + title: `Session ${new Date().toISOString().split('T')[0]}`, + tags: ['openclaw', 'session'], + }, + apiKey, + userId, + ); + + setLastSavedSessionId(workspaceDir, session.id); + } catch (err) { + // Don't block the agent if saving fails — just log it + console.error('[hyperspell] Failed to save session:', err); + } +} + +/** + * Recall relevant memories and return formatted Markdown for injection. + */ +async function recallMemories( + event: HookEvent, + apiKey: string, + userId: string, +): Promise { + const query = deriveRecallQuery(event); + if (!query) return null; + + try { + const response = (await hyperspellFetch( + '/memories/recall', + { + query, + include_documents: true, + limit: RECALL_LIMIT, + }, + apiKey, + userId, + )) as RecallResponse; + + return formatRecallResults(response); + } catch (err) { + console.error('[hyperspell] Failed to recall memories:', err); + return null; + } +} + +/** + * Main hook handler for OpenClaw's agent:bootstrap event. + */ +const handler: HookHandler = async (event: HookEvent): Promise => { + if (event.type !== 'agent' || event.action !== 'bootstrap') return; + + const apiKey = process.env['HYPERSPELL_API_KEY']; + const userId = process.env['HYPERSPELL_USER_ID']; + if (!apiKey || !userId) { + console.error( + '[hyperspell] Missing HYPERSPELL_API_KEY or HYPERSPELL_USER_ID environment variables', + ); + return; + } + + // 1. Save previous session context (if any) + await savePreviousSession(event.context, apiKey, userId); + + // 2. Recall relevant memories and inject as bootstrap file + const context = await recallMemories(event, apiKey, userId); + if (context) { + event.context.bootstrapFiles?.push({ + path: 'HYPERSPELL_CONTEXT.md', + content: context, + }); + } +}; + +export default handler; +export { handler, savePreviousSession, recallMemories, formatRecallResults }; diff --git a/packages/openclaw/src/index.ts b/packages/openclaw/src/index.ts new file mode 100644 index 0000000..174c768 --- /dev/null +++ b/packages/openclaw/src/index.ts @@ -0,0 +1,184 @@ +#!/usr/bin/env node + +/** + * CLI installer for the Hyperspell OpenClaw integration. + * + * Usage: + * npx @hyperspell/openclaw install # Install hook + skill + * npx @hyperspell/openclaw install --hook # Install hook only + * npx @hyperspell/openclaw install --skill # Install skill only + * npx @hyperspell/openclaw uninstall # Remove hook + skill + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import yargs from 'yargs'; +import { getSkillContent, getHookContent, getHookHandlerPath } from './skill'; + +const HOOK_NAME = 'hyperspell-memory'; +const SKILL_NAME = 'hyperspell-memory'; + +function getOpenClawHooksDir(): string { + return path.join(os.homedir(), '.openclaw', 'hooks'); +} + +function getOpenClawSkillsDir(): string { + return path.join(os.homedir(), '.openclaw', 'skills'); +} + +function ensureDir(dir: string): void { + fs.mkdirSync(dir, { recursive: true }); +} + +function installHook(): void { + const hooksDir = getOpenClawHooksDir(); + const hookDir = path.join(hooksDir, HOOK_NAME); + ensureDir(hookDir); + + // Write HOOK.md + const hookMdContent = getHookContent(); + fs.writeFileSync(path.join(hookDir, 'HOOK.md'), hookMdContent, 'utf-8'); + + // Copy the compiled hook handler + const handlerSource = getHookHandlerPath(); + const handlerDest = path.join(hookDir, 'handler.js'); + + if (fs.existsSync(handlerSource)) { + fs.copyFileSync(handlerSource, handlerDest); + } else { + // Fallback: write a wrapper that requires the installed package + const wrapper = [ + '// Auto-generated wrapper for @hyperspell/openclaw hook', + "const { handler } = require('@hyperspell/openclaw/hook');", + 'module.exports = handler;', + 'module.exports.default = handler;', + ].join('\n'); + fs.writeFileSync(handlerDest, wrapper, 'utf-8'); + } + + console.log(` Hook installed to ${hookDir}`); +} + +function installSkill(): void { + const skillsDir = getOpenClawSkillsDir(); + const skillDir = path.join(skillsDir, SKILL_NAME); + ensureDir(skillDir); + + const skillContent = getSkillContent(); + fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillContent, 'utf-8'); + + console.log(` Skill installed to ${skillDir}`); +} + +function uninstallHook(): void { + const hookDir = path.join(getOpenClawHooksDir(), HOOK_NAME); + if (fs.existsSync(hookDir)) { + fs.rmSync(hookDir, { recursive: true }); + console.log(` Hook removed from ${hookDir}`); + } else { + console.log(' Hook not found, nothing to remove'); + } +} + +function uninstallSkill(): void { + const skillDir = path.join(getOpenClawSkillsDir(), SKILL_NAME); + if (fs.existsSync(skillDir)) { + fs.rmSync(skillDir, { recursive: true }); + console.log(` Skill removed from ${skillDir}`); + } else { + console.log(' Skill not found, nothing to remove'); + } +} + +function checkCredentials(): void { + const apiKey = process.env['HYPERSPELL_API_KEY']; + const userId = process.env['HYPERSPELL_USER_ID']; + + if (!apiKey || !userId) { + console.log('\nConfiguration required:'); + console.log(' Set these environment variables for the hook to work:'); + if (!apiKey) { + console.log(' export HYPERSPELL_API_KEY="your-api-key"'); + } + if (!userId) { + console.log(' export HYPERSPELL_USER_ID="your-user-id"'); + } + console.log('\n Get credentials from https://app.hyperspell.com'); + } +} + +function printMcpConfig(): void { + console.log('\nMCP server configuration (for explicit recall/remember tools):'); + console.log(' Add to your OpenClaw config:'); + console.log(''); + console.log(' "mcpServers": {'); + console.log(' "hyperspell": {'); + console.log(' "command": "npx",'); + console.log(' "args": ["-y", "hyperspell-mcp@latest", "--tools=memory"],'); + console.log(' "env": {'); + console.log(' "HYPERSPELL_API_KEY": "your-api-key",'); + console.log(' "HYPERSPELL_USER_ID": "your-user-id"'); + console.log(' }'); + console.log(' }'); + console.log(' }'); +} + +async function main(): Promise { + const argv = await yargs + .scriptName('hyperspell-openclaw') + .usage('$0 ', 'Hyperspell memory integration for OpenClaw') + .command('install', 'Install the Hyperspell hook and skill for OpenClaw', (y) => + y + .option('hook', { + type: 'boolean', + describe: 'Install hook only', + default: false, + }) + .option('skill', { + type: 'boolean', + describe: 'Install skill only', + default: false, + }), + ) + .command('uninstall', 'Remove the Hyperspell hook and skill') + .demandCommand(1, 'Please specify a command: install or uninstall') + .help() + .parse(); + + const command = argv._[0]; + + if (command === 'install') { + const hookOnly = (argv as Record)['hook'] === true; + const skillOnly = (argv as Record)['skill'] === true; + const installBoth = !hookOnly && !skillOnly; + + console.log('Installing Hyperspell memory integration for OpenClaw...\n'); + + if (installBoth || hookOnly) { + installHook(); + } + if (installBoth || skillOnly) { + installSkill(); + } + + checkCredentials(); + printMcpConfig(); + + console.log('\nDone! Restart OpenClaw to activate the integration.'); + } else if (command === 'uninstall') { + console.log('Removing Hyperspell memory integration...\n'); + uninstallHook(); + uninstallSkill(); + console.log('\nDone!'); + } +} + +if (require.main === module) { + main().catch((error) => { + console.error('Error:', error); + process.exit(1); + }); +} + +export { installHook, installSkill, uninstallHook, uninstallSkill }; diff --git a/packages/openclaw/src/skill.ts b/packages/openclaw/src/skill.ts new file mode 100644 index 0000000..2d6a1e9 --- /dev/null +++ b/packages/openclaw/src/skill.ts @@ -0,0 +1,23 @@ +/** + * Skill template content for OpenClaw integration. + * + * The skill instructs the OpenClaw agent when and how to use + * Hyperspell MCP tools for on-demand memory operations. + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +export function getSkillContent(): string { + const templatePath = path.join(__dirname, 'templates', 'SKILL.md'); + return fs.readFileSync(templatePath, 'utf-8'); +} + +export function getHookContent(): string { + const templatePath = path.join(__dirname, 'templates', 'HOOK.md'); + return fs.readFileSync(templatePath, 'utf-8'); +} + +export function getHookHandlerPath(): string { + return path.join(__dirname, 'hook.js'); +} diff --git a/packages/openclaw/src/templates/HOOK.md b/packages/openclaw/src/templates/HOOK.md new file mode 100644 index 0000000..24ba652 --- /dev/null +++ b/packages/openclaw/src/templates/HOOK.md @@ -0,0 +1,38 @@ +--- +name: hyperspell-memory +description: "Long-term memory powered by Hyperspell — automatically saves conversations and recalls relevant context" +metadata: + openclaw: + emoji: "🧠" + events: ["agent:bootstrap"] + requires: + env: ["HYPERSPELL_API_KEY", "HYPERSPELL_USER_ID"] +--- + +# Hyperspell Memory Hook + +Automatically integrates [Hyperspell](https://hyperspell.com) long-term memory into your OpenClaw assistant. + +## What It Does + +On each agent run: +1. **Saves** the previous session's conversation to Hyperspell memory +2. **Recalls** relevant memories and connected content (Notion, Gmail, Slack, Drive) and injects them into the agent's context + +## Configuration + +Set your Hyperspell credentials as environment variables: + +```bash +export HYPERSPELL_API_KEY="your-api-key" +export HYPERSPELL_USER_ID="your-user-id" +``` + +Get credentials from the [Hyperspell Dashboard](https://app.hyperspell.com). + +## How It Works + +- Listens to `agent:bootstrap` events +- Uses session deduplication to avoid saving the same conversation twice +- Injects recalled context as `HYPERSPELL_CONTEXT.md` bootstrap file +- Silently skips if credentials are missing or API calls fail diff --git a/packages/openclaw/src/templates/SKILL.md b/packages/openclaw/src/templates/SKILL.md new file mode 100644 index 0000000..6ad6264 --- /dev/null +++ b/packages/openclaw/src/templates/SKILL.md @@ -0,0 +1,46 @@ +--- +name: hyperspell-memory +description: "Long-term memory powered by Hyperspell" +metadata: + openclaw: + emoji: "🧠" + tools: ["hyperspell_recall", "hyperspell_remember", "hyperspell_forget", "hyperspell_profile"] +--- + +# Hyperspell Memory + +You have access to long-term memory via Hyperspell. Use these tools to remember and recall information across sessions. Memories are searchable alongside content from connected sources (Notion, Gmail, Slack, Google Drive, and more). + +## When to Recall + +- Before responding to complex questions, search for relevant past context +- When the user references something from a previous conversation +- When you need background on a topic you've discussed before +- At the start of a new task, check if there's relevant prior context + +## When to Remember + +- After important decisions or preferences are shared +- After completing significant tasks +- When the user shares personal context (preferences, key dates, contacts) +- After learning something new about the user's needs or workflow +- When debugging reveals an important insight + +## When to Forget + +- When the user asks you to delete outdated information +- When information has changed and the old version is misleading + +## Tools + +- `hyperspell_recall` — Search memories and connected sources for relevant context +- `hyperspell_remember` — Store a conversation or insight for future recall +- `hyperspell_forget` — Delete a memory by ID or semantic search +- `hyperspell_profile` — View recent and semantically similar memories + +## Tips + +- Include enough context in memories so they're useful when recalled later +- Use tags to categorize memories (e.g., "preferences", "architecture", "debugging") +- When recalling, use natural language queries — semantic search handles paraphrasing +- Check `hyperspell_profile` to understand what context is already available diff --git a/packages/openclaw/tsconfig.build.json b/packages/openclaw/tsconfig.build.json new file mode 100644 index 0000000..5199f9d --- /dev/null +++ b/packages/openclaw/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "include": ["dist/src"], + "exclude": [], + "compilerOptions": { + "rootDir": "./dist/src", + "paths": { + "@hyperspell/openclaw/*": ["./dist/src/*"], + "@hyperspell/openclaw": ["./dist/src/index.ts"] + }, + "noEmit": false, + "declaration": true, + "declarationMap": true, + "outDir": "dist", + "pretty": true, + "sourceMap": true + } +} diff --git a/packages/openclaw/tsconfig.json b/packages/openclaw/tsconfig.json new file mode 100644 index 0000000..e34dd0a --- /dev/null +++ b/packages/openclaw/tsconfig.json @@ -0,0 +1,36 @@ +{ + "include": ["src"], + "exclude": [], + "compilerOptions": { + "target": "es2020", + "lib": ["es2020"], + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + "paths": { + "@hyperspell/openclaw/*": ["./src/*"], + "@hyperspell/openclaw": ["./src/index.ts"] + }, + "noEmit": true, + + "resolveJsonModule": true, + + "forceConsistentCasingInFileNames": true, + + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "noImplicitReturns": true, + "alwaysStrict": true, + "exactOptionalPropertyTypes": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + + "skipLibCheck": true + } +} diff --git a/packages/openclaw/yarn.lock b/packages/openclaw/yarn.lock new file mode 100644 index 0000000..0b50a66 --- /dev/null +++ b/packages/openclaw/yarn.lock @@ -0,0 +1,1176 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" + integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": + version "4.12.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== + +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== + dependencies: + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@tsconfig/node10@^1.0.7": + version "1.0.12" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.12.tgz#be57ceac1e4692b41be9de6be8c32a106636dba4" + integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/node@^20.0.0": + version "20.19.30" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.30.tgz#84fa87498ade5cd2b6ba8f8eec01d3b138ca60d0" + integrity sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g== + dependencies: + undici-types "~6.21.0" + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.35" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.35.tgz#07013e46aa4d7d7d50a49e15604c1c5340d4eb24" + integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.1.tgz#62f1befe59647524994e89de4516d8dcba7a850a" + integrity sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.31.1" + "@typescript-eslint/type-utils" "8.31.1" + "@typescript-eslint/utils" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^2.0.1" + +"@typescript-eslint/parser@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.31.1.tgz#e9b0ccf30d37dde724ee4d15f4dbc195995cce1b" + integrity sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q== + dependencies: + "@typescript-eslint/scope-manager" "8.31.1" + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/typescript-estree" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.31.1.tgz#1eb52e76878f545e4add142e0d8e3e97e7aa443b" + integrity sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw== + dependencies: + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + +"@typescript-eslint/type-utils@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.31.1.tgz#be0f438fb24b03568e282a0aed85f776409f970c" + integrity sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA== + dependencies: + "@typescript-eslint/typescript-estree" "8.31.1" + "@typescript-eslint/utils" "8.31.1" + debug "^4.3.4" + ts-api-utils "^2.0.1" + +"@typescript-eslint/types@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.31.1.tgz#478ed6f7e8aee1be7b63a60212b6bffe1423b5d4" + integrity sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ== + +"@typescript-eslint/typescript-estree@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.1.tgz#37792fe7ef4d3021c7580067c8f1ae66daabacdf" + integrity sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag== + dependencies: + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/visitor-keys" "8.31.1" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.0.1" + +"@typescript-eslint/utils@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.31.1.tgz#5628ea0393598a0b2f143d0fc6d019f0dee9dd14" + integrity sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.31.1" + "@typescript-eslint/types" "8.31.1" + "@typescript-eslint/typescript-estree" "8.31.1" + +"@typescript-eslint/visitor-keys@8.31.1": + version "8.31.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.1.tgz#6742b0e3ba1e0c1e35bdaf78c03e759eb8dd8e75" + integrity sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw== + dependencies: + "@typescript-eslint/types" "8.31.1" + eslint-visitor-keys "^4.2.0" + +"@ungap/structured-clone@^1.2.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.1.1: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1, acorn@^8.9.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.2: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +diff@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.4.tgz#7a6dbfda325f25f07517e9b518f897c08332e07d" + integrity sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + +eslint@^8.49.0: + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.1" + "@humanwhocodes/config-array" "^0.13.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" + integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.20.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.20.1.tgz#ca750a10dc925bc8b18839fd203e3ef4b3ced675" + integrity sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-yaml@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-all@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-all/-/p-all-3.0.0.tgz#077c023c37e75e760193badab2bad3ccd5782bfb" + integrity sha512-qUZbvbBFVXm6uJ7U/WDiO0fv6waBMbjlCm4E66oZdRR+egswICarIdHyVSZZHudH8T5SF8x/JG0q0duFzPnlBw== + dependencies: + p-map "^4.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@^3.0.0: + version "3.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +reusify@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^7.6.0: + version "7.7.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +string-to-stream@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/string-to-stream/-/string-to-stream-3.0.1.tgz#480e6fb4d5476d31cb2221f75307a5dcb6638a42" + integrity sha512-Hl092MV3USJuUCC6mfl9sPzGloA3K5VwdIeJjYIkXY/8K+mUvaeEabWJgArp+xXrsWxCajeT2pc4axbVhIZJyg== + dependencies: + readable-stream "^3.4.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +superstruct@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.4.tgz#0adb99a7578bd2f1c526220da6571b2d485d91ca" + integrity sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-api-utils@^2.0.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.4.0.tgz#2690579f96d2790253bdcf1ca35d569ad78f9ad8" + integrity sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA== + +ts-node@^10.5.0: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz": + version "1.1.9" + resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz#777f6f5d9e26bf0e94e5170990dd3a841d6707cd" + dependencies: + debug "^4.3.7" + fast-glob "^3.3.2" + get-stdin "^8.0.0" + p-all "^3.0.0" + picocolors "^1.1.1" + signal-exit "^3.0.7" + string-to-stream "^3.0.1" + superstruct "^1.0.4" + tslib "^2.8.1" + yargs "^17.7.2" + +tsconfig-paths@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +typescript@5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From f103dff1d5da0bb799fd2601e96a237ac53ecc76 Mon Sep 17 00:00:00 2001 From: David Szarzynski Date: Sat, 31 Jan 2026 19:58:29 -0600 Subject: [PATCH 5/6] refactor: Use /memories/add/trace for structured session saving Switch the OpenClaw hook from /memories/remember (flat text) to /memories/add/trace (structured steps) to preserve tool calls, tool results, and reasoning in session transcripts. This aligns with Manu's procedural memory endpoint (PR #367). Co-Authored-By: Claude Opus 4.5 --- packages/openclaw/src/hook.ts | 85 ++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 26 deletions(-) diff --git a/packages/openclaw/src/hook.ts b/packages/openclaw/src/hook.ts index 980e871..30fb972 100644 --- a/packages/openclaw/src/hook.ts +++ b/packages/openclaw/src/hook.ts @@ -2,7 +2,7 @@ * Hyperspell memory hook for OpenClaw. * * Listens to `agent:bootstrap` events to: - * 1. Save the previous session's conversation to Hyperspell memory + * 1. Save the previous session's conversation as a structured trace via /memories/add/trace * 2. Recall relevant memories and inject them as a bootstrap context file * * Requires HYPERSPELL_API_KEY and HYPERSPELL_USER_ID environment variables. @@ -13,7 +13,7 @@ import * as path from 'path'; const HYPERSPELL_API_BASE = 'https://api.hyperspell.com'; const MARKER_FILE = '.hyperspell-last-saved-session'; -const MAX_CONTENT_LENGTH = 50000; +const MAX_STEPS = 100; const RECALL_LIMIT = 10; // OpenClaw hook event types (inlined to avoid runtime dependency) @@ -22,9 +22,25 @@ interface WorkspaceBootstrapFile { content: string; } +interface ToolCallInfo { + tool_call_id?: string; + tool_name?: string; + input?: Record; +} + +interface ToolResultInfo { + tool_call_id?: string; + tool_name?: string; + output?: unknown; + is_error?: boolean; +} + interface SessionMessage { - role: 'user' | 'assistant' | 'system'; - content: string; + role: 'user' | 'assistant' | 'system' | 'tool'; + content?: string; + tool_calls?: ToolCallInfo[]; + tool_results?: ToolResultInfo[]; + reasoning?: Array<{ text?: string; type?: string }>; } interface SessionEntry { @@ -91,26 +107,38 @@ function setLastSavedSessionId(workspaceDir: string, sessionId: string): void { } /** - * Extract a summary of the session transcript suitable for saving as a memory. - * Takes the last N messages and formats them as a conversation. + * Convert OpenClaw session messages into structured trace steps + * compatible with Hyperspell's /memories/add/trace endpoint. */ -function extractSessionContent(session: SessionEntry): string | null { +function extractTraceSteps(session: SessionEntry): Record[] | null { const messages = session.messages; if (!messages || messages.length === 0) return null; - // Take up to the last 20 messages to stay within size limits - const recent = messages.slice(-20); + // Take up to the last MAX_STEPS messages + const recent = messages.slice(-MAX_STEPS); + + const steps: Record[] = []; - const formatted = recent - .map((msg) => { - const role = msg.role === 'user' ? 'User' : msg.role === 'assistant' ? 'Assistant' : 'System'; - return `${role}: ${msg.content}`; - }) - .join('\n\n'); + for (const msg of recent) { + const step: Record = { role: msg.role }; - if (formatted.length < 20) return null; + if (msg.content) { + step['content'] = msg.content; + } + if (msg.reasoning && msg.reasoning.length > 0) { + step['reasoning'] = msg.reasoning; + } + if (msg.tool_calls && msg.tool_calls.length > 0) { + step['tool_calls'] = msg.tool_calls; + } + if (msg.role === 'tool' && msg.tool_results && msg.tool_results.length > 0) { + step['tool_results'] = msg.tool_results; + } + + steps.push(step); + } - return formatted.slice(0, MAX_CONTENT_LENGTH); + return steps.length > 0 ? steps : null; } /** @@ -177,7 +205,9 @@ function formatRecallResults(response: RecallResponse): string | null { } /** - * Save the previous session's conversation to Hyperspell memory. + * Save the previous session's conversation as a structured trace to Hyperspell. + * Uses /memories/add/trace to preserve the full structure (messages, tool calls, + * tool results, reasoning) rather than flattening to plain text. */ async function savePreviousSession( context: HookContext, @@ -192,17 +222,20 @@ async function savePreviousSession( const lastSaved = getLastSavedSessionId(workspaceDir); if (lastSaved === session.id) return; - const content = extractSessionContent(session); - if (!content) return; + const steps = extractTraceSteps(session); + if (!steps) return; try { await hyperspellFetch( - '/memories/remember', + '/memories/add/trace', { - content, session_id: session.id, - title: `Session ${new Date().toISOString().split('T')[0]}`, - tags: ['openclaw', 'session'], + steps, + title: `OpenClaw session ${new Date().toISOString().split('T')[0]}`, + metadata: { + source_agent: 'openclaw', + message_count: steps.length, + }, }, apiKey, userId, @@ -211,7 +244,7 @@ async function savePreviousSession( setLastSavedSessionId(workspaceDir, session.id); } catch (err) { // Don't block the agent if saving fails — just log it - console.error('[hyperspell] Failed to save session:', err); + console.error('[hyperspell] Failed to save session trace:', err); } } @@ -274,4 +307,4 @@ const handler: HookHandler = async (event: HookEvent): Promise => { }; export default handler; -export { handler, savePreviousSession, recallMemories, formatRecallResults }; +export { handler, savePreviousSession, recallMemories, formatRecallResults, extractTraceSteps }; From 9febbf4dabc46807541c2fd3f82678c42eedea69 Mon Sep 17 00:00:00 2001 From: David Szarzynski Date: Sat, 31 Jan 2026 20:15:12 -0600 Subject: [PATCH 6/6] refactor: Use existing Hyperspell API endpoints instead of custom memory routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Memory tools now wrap existing endpoints directly: - hyperspell_recall → POST /memories/query - hyperspell_remember → POST /memories/add - hyperspell_forget → DELETE /memories/delete/{source}/{resource_id} - hyperspell_profile → GET /memories/list OpenClaw hook updated to use /memories/query for recall. Co-Authored-By: Claude Opus 4.5 --- packages/mcp-server/src/memory-tools.ts | 206 +++++++++++------------- packages/openclaw/src/hook.ts | 6 +- 2 files changed, 99 insertions(+), 113 deletions(-) diff --git a/packages/mcp-server/src/memory-tools.ts b/packages/mcp-server/src/memory-tools.ts index 33ef380..cc6019f 100644 --- a/packages/mcp-server/src/memory-tools.ts +++ b/packages/mcp-server/src/memory-tools.ts @@ -1,9 +1,11 @@ /** - * Memory tools for AI coding assistants. + * Memory tools for AI assistants. * - * These tools provide long-term memory capabilities optimized for coding workflows, - * enabling unified search across stored memories AND connected documents (Notion, - * Google Drive, Slack, etc.). + * Thin wrappers around existing Hyperspell API endpoints: + * - /memories/query → hyperspell_recall + * - /memories/add → hyperspell_remember + * - /memories/delete → hyperspell_forget + * - /memories/list → hyperspell_profile */ import Hyperspell from 'hyperspell'; @@ -11,7 +13,7 @@ import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { Metadata, McpTool, asTextContentResult, asErrorResult, ToolCallResult } from './types'; // ============================================================================ -// hyperspell_recall - Search memories and documents +// hyperspell_recall - Search memories and connected sources // ============================================================================ const recallMetadata: Metadata = { @@ -19,16 +21,16 @@ const recallMetadata: Metadata = { operation: 'read', tags: ['memory', 'search', 'recall'], httpMethod: 'post', - httpPath: '/memories/recall', + httpPath: '/memories/query', }; const recallTool: Tool = { name: 'hyperspell_recall', - description: `Search your memories and connected documents for relevant context. + description: `Search your memories and connected sources for relevant context. Use this tool BEFORE starting complex tasks to find relevant context from: - Previous conversations and decisions -- Connected documents (Notion, Google Drive, Slack, Gmail) +- Connected sources (Notion, Google Drive, Slack, Gmail) - Stored insights and learnings This enables "unified search" - searching everything at once, not just memories.`, @@ -39,21 +41,17 @@ This enables "unified search" - searching everything at once, not just memories. type: 'string', description: 'Natural language query to search for relevant context.', }, - include_documents: { - type: 'boolean', + sources: { + type: 'array', + items: { type: 'string' }, description: - 'If true (default), search connected documents (Notion, Drive, etc.) in addition to memories.', - default: true, + 'Sources to search (e.g. ["vault", "notion", "slack", "google_drive", "google_mail"]). Defaults to all connected sources.', }, limit: { type: 'number', - description: 'Maximum number of results to return (1-50, default 10).', + description: 'Maximum number of results to return (default 10).', default: 10, }, - session_id: { - type: 'string', - description: 'Optional session ID to boost results from the current session.', - }, }, required: ['query'], }, @@ -71,29 +69,35 @@ const recallHandler = async ( } const query = String(args['query']); - const includeDocuments = args['include_documents'] !== false; const limit = Math.min(Math.max(Number(args['limit']) || 10, 1), 50); + // Build sources list — default to vault + common integrations + let sources: string[] | undefined; + if (Array.isArray(args['sources']) && args['sources'].length > 0) { + sources = args['sources'].map(String); + } else { + sources = ['vault', 'notion', 'google_drive', 'slack', 'google_mail']; + } + try { - // Use the new /memories/recall endpoint - const response = await client.post('/memories/recall', { + const response = await client.post('/memories/query', { body: { query, - include_documents: includeDocuments, - limit, - session_id: args['session_id'] ? String(args['session_id']) : undefined, + sources, + options: { + max_results: limit, + }, }, }); - // Format results for readability - const results = (response as any).results || []; - const sourcesSearched = (response as any).sources_searched || []; + const result = response as any; + const documents = result.documents || []; const formatted = { - query_id: (response as any).query_id, - sources_searched: sourcesSearched, - result_count: results.length, - results: results.map((r: any) => ({ + query_id: result.query_id, + result_count: documents.length, + answer: result.answer || undefined, + results: documents.map((r: any) => ({ title: r.title || r.resource_id, source: r.source, score: r.score, @@ -116,7 +120,7 @@ export const recallMcpTool: McpTool = { }; // ============================================================================ -// hyperspell_remember - Store a memory for future recall +// hyperspell_remember - Store a memory // ============================================================================ const rememberMetadata: Metadata = { @@ -124,7 +128,7 @@ const rememberMetadata: Metadata = { operation: 'write', tags: ['memory', 'store', 'remember'], httpMethod: 'post', - httpPath: '/memories/remember', + httpPath: '/memories/add', }; const rememberTool: Tool = { @@ -137,7 +141,7 @@ Use this tool AFTER completing significant work to preserve: - Project-specific patterns and conventions - User preferences and context -The stored memory will be searchable via hyperspell_recall alongside connected documents.`, +The stored memory will be searchable via hyperspell_recall alongside connected sources.`, inputSchema: { type: 'object', properties: { @@ -150,15 +154,13 @@ The stored memory will be searchable via hyperspell_recall alongside connected d type: 'string', description: 'Optional title for the memory.', }, - tags: { - type: 'array', - items: { type: 'string' }, - description: 'Optional tags for categorization (max 10).', - }, - session_id: { + collection: { type: 'string', - description: - 'Optional session ID for deduplication. Same content in same session will not be stored twice.', + description: 'Optional collection name to group related memories.', + }, + metadata: { + type: 'object', + description: 'Optional custom metadata for filtering (string/number/boolean values).', }, }, required: ['content'], @@ -179,13 +181,12 @@ const rememberHandler = async ( } try { - // Use the new /memories/remember endpoint - const response = await client.post('/memories/remember', { + const response = await client.post('/memories/add', { body: { - content, + text: content, title: args['title'] ? String(args['title']) : undefined, - tags: Array.isArray(args['tags']) ? args['tags'].map(String).slice(0, 10) : undefined, - session_id: args['session_id'] ? String(args['session_id']) : undefined, + collection: args['collection'] ? String(args['collection']) : undefined, + metadata: args['metadata'] && typeof args['metadata'] === 'object' ? args['metadata'] : undefined, }, }); @@ -194,12 +195,9 @@ const rememberHandler = async ( return asTextContentResult({ success: true, resource_id: result.resource_id, + source: result.source, status: result.status, - deduplicated: result.deduplicated || false, - message: - result.deduplicated ? - 'Content was already stored in this session (deduplicated)' - : 'Memory stored successfully and will be available for recall once indexed', + message: 'Memory stored successfully and will be available for recall once indexed', }); } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error'; @@ -221,31 +219,30 @@ const forgetMetadata: Metadata = { resource: 'memories', operation: 'write', tags: ['memory', 'delete', 'forget'], - httpMethod: 'post', - httpPath: '/memories/forget', + httpMethod: 'delete', + httpPath: '/memories/delete/{source}/{resource_id}', }; const forgetTool: Tool = { name: 'hyperspell_forget', - description: `Delete a memory by ID or semantic search. - -Use this tool to remove outdated or incorrect memories: -- Delete by memory_id for precise removal -- Delete by query to find and remove the closest matching memory + description: `Delete a memory by its resource ID. -At least one of memory_id or query must be provided.`, +Use this tool to remove outdated or incorrect memories. +The resource_id can be found in results from hyperspell_recall or hyperspell_profile.`, inputSchema: { type: 'object', properties: { - memory_id: { + resource_id: { type: 'string', description: 'The resource_id of the memory to delete.', }, - query: { + source: { type: 'string', - description: 'Semantic search query to find and delete the closest matching memory.', + description: 'The source of the memory (default: "vault").', + default: 'vault', }, }, + required: ['resource_id'], }, }; @@ -253,23 +250,21 @@ const forgetHandler = async ( client: Hyperspell, args: Record | undefined, ): Promise => { - if (!args?.['memory_id'] && !args?.['query']) { - return asErrorResult('Either memory_id or query must be provided'); + if (!args?.['resource_id']) { + return asErrorResult('resource_id is required'); } + const resourceId = String(args['resource_id']); + const source = String(args['source'] || 'vault'); + try { - const response = await client.post('/memories/forget', { - body: { - memory_id: args['memory_id'] ? String(args['memory_id']) : undefined, - query: args['query'] ? String(args['query']) : undefined, - }, - }); + const response = await client.delete(`/memories/delete/${source}/${resourceId}`, {}); const result = response as any; return asTextContentResult({ success: result.success, - deleted_memory_id: result.deleted_memory_id, + resource_id: result.resource_id, chunks_deleted: result.chunks_deleted, message: result.message, }); @@ -286,42 +281,33 @@ export const forgetMcpTool: McpTool = { }; // ============================================================================ -// hyperspell_profile - Get memory profile +// hyperspell_profile - Get recent memories // ============================================================================ const profileMetadata: Metadata = { resource: 'memories', operation: 'read', tags: ['memory', 'profile', 'context'], - httpMethod: 'post', - httpPath: '/memories/profile', + httpMethod: 'get', + httpPath: '/memories/list', }; const profileTool: Tool = { name: 'hyperspell_profile', - description: `Get a summary of recent and relevant memories. + description: `List recent memories to understand what context is available. -Use this tool to understand what context is available: -- recent_memories: Most recently stored memories -- similar_memories: Semantically similar memories (if query provided) - -Useful before responding to understand what you know about the user/project.`, +Use this tool to see what memories have been stored, including their +resource IDs (useful for hyperspell_forget).`, inputSchema: { type: 'object', properties: { - query: { + source: { type: 'string', - description: 'Optional focus query to find semantically similar memories.', - }, - recent_limit: { - type: 'number', - description: 'Maximum number of recent memories to return (1-50, default 10).', - default: 10, + description: 'Filter by source (e.g. "vault", "procedure"). Defaults to all sources.', }, - similar_limit: { - type: 'number', - description: 'Maximum number of similar memories to return (1-50, default 10).', - default: 10, + collection: { + type: 'string', + description: 'Filter by collection name.', }, }, }, @@ -334,38 +320,38 @@ const profileHandler = async ( client: Hyperspell, args: Record | undefined, ): Promise => { - const recentLimit = Math.min(Math.max(Number(args?.['recent_limit']) || 10, 1), 50); - const similarLimit = Math.min(Math.max(Number(args?.['similar_limit']) || 10, 1), 50); - try { - const response = await client.post('/memories/profile', { - body: { - query: args?.['query'] ? String(args['query']) : undefined, - recent_limit: recentLimit, - similar_limit: similarLimit, - }, - }); + const queryParams: Record = {}; + if (args?.['source']) { + queryParams['source'] = String(args['source']); + } + if (args?.['collection']) { + queryParams['collection'] = String(args['collection']); + } + + const qs = new URLSearchParams(queryParams).toString(); + const url = qs ? `/memories/list?${qs}` : '/memories/list'; + + const response = await client.get(url, {}); const result = response as any; + const items = result.items || result.data || []; const formatted = { - recent_memories: (result.recent_memories || []).map((r: any) => ({ - title: r.title || r.resource_id, + count: items.length, + memories: items.map((r: any) => ({ + title: r.title || r.data?.title || r.resource_id, source: r.source, resource_id: r.resource_id, - })), - similar_memories: (result.similar_memories || []).map((r: any) => ({ - title: r.title || r.resource_id, - source: r.source, - score: r.score, - resource_id: r.resource_id, + status: r.status, + collection: r.collection, })), }; return asTextContentResult(formatted); } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error'; - return asErrorResult(`Failed to get profile: ${message}`); + return asErrorResult(`Failed to list memories: ${message}`); } }; diff --git a/packages/openclaw/src/hook.ts b/packages/openclaw/src/hook.ts index 30fb972..1dbb35c 100644 --- a/packages/openclaw/src/hook.ts +++ b/packages/openclaw/src/hook.ts @@ -261,11 +261,11 @@ async function recallMemories( try { const response = (await hyperspellFetch( - '/memories/recall', + '/memories/query', { query, - include_documents: true, - limit: RECALL_LIMIT, + sources: ['vault', 'procedure', 'notion', 'google_drive', 'slack', 'google_mail'], + options: { max_results: RECALL_LIMIT }, }, apiKey, userId,