Files
openhuman/.claude/rules/17-skills-memory-inference-flow.md
T
Steven EnamakelandGitHub bfaabd3b86 fix/rename (#20)
* chore: update AlphaHuman version to 0.49.3 and configure updater plugin in tauri.conf.json

- Bumped the AlphaHuman package version in Cargo.lock to 0.49.3.
- Added updater configuration in tauri.conf.json to enable automatic updates with specified endpoints.

* refactor: rename AlphaHuman to OpenHuman across the codebase

- Updated all instances of "AlphaHuman" to "OpenHuman" in comments, tooltips, and constants to reflect the new branding.
- Adjusted relevant documentation and prompts to ensure consistency with the new name.

* refactor: update documentation and configurations to reflect OpenHuman branding

- Replaced all instances of "AlphaHuman" with "OpenHuman" in documentation, comments, and configuration files to ensure consistency with the new branding.
- Updated deep link URLs and related authentication flows to use the new "openhuman://" scheme.
- Adjusted paths and references in the skills system and other related files to align with the new project name.te

* refactor: standardize OpenHuman references and update configurations

- Replaced all instances of "AlphaHuman" with "OpenHuman" across documentation, comments, and configuration files to maintain branding consistency.
- Updated URLs and paths to reflect the new "openhuman://" scheme.
- Adjusted environment variable names and related settings to align with the new project identity.
- Enhanced documentation for clarity and accuracy regarding the OpenHuman framework.r

* chore: update subproject commit reference in skills directory

* refactor: update backend URL to reflect new service domain

- Changed default backend URL from "https://api.openhuman.xyz" to "https://api.tinyhumans.ai" in both JavaScript and Rust configuration files.
- Ensured consistency across the codebase regarding the new backend service endpoint.

* feat: introduce identity and migration modules for OpenHuman

- Added a new identity module to support AIEOS v1.1 JSON format, including structures for identity, psychology, linguistics, motivations, capabilities, physicality, history, and interests.
- Implemented a migration module to facilitate data migration from OpenClaw memory, including SQLite and Markdown sources, with detailed reporting on migration statistics and warnings.
- Established utility functions for handling multimodal content and image processing within the OpenHuman framework.
- Enhanced the agent system with new dispatcher and classifier functionalities to improve tool management and message classification.

* chore: remove Android project files and configurations

- Deleted various Android project files including .editorconfig, .gitignore, build.gradle.kts, gradle.properties, and others to clean up the project structure.
- Removed all related resources, layouts, and source files from the Android app directory to streamline the codebase.
- This cleanup is part of a larger effort to refactor and simplify the project structure.

* refactor: update login flow and remove Telegram integration

- Removed the TelegramLoginButton component and its references from the OAuthLoginSection, streamlining the login options.
- Updated the AppRoutes to remove the login route, reflecting changes in the authentication flow.
- Enhanced the RotatingTetrahedronCanvas component with improved geometry and lighting effects for better visual presentation.
- Adjusted the TypewriterGreeting component's styling for consistency.
- Cleaned up the Welcome page to integrate the OAuthLoginSection directly, improving user experience.

* chore: update subproject commit reference in skills directory

* chore: update test configurations and improve test assertions

- Modified test scripts in package.json to use a specific Vitest configuration file for consistency.
- Updated assertions in loader tests to ensure loading durations are non-negative.
- Enhanced tool loading tests to clarify expected behavior regarding localStorage and cache management.
- Adjusted agent tool registry tests to improve error handling and ensure accurate statistics.
- Refined device detection tests to reflect updated fallback URLs.

* fix: enhance parameter formatting and remove unused components

- Updated the `formatParameters` function to handle cases where schema properties are empty, returning a more informative response.
- Deleted the `DownloadScreen` component and associated device detection utilities to streamline the codebase and remove unused functionality.
- Adjusted tests to reflect changes in the tool loading and agent tool registry, ensuring accuracy in assertions.

* chore: simplify Vitest configuration by removing unused include patterns

- Updated the Vitest configuration to remove unnecessary test file patterns, streamlining the test setup for better clarity and maintainability.

* refactor: update paths and comments for AI configuration and file watching

- Modified Vite configuration to ignore only the `src-tauri` directory.
- Updated logging messages to reflect the correct path for writing AI configuration files.
- Adjusted fetch calls in the file watcher to use the new path for `TOOLS.md`.
- Revised comments and logic in Rust code to clarify the handling of AI configuration file paths, including legacy fallback options.

* chore: remove unused updater secrets from GitHub Actions workflow

- Deleted UPDATER_GIST_URL and UPDATER_GIST_ID environment variables from the package-and-publish workflow, streamlining the configuration.

* chore: comment out Vitest thresholds for clarity

- Commented out the thresholds section in the Vitest configuration to improve clarity and maintainability, as it is currently not in use.

* ran formatter

* chore: update updater public key in tauri configuration

- Replaced the existing public key in the updater plugin configuration with a new value to ensure proper functionality and security.

* chore: update ESLint configuration and refactor components

- Added `localStorage` and `sessionStorage` as readonly globals in ESLint configuration for better linting support.
- Removed unused imports from `SkillsPanel.tsx` to clean up the code.
- Changed the type of `watcherInterval` in `file-watcher.ts` for improved type safety.
- Refactored toast management logic in `Intelligence.tsx` to enhance clarity and maintainability.
- Simplified import statements in `IntelligenceProvider.tsx` for consistency.
- Streamlined object property shorthand in `agentToolRegistry.ts` for cleaner code.

* refactor: improve error handling and type safety in Intelligence component

- Enhanced toast notification logic to defer state updates, preventing potential issues with setState in effects.
- Updated the source filter dispatch to use a more specific type for improved type safety.

* refactor: enhance type safety across various components and services

- Updated type definitions from `any` to `unknown` in multiple files to improve type safety and prevent potential runtime errors.
- Refactored state management in `TauriCommandsPanel` to use more specific types.
- Adjusted context and parameters in several interfaces to ensure consistent typing.
- Added ESLint directive to `polyfills.ts` for intentional global assignments.
- Streamlined type handling in utility functions and API responses for better clarity and maintainability.

* refactor: streamline import statements and improve code clarity

- Consolidated import statements in `agentToolRegistry.ts` and `intelligenceSlice.ts` for better readability.
- Simplified the `createTestStore` function in `test-utils.tsx` to enhance code conciseness.
- Cleaned up the `isExecutionStepProgressEvent` function in `intelligence-chat-api.ts` for improved clarity and maintainability.
2026-03-26 17:04:46 -07:00

11 KiB

Skills → Memory Layer → Agent Inference: Full Flow

Overview

This document traces the complete data flow from skill discovery and OAuth/sync events, through the TinyHumans Neocortex memory layer (tinyhumansai SDK), and into the Rust-side agentic inference loop — showing exactly how skill data is written to and read from memory and how it reaches the LLM context at inference time.


1. Skill Discovery & Lifecycle (SkillProvider.tsx)

File: src/providers/SkillProvider.tsx

On app mount (when a JWT token is present) SkillProvider calls invoke('runtime_discover_skills') → the Rust runtime scans skills/skills/{skill-id}/manifest.json from the git submodule and returns a manifest list.

Token present
  → discoverSkills()
    → invoke('runtime_discover_skills')         // Rust: qjs_engine.rs:184
      → reads skills/skills/*/manifest.json
      → filters: is_javascript() && supports_current_platform()
      → returns manifests[]
  → skillManager.registerSkill(manifest)        // in-memory registry
  → for each manifest with setupComplete:
      skillManager.startSkill(manifest)          // starts V8/QuickJS instance

Two Tauri event listeners run continuously:

Event What it does
skill-state-changed Dispatches setSkillState into Redux skillsSlice.skillStates[skillId]
runtime:skill-status-changed Updates skillsSlice.skills[skillId].status; surfaces errors

2. Memory Client Initialisation

File: src-tauri/src/memory/mod.rs Tauri command: init_memory_client (called by frontend after auth)

The MemoryClient wraps the TinyHumansMemoryClient from the tinyhumansai Rust crate. It is constructed with the user's JWT (authSlice.token) and stored as Arc<MemoryClient> in MemoryState (a Mutex<Option<MemoryClientRef>>).

Base URL resolution (in priority order):

  1. OPENHUMAN_BASE_URL env var
  2. TINYHUMANS_BASE_URL env var
  3. SDK default

3. Skill → Memory Sync: Two Write Paths

Both trigger inside src-tauri/src/runtime/qjs_skill_instance.rs.

3a. OAuth Completion (skill/oauth-complete)

When a user connects a skill via OAuth, the JS runtime calls back with skill/oauth-complete. After the OAuth flow completes the skill's ops state (data published via state.set()) is snapshotted and stored to memory:

skill/oauth-complete handler
  → handle_js_call(rt, ctx, "onOAuthComplete", params)   // runs skill JS
  → ops_state.read().data.clone()                        // snapshot skill state
  → tokio::spawn (fire-and-forget):
      MemoryClient::store_skill_sync(
          skill_id       = e.g. "gmail"
          integration_id = params["integrationId"]       // e.g. user email
          title          = "{skill} OAuth sync — {integrationId}"
          content        = JSON snapshot of ops state
          namespace      = "skill:{skill_id}:{integration_id}"
      )
      → tinyhumansai::TinyHumansMemoryClient::insert_memory(InsertMemoryParams { ... })
      → HTTP POST to TinyHumans Neocortex API

3b. Periodic Sync (skill/sync)

The runtime fires skill/sync events on a cron schedule. The flow is identical to OAuth completion but uses integration_id = "default" and title "{skill} periodic sync":

skill/sync handler
  → handle_js_call(rt, ctx, "onSync", "{}")
  → ops_state.read().data.clone()
  → tokio::spawn:
      MemoryClient::store_skill_sync(
          skill_id       = e.g. "notion"
          integration_id = "default"
          namespace      = "skill:notion:default"
          content        = JSON snapshot
      )

Namespace Pattern

All skill memories are stored under skill:{skill_id}:{integration_id}. Examples: skill:gmail:user@example.com, skill:notion:default.


4. Memory Operations Reference

File: src-tauri/src/memory/mod.rs

Method SDK call When used
store_skill_sync(...) insert_memory OAuth complete, periodic sync
query_skill_context(...) query_memory RAG query — fetch relevant chunks for a user question
recall_skill_context(...) recall_memory Recall synthesised summary from Master node
clear_skill_memory(...) delete_memory OAuth revoke / disconnect

5. Conversation → Inference: Two Code Paths

File: src/pages/Conversations.tsx

useRustChat() returns true when running in Tauri (desktop). This selects between two paths:

handleSendMessage(text)
  ├─ rustChat == true  → Rust path  (invoke chat_send)
  └─ rustChat == false → Web path   (handleSendMessageWeb — TypeScript loop)

5a. Rust Path (Desktop)

chatSend({ threadId, message, model, authToken, backendUrl, messages, notionContext })
  → invoke('chat_send')               // src-tauri/src/commands/chat.rs:359
    → spawns background task
    → chat_send_inner(...)

Completion events flow back over Tauri events:

Event Frontend handler
chat:tool_call shows active tool indicator
chat:tool_result clears tool indicator
chat:done dispatch(addInferenceResponse(...))
chat:error shows error, clears loading state

5b. Web/Fallback Path

Used when not running in Tauri (browser). Runs the agentic loop entirely in TypeScript:

  • Calls inferenceApi.createChatCompletion(request) directly
  • Executes tools via skillManager.callTool(skillId, toolName, args)
  • Both paths share the same 5-round MAX_TOOL_ROUNDS limit and {skillId}__{toolName} naming convention

6. Rust Agentic Loop in Detail

File: src-tauri/src/commands/chat.rschat_send_inner()

Step 1: Load OpenClaw context
  → load_openclaw_context(app)
    → reads ai/SOUL.md, IDENTITY.md, AGENTS.md, USER.md, BOOTSTRAP.md, MEMORY.md, TOOLS.md
    → cached in static AI_CONFIG_CACHE (cleared on restart)
    → truncated to MAX_CONTEXT_CHARS (20,000 chars)

Step 2: Recall memory context
  → MemoryClient::recall_skill_context("conversations", thread_id, 10)
    → tinyhumansai recall_memory(namespace="skill:conversations:{thread_id}")
    → returns synthesised summary string or None

Step 3: Build processed user message
  processed = user_message
  if openclaw_context  → prepend as "## Project Context\n...\n\nUser message: {processed}"
  if memory_context    → prepend as "[MEMORY_CONTEXT]\n{mem}\n[/MEMORY_CONTEXT]\n\n{processed}"
  if notion_context    → prepend as "{notionContext}\n\n{processed}"

Step 4: Build messages array
  → history (ChatMessagePayload[]) + processed user message

Step 5: Discover tools
  → engine.all_tools()
    → returns all tools from running skills
    → namespaced: "{skill_id}__{tool_name}"
    → formatted as OpenAI function-calling schema

Step 6: Agentic loop (max 5 rounds)
  for round in 0..MAX_TOOL_ROUNDS:
    POST {backend_url}/openai/v1/chat/completions
      body: { model, messages, tools, tool_choice: "auto" }
      timeout: 120s
      auth: Bearer {auth_token}

    if finish_reason == "tool_calls":
      emit chat:tool_call
      engine.call_tool(skill_id, tool_name, args)   // 60s timeout
        → QuickJS/V8 runtime executes skill JS tool handler
      emit chat:tool_result
      append tool result to messages
      continue loop

    else (finish_reason == "stop"):
      emit chat:done { full_response, rounds_used, token_counts }
      return Ok(())

7. Tool Execution: Rust → Skill JS

File: src-tauri/src/runtime/qjs_engine.rscall_tool(skill_id, tool_name, args)

The Rust runtime routes call_tool into the running QuickJS/V8 skill instance:

  • Serialises args as JSON
  • Calls the JS tool handler registered by the skill
  • Returns ToolCallResult { content: Vec<ToolContent>, is_error: bool }
  • Text content is extracted and appended as the tool role message in the loop

8. End-to-End Flow Summary

User types message (Conversations.tsx)
  │
  ├─ [Web path only] invoke('recall_memory')      → TinyHumans API (recall)
  │                                                  returns synthesised context
  ├─ buildNotionContext()                          → from Redux skillStates.notion
  │
  ▼
invoke('chat_send')                                [Rust/desktop path]
  │
  ▼
Rust: chat_send_inner()
  ├─ load_openclaw_context()                       → ai/*.md files (cached)
  ├─ recall_skill_context("conversations", tid)    → TinyHumans API (recall)
  ├─ Build prompt: [OpenClaw] + [MEMORY] + [Notion] + user_message
  ├─ discover_tools()                              → all running skill tools
  │
  └─ Agentic loop (≤5 rounds):
       POST /openai/v1/chat/completions             → Backend LLM (neocortex-mk1)
         │
         ├─ tool_calls → engine.call_tool()         → QuickJS/V8 skill instance
         │                  └─ JS tool handler
         │                  └─ returns result string
         │                  └─ appended as tool message
         │
         └─ stop → emit chat:done
                     └─ Frontend: dispatch(addInferenceResponse(...))

Separately (async, fire-and-forget):
  Skill onSync() / onOAuthComplete()
    → MemoryClient::store_skill_sync()
      → TinyHumans insert_memory (namespace: skill:{id}:{integrationId})

Key Files Reference

File Role
src/providers/SkillProvider.tsx Discovery, lifecycle, Redux state sync
src/pages/Conversations.tsx Message send, both code paths, Notion context
src/services/chatService.ts chatSend(), chatCancel(), useRustChat()
src-tauri/src/commands/chat.rs Rust agentic loop, context assembly, tool dispatch
src-tauri/src/commands/memory.rs recall_memory Tauri command
src-tauri/src/memory/mod.rs MemoryClient wrapping tinyhumansai SDK
src-tauri/src/runtime/qjs_skill_instance.rs Skill sync → memory write triggers
src-tauri/src/runtime/qjs_engine.rs discover_skills(), call_tool(), all_tools()
skills/skills/*/manifest.json Skill metadata (git submodule)