Compare commits
No commits in common. "main" and "v2.4.2" have entirely different histories.
|
|
@ -1 +0,0 @@
|
||||||
../skills
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
../skills
|
|
||||||
288
.env.template
|
|
@ -8,10 +8,6 @@
|
||||||
# Application Settings
|
# Application Settings
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
LOG_LEVEL=INFO
|
LOG_LEVEL=INFO
|
||||||
PERFORMANCE_LOG_FORMAT=compact # compact|rich
|
|
||||||
# API server processes used by the Docker entrypoint (default: 1).
|
|
||||||
# Each process owns a separate pool when connection pooling is enabled.
|
|
||||||
# API_WORKERS=1
|
|
||||||
# SESSION_OBSERVERS_LIMIT=10
|
# SESSION_OBSERVERS_LIMIT=10
|
||||||
# GET_CONTEXT_MAX_TOKENS=100000
|
# GET_CONTEXT_MAX_TOKENS=100000
|
||||||
# MAX_FILE_SIZE=5242880 # Bytes
|
# MAX_FILE_SIZE=5242880 # Bytes
|
||||||
|
|
@ -19,24 +15,11 @@ PERFORMANCE_LOG_FORMAT=compact # compact|rich
|
||||||
|
|
||||||
# Embedding settings
|
# Embedding settings
|
||||||
# EMBED_MESSAGES=true
|
# EMBED_MESSAGES=true
|
||||||
# EMBEDDING_VECTOR_DIMENSIONS=1536
|
# MAX_EMBEDDING_TOKENS=8192
|
||||||
# EMBEDDING_MAX_INPUT_TOKENS=8192
|
# MAX_EMBEDDING_TOKENS_PER_REQUEST=300000
|
||||||
# EMBEDDING_MAX_TOKENS_PER_REQUEST=300000
|
|
||||||
# EMBEDDING_MODEL_CONFIG__TRANSPORT=openai
|
|
||||||
# EMBEDDING_MODEL_CONFIG__MODEL=text-embedding-3-small
|
|
||||||
# EMBEDDING_MODEL_CONFIG__MAX_BATCH_SIZE=10
|
|
||||||
# EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL=
|
|
||||||
# EMBEDDING_MODEL_CONFIG__OVERRIDES__API_KEY_ENV=
|
|
||||||
|
|
||||||
# LANGFUSE_HOST=
|
# LANGFUSE_HOST=
|
||||||
# LANGFUSE_PUBLIC_KEY=
|
# LANGFUSE_PUBLIC_KEY=
|
||||||
# LANGFUSE_SECRET_KEY=
|
|
||||||
|
|
||||||
# COLLECT_METRICS_LOCAL=false
|
|
||||||
# LOCAL_METRICS_FILE=metrics.jsonl
|
|
||||||
# REASONING_TRACES_FILE=traces.jsonl # Path to JSONL file for reasoning traces
|
|
||||||
|
|
||||||
# NAMESPACE="honcho"
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Database Settings (REQUIRED)
|
# Database Settings (REQUIRED)
|
||||||
|
|
@ -50,15 +33,12 @@ DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/postgres
|
||||||
# DB_POOL_CLASS=default
|
# DB_POOL_CLASS=default
|
||||||
# DB_POOL_SIZE=10
|
# DB_POOL_SIZE=10
|
||||||
# DB_MAX_OVERFLOW=20
|
# DB_MAX_OVERFLOW=20
|
||||||
# DB_POOL_TIMEOUT=5 # seconds a pooled checkout waits for a free connection (QueuePool only)
|
# DB_POOL_TIMEOUT=30
|
||||||
# DB_POOL_RECYCLE=300
|
# DB_POOL_RECYCLE=300
|
||||||
# DB_POOL_PRE_PING=true
|
# DB_POOL_PRE_PING=true
|
||||||
# DB_POOL_USE_LIFO=true
|
# DB_POOL_USE_LIFO=true
|
||||||
# DB_SQL_DEBUG=false
|
# DB_SQL_DEBUG=false
|
||||||
# DB_TRACING=false
|
# DB_TRACING=false
|
||||||
# Per-connection establish timeout (seconds) so a single connection attempt
|
|
||||||
# fails fast instead of hanging when the server/pooler is unreachable.
|
|
||||||
# DB_CONNECT_TIMEOUT_SECONDS=2
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Authentication Settings
|
# Authentication Settings
|
||||||
|
|
@ -71,179 +51,78 @@ AUTH_USE_AUTH=false
|
||||||
# AUTH_JWT_SECRET=your-secret-key-here
|
# AUTH_JWT_SECRET=your-secret-key-here
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# LLM Provider (REQUIRED)
|
# LLM API Keys (REQUIRED for full functionality)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Honcho uses LLMs for memory extraction, summarization, dialectic chat, and
|
# OpenAI API key for embeddings
|
||||||
# dream consolidation. The server will fail to start without a provider configured.
|
LLM_OPENAI_API_KEY=your-openai-api-key-here
|
||||||
#
|
|
||||||
# Quick start: set LLM_OPENAI_API_KEY below to use the built-in defaults.
|
# Anthropic API key for dialectic and deriver functionality
|
||||||
# Text-generation features default to transport = "openai" and
|
LLM_ANTHROPIC_API_KEY=your-anthropic-api-key-here
|
||||||
# model = "gpt-5.4-mini". Embeddings default to transport = "openai" and
|
|
||||||
# model = "text-embedding-3-small". For OpenAI-compatible proxies
|
# Google API key for summarization (if using Gemini)
|
||||||
# (OpenRouter, Together, Fireworks, vLLM, Ollama, LiteLLM), override
|
# LLM_GEMINI_API_KEY=your-google-api-key-here
|
||||||
# MODEL_CONFIG__MODEL and MODEL_CONFIG__OVERRIDES__BASE_URL on each feature
|
|
||||||
# section you want to route through that endpoint.
|
# Groq API key for query generation (if using Groq)
|
||||||
# Models must support tool calling (function calling).
|
# LLM_GROQ_API_KEY=your-groq-api-key-here
|
||||||
#
|
|
||||||
# Supported transports: openai, anthropic, gemini
|
# Base URL for OpenAI Compatible Requests if you want to use a different provider
|
||||||
# Each transport picks up its API key from the corresponding LLM_*_API_KEY.
|
# LLM_OPENAI_COMPATIBLE_BASE_URL=
|
||||||
# Base URLs are set per-module via MODEL_CONFIG__OVERRIDES__BASE_URL.
|
# LLM_OPENAI_COMPATIBLE_API_KEY=
|
||||||
#
|
|
||||||
LLM_OPENAI_API_KEY=your-api-key-here
|
|
||||||
# LLM_ANTHROPIC_API_KEY=
|
|
||||||
# LLM_GEMINI_API_KEY=
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# LLM Configuration
|
# LLM Configuration
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Global LLM settings
|
# Global LLM settings
|
||||||
# LLM_DEFAULT_MAX_TOKENS=2500
|
# LLM_DEFAULT_MAX_TOKENS=2500
|
||||||
# LLM_MAX_TOOL_OUTPUT_CHARS=10000 # Max chars for tool output (~2500 tokens)
|
|
||||||
# LLM_MAX_MESSAGE_CONTENT_CHARS=2000 # Max chars per message in tool results
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Deriver (Background Worker)
|
# Deriver (Background Worker) Settings
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# DERIVER_ENABLED=true
|
|
||||||
# Defaults:
|
|
||||||
# DERIVER_MODEL_CONFIG__TRANSPORT=openai
|
|
||||||
# DERIVER_MODEL_CONFIG__MODEL=gpt-5.4-mini
|
|
||||||
# Optional overrides:
|
|
||||||
# DERIVER_MODEL_CONFIG__MODEL=your-model-here
|
|
||||||
# DERIVER_MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
# DERIVER_WORKERS=1
|
# DERIVER_WORKERS=1
|
||||||
# DERIVER_POLLING_SLEEP_INTERVAL_SECONDS=1.0
|
# DERIVER_POLLING_SLEEP_INTERVAL_SECONDS=1.0
|
||||||
# Adaptive polling: grows the idle/error sleep from the base toward the max by
|
|
||||||
# the multiplier each cycle, snapping back to base when work is found.
|
|
||||||
# DERIVER_POLLING_BACKOFF_ENABLED=true
|
|
||||||
# DERIVER_POLLING_SLEEP_MAX_INTERVAL_SECONDS=30.0
|
|
||||||
# DERIVER_POLLING_BACKOFF_MULTIPLIER=2.0
|
|
||||||
# Jitter so instances that start together don't poll in lockstep. Startup: sleep
|
|
||||||
# a random delay in [0, value] before the first poll (0.0 disables). Per-cycle:
|
|
||||||
# multiply every poll sleep by a random factor in [1-ratio, 1+ratio] (0.0 disables).
|
|
||||||
# DERIVER_POLLING_STARTUP_JITTER_SECONDS=30.0
|
|
||||||
# DERIVER_POLLING_JITTER_RATIO=0.5
|
|
||||||
# DERIVER_STALE_SESSION_TIMEOUT_MINUTES=5
|
# DERIVER_STALE_SESSION_TIMEOUT_MINUTES=5
|
||||||
# DERIVER_QUEUE_ERROR_RETENTION_SECONDS=2592000 # 30 days
|
# DERIVER_PROVIDER=google
|
||||||
# DERIVER_MODEL_CONFIG__TEMPERATURE=
|
# DERIVER_MODEL=gemini-2.0-flash-lite
|
||||||
# DERIVER_MODEL_CONFIG__THINKING_EFFORT=minimal
|
# DERIVER_MAX_OUTPUT_TOKENS=2500
|
||||||
# DERIVER_MODEL_CONFIG__THINKING_BUDGET_TOKENS=1024 # Gemini/Anthropic only
|
# only applied when using Anthropic as provider
|
||||||
# DERIVER_MODEL_CONFIG__STRUCTURED_OUTPUT_MODE=json_object # for providers without json_schema support
|
# DERIVER_THINKING_BUDGET_TOKENS=1024
|
||||||
# DERIVER_DEDUPLICATE=true
|
|
||||||
# DERIVER_MODEL_CONFIG__MAX_OUTPUT_TOKENS=4096
|
|
||||||
# DERIVER_LOG_OBSERVATIONS=false
|
|
||||||
# DERIVER_MAX_INPUT_TOKENS=25000
|
|
||||||
# DERIVER_MAX_CUSTOM_INSTRUCTIONS_TOKENS=2000
|
|
||||||
# DERIVER_WORKING_REPRESENTATION_MAX_OBSERVATIONS=100
|
# DERIVER_WORKING_REPRESENTATION_MAX_OBSERVATIONS=100
|
||||||
# DERIVER_REPRESENTATION_BATCH_WORK_UNIT_TARGET_TOKENS=512 # Min tokens a work unit accumulates before the deriver claims it; 0 disables the gate
|
# DERIVER_REPRESENTATION_BATCH_MAX_TOKENS=4096
|
||||||
# DERIVER_REPRESENTATION_BATCH_TARGET_INPUT_TOKENS=1024 # Max context-window tokens per deriver LLM call
|
# DERIVER_MAX_INPUT_TOKENS=23000
|
||||||
# DERIVER_REPRESENTATION_BATCH_MAX_AGE_SECONDS=1800
|
|
||||||
# DERIVER_FLUSH_ENABLED=false # Bypass batch token threshold, process work immediately
|
|
||||||
# DERIVER_MODEL_CONFIG__FALLBACK__MODEL=
|
|
||||||
# DERIVER_MODEL_CONFIG__FALLBACK__TRANSPORT=
|
|
||||||
# DERIVER_MODEL_CONFIG__OVERRIDES__BASE_URL=
|
|
||||||
# DERIVER_MODEL_CONFIG__OVERRIDES__API_KEY_ENV=
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Peer Card
|
# Peer Card Configuration
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# PEER_CARD_ENABLED=true
|
# ENABLED=true
|
||||||
|
# PROVIDER=openai
|
||||||
|
# MODEL=gpt-5-nano-2025-08-07
|
||||||
|
# MAX_OUTPUT_TOKENS=4000
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Dialectic
|
# Dialectic Settings
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# DIALECTIC_MAX_OUTPUT_TOKENS=8192
|
# DIALECTIC_PROVIDER=anthropic
|
||||||
# DIALECTIC_MAX_INPUT_TOKENS=100000
|
# DIALECTIC_MODEL=claude-sonnet-4-20250514
|
||||||
# DIALECTIC_HISTORY_TOKEN_LIMIT=8192
|
# DIALECTIC_PERFORM_QUERY_GENERATION=false
|
||||||
# DIALECTIC_SESSION_HISTORY_MAX_TOKENS=4096
|
# DIALECTIC_QUERY_GENERATION_PROVIDER=groq
|
||||||
#
|
# DIALECTIC_QUERY_GENERATION_MODEL=llama-3.1-8b-instant
|
||||||
# Per-level settings (reasoning_level parameter in API)
|
# DIALECTIC_MAX_OUTPUT_TOKENS=2500
|
||||||
# Each level has its own nested MODEL_CONFIG, tool iterations, and max output tokens.
|
# DIALECTIC_SEMANTIC_SEARCH_TOP_K=10
|
||||||
# MAX_OUTPUT_TOKENS is optional per level; if not set, uses global DIALECTIC_MAX_OUTPUT_TOKENS.
|
# DIALECTIC_SEMANTIC_SEARCH_MAX_DISTANCE=0.85
|
||||||
# Defaults:
|
# DIALECTIC_THINKING_BUDGET_TOKENS=1024
|
||||||
# DIALECTIC_LEVELS__minimal__MODEL_CONFIG__TRANSPORT=openai
|
# DIALECTIC_CONTEXT_WINDOW_SIZE=100000
|
||||||
# DIALECTIC_LEVELS__minimal__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
|
||||||
# DIALECTIC_LEVELS__minimal__MAX_TOOL_ITERATIONS=1
|
|
||||||
# DIALECTIC_LEVELS__minimal__MAX_OUTPUT_TOKENS=250
|
|
||||||
# DIALECTIC_LEVELS__minimal__TOOL_CHOICE=auto
|
|
||||||
# DIALECTIC_LEVELS__low__MODEL_CONFIG__TRANSPORT=openai
|
|
||||||
# DIALECTIC_LEVELS__low__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
|
||||||
# DIALECTIC_LEVELS__low__MAX_TOOL_ITERATIONS=5
|
|
||||||
# DIALECTIC_LEVELS__low__TOOL_CHOICE=auto
|
|
||||||
# DIALECTIC_LEVELS__medium__MODEL_CONFIG__TRANSPORT=openai
|
|
||||||
# DIALECTIC_LEVELS__medium__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
|
||||||
# DIALECTIC_LEVELS__medium__MAX_TOOL_ITERATIONS=2
|
|
||||||
# DIALECTIC_LEVELS__high__MODEL_CONFIG__TRANSPORT=openai
|
|
||||||
# DIALECTIC_LEVELS__high__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
|
||||||
# DIALECTIC_LEVELS__high__MAX_TOOL_ITERATIONS=4
|
|
||||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__TRANSPORT=openai
|
|
||||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
|
||||||
# DIALECTIC_LEVELS__max__MAX_TOOL_ITERATIONS=10
|
|
||||||
# Optional overrides (model and OpenAI-compatible base URL are per-level):
|
|
||||||
# DIALECTIC_LEVELS__minimal__MODEL_CONFIG__MODEL=your-model-here
|
|
||||||
# DIALECTIC_LEVELS__minimal__MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
# DIALECTIC_LEVELS__low__MODEL_CONFIG__MODEL=your-model-here
|
|
||||||
# DIALECTIC_LEVELS__low__MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
# DIALECTIC_LEVELS__medium__MODEL_CONFIG__MODEL=your-model-here
|
|
||||||
# DIALECTIC_LEVELS__medium__MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
# DIALECTIC_LEVELS__high__MODEL_CONFIG__MODEL=your-model-here
|
|
||||||
# DIALECTIC_LEVELS__high__MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__MODEL=your-model-here
|
|
||||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__THINKING_EFFORT=medium
|
|
||||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__THINKING_BUDGET_TOKENS=1024
|
|
||||||
# Optional backup per level (must set both or neither):
|
|
||||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__FALLBACK__MODEL=gemini-2.5-pro
|
|
||||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__FALLBACK__TRANSPORT=gemini
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Summary
|
# Summary Settings
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# SUMMARY_ENABLED=true
|
# SUMMARY_ENABLED=true
|
||||||
# Defaults:
|
|
||||||
# SUMMARY_MODEL_CONFIG__TRANSPORT=openai
|
|
||||||
# SUMMARY_MODEL_CONFIG__MODEL=gpt-5.4-mini
|
|
||||||
# Optional overrides:
|
|
||||||
# SUMMARY_MODEL_CONFIG__MODEL=your-model-here
|
|
||||||
# SUMMARY_MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
# SUMMARY_MODEL_CONFIG__THINKING_EFFORT=minimal
|
|
||||||
# SUMMARY_MODEL_CONFIG__THINKING_BUDGET_TOKENS=1024 # Gemini/Anthropic only
|
|
||||||
# SUMMARY_MESSAGES_PER_SHORT_SUMMARY=20
|
# SUMMARY_MESSAGES_PER_SHORT_SUMMARY=20
|
||||||
# SUMMARY_MESSAGES_PER_LONG_SUMMARY=60
|
# SUMMARY_MESSAGES_PER_LONG_SUMMARY=60
|
||||||
|
# SUMMARY_PROVIDER=google
|
||||||
|
# SUMMARY_MODEL=gemini-1.5-flash-latest
|
||||||
# SUMMARY_MAX_TOKENS_SHORT=1000
|
# SUMMARY_MAX_TOKENS_SHORT=1000
|
||||||
# SUMMARY_MAX_TOKENS_LONG=4000
|
# SUMMARY_MAX_TOKENS_LONG=2000
|
||||||
# SUMMARY_MODEL_CONFIG__FALLBACK__MODEL=
|
# SUMMARY_THINKING_BUDGET_TOKENS=512
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Dream
|
|
||||||
# =============================================================================
|
|
||||||
# DREAM_ENABLED=true
|
|
||||||
# Defaults:
|
|
||||||
# DREAM_DEDUCTION_MODEL_CONFIG__TRANSPORT=openai
|
|
||||||
# DREAM_DEDUCTION_MODEL_CONFIG__MODEL=gpt-5.4-mini
|
|
||||||
# DREAM_INDUCTION_MODEL_CONFIG__TRANSPORT=openai
|
|
||||||
# DREAM_INDUCTION_MODEL_CONFIG__MODEL=gpt-5.4-mini
|
|
||||||
# Optional overrides:
|
|
||||||
# DREAM_DEDUCTION_MODEL_CONFIG__MODEL=your-model-here
|
|
||||||
# DREAM_DEDUCTION_MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
# DREAM_INDUCTION_MODEL_CONFIG__MODEL=your-model-here
|
|
||||||
# DREAM_INDUCTION_MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
# DREAM_DOCUMENT_THRESHOLD=50
|
|
||||||
# DREAM_IDLE_TIMEOUT_MINUTES=60
|
|
||||||
# DREAM_MIN_HOURS_BETWEEN_DREAMS=8
|
|
||||||
# DREAM_ENABLED_TYPES=["omni"]
|
|
||||||
# DREAM_MAX_TOOL_ITERATIONS=20
|
|
||||||
# DREAM_HISTORY_TOKEN_LIMIT=16384
|
|
||||||
|
|
||||||
# Surprisal sampling (advanced):
|
|
||||||
# DREAM_SURPRISAL__ENABLED=false
|
|
||||||
# DREAM_SURPRISAL__TREE_TYPE=kdtree
|
|
||||||
# DREAM_SURPRISAL__TREE_K=5
|
|
||||||
# DREAM_SURPRISAL__SAMPLING_STRATEGY=recent
|
|
||||||
# DREAM_SURPRISAL__SAMPLE_SIZE=200
|
|
||||||
# DREAM_SURPRISAL__TOP_PERCENT_SURPRISAL=0.10
|
|
||||||
# DREAM_SURPRISAL__MIN_HIGH_SURPRISAL_FOR_REPLACE=10
|
|
||||||
# DREAM_SURPRISAL__INCLUDE_LEVELS=["explicit","deductive"]
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Webhook Settings
|
# Webhook Settings
|
||||||
|
|
@ -263,72 +142,7 @@ LLM_OPENAI_API_KEY=your-api-key-here
|
||||||
# SENTRY_PROFILES_SAMPLE_RATE=0.1
|
# SENTRY_PROFILES_SAMPLE_RATE=0.1
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Prometheus Metrics Settings (Pull-based metrics)
|
# Metrics (Optional)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# METRICS_ENABLED=false
|
# ENABLED=false
|
||||||
# METRICS_NAMESPACE=honcho # Inherits from NAMESPACE if not set
|
# NAMESPACE=honcho
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# CloudEvents Telemetry Settings (Analytics events)
|
|
||||||
# =============================================================================
|
|
||||||
# TELEMETRY_ENABLED=false
|
|
||||||
# TELEMETRY_ENDPOINT=https://telemetry.honcho.dev/v1/events
|
|
||||||
# TELEMETRY_HEADERS={"Authorization": "Bearer your-token"} # JSON string for auth headers
|
|
||||||
# TELEMETRY_BATCH_SIZE=100
|
|
||||||
# TELEMETRY_FLUSH_INTERVAL_SECONDS=1.0
|
|
||||||
# TELEMETRY_FLUSH_THRESHOLD=50
|
|
||||||
# TELEMETRY_MAX_RETRIES=3
|
|
||||||
# TELEMETRY_MAX_BUFFER_SIZE=10000
|
|
||||||
# TELEMETRY_NAMESPACE=honcho # Inherits from NAMESPACE if not set
|
|
||||||
|
|
||||||
# Full-fidelity payload tracing (llm.call.traced / trace.content). Default-off
|
|
||||||
# TELEMETRY_TRACE_PAYLOADS_ENABLED=false # Trace events ship to TELEMETRY_ENDPOINT
|
|
||||||
# TELEMETRY_TRACE_MAX_BYTES=262144 # Per-message cap; oversized content is clipped
|
|
||||||
# TELEMETRY_TRACE_PURPOSES=[] # JSON list of CallPurpose values to capture; empty = all
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Cache
|
|
||||||
# =============================================================================
|
|
||||||
# CACHE_ENABLED=false
|
|
||||||
# CACHE_URL="redis://localhost:6379/0?suppress=true"
|
|
||||||
# CACHE_CLUSTER=false # true when CACHE_URL is a Redis Cluster (e.g. Memorystore for Redis Cluster)
|
|
||||||
# CACHE_NAMESPACE="honcho" # Inherits from NAMESPACE if not set
|
|
||||||
# CACHE_DEFAULT_TTL_SECONDS=300
|
|
||||||
# CACHE_DEFAULT_LOCK_TTL_SECONDS=5
|
|
||||||
# CACHE_LOCK_WAIT_CHECK_INTERVAL_SECONDS=0.1
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# CORS Settings
|
|
||||||
# =============================================================================
|
|
||||||
# JSON array of origins allowed by the FastAPI CORSMiddleware. Defaults match
|
|
||||||
# the previously hardcoded list: localhost, 127.0.0.1:8000 and api.honcho.dev.
|
|
||||||
# CORS_ORIGINS=["http://localhost","http://127.0.0.1:8000","https://api.honcho.dev"]
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Vector Store Settings
|
|
||||||
# =============================================================================
|
|
||||||
# Vector store type: "pgvector", "turbopuffer", or "lancedb"
|
|
||||||
VECTOR_STORE_TYPE=pgvector
|
|
||||||
|
|
||||||
# Migration flag: set to true when migration from pgvector is complete
|
|
||||||
VECTOR_STORE_MIGRATED=false
|
|
||||||
|
|
||||||
# Global namespace prefix for all vector namespaces
|
|
||||||
# Namespaces follow the pattern: {NAMESPACE}.{type}.{hash}
|
|
||||||
# where hash is a base64url-encoded SHA-256 of the workspace/peer names
|
|
||||||
# - Documents: {NAMESPACE}.doc.{hash(workspace, observer, observed)}
|
|
||||||
# - Messages: {NAMESPACE}.msg.{hash(workspace)}
|
|
||||||
# VECTOR_STORE_NAMESPACE=honcho # Inherits from NAMESPACE if not set
|
|
||||||
|
|
||||||
# Embedding dimensions are configured via EMBEDDING_VECTOR_DIMENSIONS (see top
|
|
||||||
# of this file). VECTOR_STORE_DIMENSIONS is deprecated and ignored.
|
|
||||||
|
|
||||||
# Turbopuffer-specific settings (required if TYPE is "turbopuffer")
|
|
||||||
# VECTOR_STORE_TURBOPUFFER_API_KEY=your-turbopuffer-api-key
|
|
||||||
# VECTOR_STORE_TURBOPUFFER_REGION=gcp-us-east4
|
|
||||||
|
|
||||||
# LanceDB-specific settings (local embedded mode)
|
|
||||||
# VECTOR_STORE_LANCEDB_PATH=./lancedb_data
|
|
||||||
|
|
||||||
# Reconciliation interval for background sync (default: 5 minutes)
|
|
||||||
# VECTOR_STORE_RECONCILIATION_INTERVAL_SECONDS=300
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# Shell entrypoints are executed with sh/dash inside the container image. A
|
|
||||||
# Windows checkout with core.autocrlf=true rewrites them to CRLF, and dash
|
|
||||||
# then aborts with "set: Illegal option" because the carriage return becomes
|
|
||||||
# part of the "-e" flag argument (docker/entrypoint.sh).
|
|
||||||
*.sh text eol=lf
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
# Code owners for Honcho.
|
|
||||||
#
|
|
||||||
# Beyond review routing, this file is the allowlist for manually triggering
|
|
||||||
# the live-llm-tests and unified-tests workflows (via their PR labels and
|
|
||||||
# workflow_dispatch). The gate jobs grep every @username in this file —
|
|
||||||
# regardless of which path pattern it sits on — and read it from `main`,
|
|
||||||
# never from the PR branch, so additions only take effect once merged.
|
|
||||||
#
|
|
||||||
# The workflow gates only understand individual @usernames (no @org/team
|
|
||||||
# entries).
|
|
||||||
#
|
|
||||||
# Order matters: GitHub applies the LAST matching pattern, so narrower rules
|
|
||||||
# go further down. Paths not listed here have no automatic reviewer.
|
|
||||||
|
|
||||||
# Telemetry, tracing, metrics.
|
|
||||||
/src/telemetry/ @akattelu @Rajat-Ahuja1997
|
|
||||||
|
|
||||||
# Data model, connections, configuration, LLM transport.
|
|
||||||
/src/db.py @akattelu @eisene
|
|
||||||
/src/models.py @akattelu @eisene
|
|
||||||
/src/config.py @akattelu @eisene
|
|
||||||
/src/cache/ @akattelu @eisene
|
|
||||||
/src/crud/ @akattelu @eisene
|
|
||||||
/migrations/ @akattelu @eisene
|
|
||||||
/src/llm/ @akattelu @eisene
|
|
||||||
|
|
||||||
# Client-facing surfaces and API shape.
|
|
||||||
/sdks/ @ajspig @akattelu
|
|
||||||
/mcp/ @ajspig @akattelu
|
|
||||||
/honcho-cli/ @ajspig @akattelu
|
|
||||||
/src/routers/ @ajspig @akattelu
|
|
||||||
/src/schemas/ @ajspig @akattelu
|
|
||||||
|
|
||||||
# The reasoning agents, their prompts, and shared agent tooling.
|
|
||||||
/src/deriver/ @eisene @akattelu
|
|
||||||
/src/dreamer/ @eisene @akattelu
|
|
||||||
/src/dialectic/ @eisene @akattelu
|
|
||||||
/src/utils/ @eisene @akattelu
|
|
||||||
|
|
||||||
# Deployment, and swappable storage and inference backends.
|
|
||||||
# /src/llm/backends/ must stay below /src/llm/ above — last match wins.
|
|
||||||
/docker/ @eisene @Rajat-Ahuja1997
|
|
||||||
/Dockerfile @eisene @Rajat-Ahuja1997
|
|
||||||
/docker-compose.yml.example @eisene @Rajat-Ahuja1997
|
|
||||||
/src/vector_store/ @eisene @Rajat-Ahuja1997
|
|
||||||
/src/llm/backends/ @eisene @Rajat-Ahuja1997
|
|
||||||
|
|
||||||
# Documentation and contributor-facing policy.
|
|
||||||
/docs/ @ajspig @akattelu
|
|
||||||
/README.md @ajspig @akattelu
|
|
||||||
/CONTRIBUTING.md @akattelu @ajspig
|
|
||||||
/SECURITY.md @Rajat-Ahuja1997 @ajspig
|
|
||||||
|
|
||||||
# Reviewers auto-requested on changes under .github/ (workflows, this file,
|
|
||||||
# templates).
|
|
||||||
/.github/ @akattelu @eisene @Rajat-Ahuja1997 @VVoruganti
|
|
||||||
|
|
||||||
# CI-trigger allowlist only: this path matches no real file, so these people
|
|
||||||
# are never auto-requested for review, but the workflow gates still pick
|
|
||||||
# them up.
|
|
||||||
/ci-trigger-allowlist @ajspig @courtlandleer @erosika @lowyelling @vintrocode
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
---
|
||||||
|
name: "🐞 Bug Report"
|
||||||
|
about: "Report an issue to help the project improve."
|
||||||
|
title: "[Bug] "
|
||||||
|
labels: "bug"
|
||||||
|
assignees: ""
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# **🐞 Bug Report**
|
||||||
|
|
||||||
|
## **Describe the bug**
|
||||||
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Is this a regression?**
|
||||||
|
<!-- Did this behaviour used to work in the previous version? -->
|
||||||
|
<!-- Yes, the last version in which this bug was not present was: ... -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **To Reproduce**
|
||||||
|
|
||||||
|
<!-- Steps to reproduce the error:
|
||||||
|
(e.g.:)
|
||||||
|
1. Use x argument / navigate to
|
||||||
|
2. Fill this information
|
||||||
|
3. Go to...
|
||||||
|
4. See error -->
|
||||||
|
|
||||||
|
<!-- Write the steps here (add or remove as many steps as needed)-->
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
4.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Expected behaviour**
|
||||||
|
<!-- A clear and concise description of what you expected to happen. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Media prove**
|
||||||
|
<!-- If applicable, add screenshots or videos to help explain your problem. -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Your environment**
|
||||||
|
|
||||||
|
<!-- use all the applicable bulleted list elements for this specific issue,
|
||||||
|
and remove all the bulleted list elements that are not relevant for this issue. -->
|
||||||
|
|
||||||
|
* OS: <!--[e.g. Ubuntu 5.4.0-26-generic x86_64 / Windows 1904 ...]-->
|
||||||
|
* Browser name and version:
|
||||||
|
* Honcho Server Version: <!-- e.g. v0.0.8 -->
|
||||||
|
* Honcho Client Version: <!-- e.g. Python v0.0.8 -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Additional context**
|
||||||
|
<!-- Add any other context or additional information about the problem here.-->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||||
|
|
||||||
|
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||||
|
|
||||||
|
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
name: Bug report
|
|
||||||
description: Something is broken or incorrect in Honcho (API, deriver, SDK, managed offering, etc.).
|
|
||||||
title: "[Bug] "
|
|
||||||
labels: ["bug"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for filing a bug. Please search [existing issues](https://github.com/plastic-labs/honcho/issues) first.
|
|
||||||
|
|
||||||
**Security vulnerability?** Do not use this form — report privately via [SECURITY.md](https://github.com/plastic-labs/honcho/blob/main/SECURITY.md).
|
|
||||||
|
|
||||||
**Memory / recall quality** (wrong or noisy conclusions, weak dialectic answers) with no crash? Prefer the **Memory / recall quality** template.
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: deploy_mode
|
|
||||||
attributes:
|
|
||||||
label: Deploy mode
|
|
||||||
description: Where are you running Honcho?
|
|
||||||
options:
|
|
||||||
- Managed (api.honcho.dev / app.honcho.dev)
|
|
||||||
- Self-hosted
|
|
||||||
- Unsure
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: version
|
|
||||||
attributes:
|
|
||||||
label: Honcho version
|
|
||||||
description: Server image tag or release, and SDK version if you use one. Write "managed" if you are not self-hosting.
|
|
||||||
placeholder: e.g. server v2.4.1, honcho-ai 2.1.0
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Describe the bug
|
|
||||||
description: Clear and concise description of what is wrong.
|
|
||||||
placeholder: When I…, Honcho…
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: repro
|
|
||||||
attributes:
|
|
||||||
label: Steps to reproduce
|
|
||||||
description: Minimal steps or a short script/API sequence. Redact secrets, JWTs, and production user content.
|
|
||||||
placeholder: |
|
|
||||||
1. Create a session with …
|
|
||||||
2. POST /v3/... with body …
|
|
||||||
3. Observe …
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: logs
|
|
||||||
attributes:
|
|
||||||
label: Logs and evidence
|
|
||||||
description: Relevant API or deriver logs or stack traces. Redact secrets and user content.
|
|
||||||
render: shell
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: context
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Config knobs, deployment notes, screenshots, related issues/PRs.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
name: "💉 Failing Test"
|
||||||
|
about: "Report failing tests or CI jobs."
|
||||||
|
title: "[Test] "
|
||||||
|
labels: "Type: Test"
|
||||||
|
assignees: ""
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# **💉 Failing Test**
|
||||||
|
|
||||||
|
## **Which jobs/test(s) are failing**
|
||||||
|
<!-- The CI jobs or tests that are failing -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Reason for failure/description**
|
||||||
|
<!-- Try to describe why the test is failing or what we are missing to make it pass. -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Media prove**
|
||||||
|
<!-- If applicable, add screenshots or videos to help explain your problem. -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Additional context**
|
||||||
|
<!-- Add any other context or additional information about the problem here. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||||
|
|
||||||
|
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||||
|
|
||||||
|
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
name: Memory / recall quality
|
|
||||||
description: Conclusions, representations, or dialectic answers are wrong, noisy, missing, or low-quality — not a hard crash.
|
|
||||||
title: "[Quality] "
|
|
||||||
labels: ["quality"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Use this when Honcho runs without erroring, but **memory formation or recall quality** is off (bad conclusions, missed facts, weak chat answers, polluted representations, etc.).
|
|
||||||
|
|
||||||
For crashes, 5xxs, auth failures, or incorrect API mechanics, use the **Bug report** template instead.
|
|
||||||
|
|
||||||
**Do not paste production user content, full peer representations, or secrets.** Redact or invent a minimal synthetic example.
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: deploy_mode
|
|
||||||
attributes:
|
|
||||||
label: Deploy mode
|
|
||||||
options:
|
|
||||||
- Managed (api.honcho.dev / app.honcho.dev)
|
|
||||||
- Self-hosted
|
|
||||||
- Unsure
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: version
|
|
||||||
attributes:
|
|
||||||
label: Honcho version
|
|
||||||
description: Server image tag or release, and SDK version if you use one. Write "managed" if you are not self-hosting.
|
|
||||||
placeholder: e.g. server v2.4.1, honcho-ai 2.1.0
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: What is wrong with the quality?
|
|
||||||
description: Describe the failure mode (noise, omission, contradiction, staleness, over/under-generalization, etc.).
|
|
||||||
placeholder: After ingesting messages about X, Honcho concludes Y / chat answers Z…
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: repro
|
|
||||||
attributes:
|
|
||||||
label: Minimal scenario
|
|
||||||
description: >
|
|
||||||
Smallest synthetic message sequence or setup that triggers the issue.
|
|
||||||
Prefer invented names/facts over real user data. Include observer/observed
|
|
||||||
peer setup if relevant (self vs cross-peer).
|
|
||||||
placeholder: |
|
|
||||||
1. Peers: alice (user), bot (agent); session S
|
|
||||||
2. Messages ingested: …
|
|
||||||
3. Query / conclusion listing shows: …
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: config
|
|
||||||
attributes:
|
|
||||||
label: Relevant config
|
|
||||||
description: >
|
|
||||||
Custom instructions, provider/model, deriver/dream settings, or workspace/peer
|
|
||||||
config that affects reasoning. Redact secrets.
|
|
||||||
placeholder: |
|
|
||||||
Provider/model: …
|
|
||||||
Custom instructions: (summary or redacted)
|
|
||||||
Other: …
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: evidence
|
|
||||||
attributes:
|
|
||||||
label: Evidence
|
|
||||||
description: Redacted conclusion text, chat excerpts, or counts that show the failure. No production PII.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: context
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Frequency, scale (message/conclusion counts), related issues, workarounds.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
@ -8,10 +8,6 @@ assignees: ""
|
||||||
---
|
---
|
||||||
# **📚 Documentation Issue Report**
|
# **📚 Documentation Issue Report**
|
||||||
|
|
||||||
**Security vulnerability?** Do not use this form — report privately via [SECURITY.md](https://github.com/plastic-labs/honcho/blob/main/SECURITY.md).
|
|
||||||
|
|
||||||
GitHub issues are public. Redact secrets, JWTs, and production user content.
|
|
||||||
|
|
||||||
## **Describe the bug**
|
## **Describe the bug**
|
||||||
<!-- A clear and concise description of what the bug is. -->
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
|
|
||||||
|
|
@ -37,8 +33,8 @@ GitHub issues are public. Redact secrets, JWTs, and production user content.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **Screenshots and videos**
|
### **Media prove**
|
||||||
<!-- If applicable, add screenshots or videos to help explain your problem. Redact secrets and production content. -->
|
<!-- If applicable, add screenshots or videos to help explain your problem. -->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -50,7 +46,7 @@ GitHub issues are public. Redact secrets, JWTs, and production user content.
|
||||||
---
|
---
|
||||||
|
|
||||||
### **Additional context**
|
### **Additional context**
|
||||||
<!-- Add any other context about the problem. Redact secrets and production user content. -->
|
<!-- Add any other context or additional information about the problem here.-->
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
name: Feature request
|
|
||||||
description: Propose a new capability or an improvement to an existing one.
|
|
||||||
title: "[Feature] "
|
|
||||||
labels: ["enhancement"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Tell us what problem you are trying to solve. Concrete use cases beat abstract wishlists.
|
|
||||||
|
|
||||||
Questions about how to use Honcho belong on [Discord](https://discord.gg/honcho), not here.
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: request_type
|
|
||||||
attributes:
|
|
||||||
label: Request type
|
|
||||||
options:
|
|
||||||
- New capability
|
|
||||||
- Improve an existing capability
|
|
||||||
- API / SDK surface
|
|
||||||
- Managed offering
|
|
||||||
- Docs / DX
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: problem
|
|
||||||
attributes:
|
|
||||||
label: Problem
|
|
||||||
description: What is hard or impossible today? Who hits this?
|
|
||||||
placeholder: I'm always frustrated when… / My integration needs…
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: solution
|
|
||||||
attributes:
|
|
||||||
label: Proposed solution
|
|
||||||
description: What you would like Honcho to support. Sketches and API shapes welcome.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: alternatives
|
|
||||||
attributes:
|
|
||||||
label: Alternatives considered
|
|
||||||
description: Workarounds, other APIs, or designs you already tried or ruled out.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: context
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Links, prior art, screenshots, related issues/PRs.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
name: "🚀🆕 Feature Request"
|
||||||
|
about: "Suggest an idea or possible new feature for this project."
|
||||||
|
title: ""
|
||||||
|
labels: 'feature'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# **🚀 Feature Request**
|
||||||
|
|
||||||
|
## **Is your feature request related to a problem? Please describe.**
|
||||||
|
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Describe the solution you'd like**
|
||||||
|
<!-- A clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Describe alternatives you've considered**
|
||||||
|
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Additional context**
|
||||||
|
<!-- Add any other context or additional information about the problem here.-->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||||
|
|
||||||
|
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||||
|
|
||||||
|
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
name: Integration request
|
|
||||||
description: Add Honcho to an app store, agent framework, plugin marketplace, or other third-party surface — or improve an existing integration.
|
|
||||||
title: "[Integration] "
|
|
||||||
labels: ["integration"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Use this when you want Honcho available in (or better supported by) an external product surface — app stores, agent frameworks, plugin marketplaces, IDE extensions, MCP clients, etc.
|
|
||||||
|
|
||||||
For core API/SDK product features that are not about a third-party surface, use the **Feature request** template instead.
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: request_kind
|
|
||||||
attributes:
|
|
||||||
label: What kind of request is this?
|
|
||||||
options:
|
|
||||||
- New integration / listing (Honcho is not there yet)
|
|
||||||
- Improve an existing integration
|
|
||||||
- Official plugin / extension
|
|
||||||
- Marketplace or app-store listing
|
|
||||||
- Docs / guide for integrating with a specific tool
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: target
|
|
||||||
attributes:
|
|
||||||
label: Target product or platform
|
|
||||||
description: Name of the app, framework, marketplace, or tool.
|
|
||||||
placeholder: e.g. Claude Code, Cursor, CrewAI, OpenClaw, VS Code Marketplace…
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: target_url
|
|
||||||
attributes:
|
|
||||||
label: Link (if any)
|
|
||||||
description: Docs, marketplace page, repo, or product URL.
|
|
||||||
placeholder: https://…
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: why
|
|
||||||
attributes:
|
|
||||||
label: Why does this matter?
|
|
||||||
description: Who would use it, and what does the integration unlock?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: shape
|
|
||||||
attributes:
|
|
||||||
label: What should the integration look like?
|
|
||||||
description: >
|
|
||||||
e.g. one-click install, MCP server listing, native memory backend,
|
|
||||||
SDK recipe, plugin with slash commands, env-var setup, etc.
|
|
||||||
Link to prior art or a sketch if you have one.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: context
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Related issues, community demand, constraints, offers to help build it.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
name: "🚀➕ Enhancement Request"
|
||||||
|
about: "Suggest an enhancement for this project. Improve an existing feature"
|
||||||
|
title: ""
|
||||||
|
labels: "Type: Enhancement"
|
||||||
|
assignees: ""
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# **🚀 Enhancement Request**
|
||||||
|
|
||||||
|
## **Is your enhancement request related to a problem? Please describe.**
|
||||||
|
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Describe the solution you'd like**
|
||||||
|
<!-- A clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Describe alternatives you've considered**
|
||||||
|
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Additional context**
|
||||||
|
<!-- Add any other context or additional information about the problem here.-->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||||
|
|
||||||
|
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||||
|
|
||||||
|
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
---
|
||||||
|
name: "⚠️ Security Report"
|
||||||
|
about: "Report an issue to help the project improve."
|
||||||
|
title: ""
|
||||||
|
labels: "security"
|
||||||
|
assignees: ""
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||||
|
|
||||||
|
READ CAREFULLY IF YOUR ISSUE REPORT CONTAINS SENSIBLE OR PRIVATE DATA:
|
||||||
|
(data that might be leaked or subtracted from our servers due to this
|
||||||
|
security issue).
|
||||||
|
|
||||||
|
If this security report (or the guide on how to "identify the security bug") includes
|
||||||
|
certain personal information or involves personal identifiable data, or you believe
|
||||||
|
that the data that you might leak by exposing the way on how to attack the project
|
||||||
|
could be considered as a data leak or could violate the privacy of any kind of
|
||||||
|
data or sensible data, please do not post it here and directly email the developer:
|
||||||
|
(hello@plasticlabs.ai). You should post the issue with the least amount of
|
||||||
|
sensible or private data as possible to help us manage the security issue, and
|
||||||
|
with the extra data sent from your email to the developer (if any), we will deeply
|
||||||
|
analyze and try to fix it as fast as possible.
|
||||||
|
|
||||||
|
If you are in doubt about the data that you might post here (screenshots or media
|
||||||
|
also, count as data), please directly email us.
|
||||||
|
|
||||||
|
The data that must NOT be posted here:
|
||||||
|
|
||||||
|
* Legal and/or full names
|
||||||
|
* Names or usernames combined with other identifiers like phone numbers or email addresses
|
||||||
|
* Health or financial information (including insurance information, social security numbers, etc.)
|
||||||
|
* Information about political or religious affiliations
|
||||||
|
* Information about race, ethnicity, sexual orientation, gender, or other identifying information that could be used for discriminatory purposes
|
||||||
|
|
||||||
|
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||||
|
|
||||||
|
# **⚠️ Security Report**
|
||||||
|
|
||||||
|
## **Describe the security issue**
|
||||||
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **To Reproduce**
|
||||||
|
|
||||||
|
<!-- Steps to reproduce the error:
|
||||||
|
(e.g.:)
|
||||||
|
1. Use x argument / navigate to
|
||||||
|
2. Fill this information
|
||||||
|
3. Go to...
|
||||||
|
4. See error -->
|
||||||
|
|
||||||
|
<!-- Write the steps here (add or remove as many steps as needed)-->
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
4.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Expected behaviour**
|
||||||
|
<!-- A clear and concise description of what you expected to happen. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Media prove**
|
||||||
|
<!-- If applicable, add screenshots or videos to help explain your problem. -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Your environment**
|
||||||
|
|
||||||
|
<!-- use all the applicable bulleted list elements for this specific issue,
|
||||||
|
and remove all the bulleted list elements that are not relevant for this issue. -->
|
||||||
|
|
||||||
|
* OS: <!--[e.g. Ubuntu 5.4.0-26-generic x86_64 / Windows 1904 ...]-->
|
||||||
|
* Browser name and version:
|
||||||
|
* Honcho Server Version: <!--[e.g. v0.0.1]-->
|
||||||
|
* Honcho Client Version: <!--[e.g. Python v0.0.1]-->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Additional context**
|
||||||
|
<!-- Add any other context or additional information about the problem here.-->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
name: "❓ Question or Support Request"
|
||||||
|
about: "Questions and requests for support."
|
||||||
|
title: ""
|
||||||
|
labels: "question"
|
||||||
|
assignees: ""
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# **❓ Question or Support Request**
|
||||||
|
|
||||||
|
## **Describe your question or ask for support.**
|
||||||
|
<!-- A clear and concise description of what your doubt is. -->
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||||
|
|
||||||
|
Before posting any questions or asking for support, first read the project's README.md file and
|
||||||
|
(if there is any) the WIKI pages or any other additional documentation that might be listed
|
||||||
|
in the project's README.md file.
|
||||||
|
|
||||||
|
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||||
|
|
||||||
|
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Report a security vulnerability
|
|
||||||
url: https://github.com/plastic-labs/honcho/security/advisories/new
|
|
||||||
about: Private vulnerability reporting only — do not file public security issues.
|
|
||||||
- name: Question or support
|
|
||||||
url: https://discord.gg/honcho
|
|
||||||
about: Ask the community and maintainers on Discord.
|
|
||||||
- name: Documentation
|
|
||||||
url: https://honcho.dev/docs
|
|
||||||
about: Guides, API reference, and self-hosting docs.
|
|
||||||
|
|
@ -31,7 +31,7 @@ For more information on closing issues using keywords, please check https://docs
|
||||||
## **Changelog**
|
## **Changelog**
|
||||||
|
|
||||||
<!-- 📛📛📛📛
|
<!-- 📛📛📛📛
|
||||||
Log of changes introduced in this release in the style of https://keepachangelog.com/en/1.1.0/
|
Log of changes introduced in this release in the style fo https://keepachangelog.com/en/1.1.0/
|
||||||
📛📛📛📛 -->
|
📛📛📛📛 -->
|
||||||
|
|
||||||
### **Added**
|
### **Added**
|
||||||
|
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
name: Load staging secrets
|
|
||||||
description: >-
|
|
||||||
Resolve staging secret ids from the two newest v<major.minor.patch> git tags,
|
|
||||||
then load the newest fetchable secret's keys into the job environment from
|
|
||||||
AWS Secrets Manager. Falls back to the second-latest tag when the latest
|
|
||||||
tag's secret isn't published yet, and fails the job loudly when neither can
|
|
||||||
be fetched. Requires the repository to be checked out and AWS credentials to
|
|
||||||
be configured beforehand.
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
secret-prefix:
|
|
||||||
description: >-
|
|
||||||
Secret-name prefix combined with a resolved tag version to form the full
|
|
||||||
secret id. Masked so it stays out of public CI logs.
|
|
||||||
required: true
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- name: Resolve secret ids from latest git tags
|
|
||||||
id: resolve-secret
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
SECRET_PREFIX: ${{ inputs.secret-prefix }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
: "${SECRET_PREFIX:?secret-prefix input is empty — is the STAGING_SECRET_PREFIX secret set for this environment?}"
|
|
||||||
# Keep the secret-name prefix out of public CI logs.
|
|
||||||
echo "::add-mask::${SECRET_PREFIX}"
|
|
||||||
|
|
||||||
# Two newest v<major.minor.patch> tags, highest first (tags are public).
|
|
||||||
versions="$(git ls-remote --tags origin 'v*' \
|
|
||||||
| sed -n 's#.*refs/tags/v\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)$#\1#p' \
|
|
||||||
| sort -t. -k1,1nr -k2,2nr -k3,3nr -u)"
|
|
||||||
latest="$(printf '%s\n' "$versions" | sed -n '1p')"
|
|
||||||
second="$(printf '%s\n' "$versions" | sed -n '2p')"
|
|
||||||
if [ -z "${latest:-}" ]; then
|
|
||||||
echo "::error::No v<semver> git tags found to resolve a secret version"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
latest_id="${SECRET_PREFIX}${latest}"
|
|
||||||
echo "::add-mask::${latest_id}"
|
|
||||||
echo "latest-id=${latest_id}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Latest version: ${latest}"
|
|
||||||
if [ -n "${second:-}" ]; then
|
|
||||||
second_id="${SECRET_PREFIX}${second}"
|
|
||||||
echo "::add-mask::${second_id}"
|
|
||||||
echo "second-id=${second_id}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Fallback version: ${second}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fetch the latest tag's secret. continue-on-error so a not-yet-published
|
|
||||||
# latest falls through to the second-latest instead of failing the job.
|
|
||||||
- name: Fetch staging secret (latest)
|
|
||||||
id: fetch-latest
|
|
||||||
continue-on-error: true
|
|
||||||
uses: aws-actions/aws-secretsmanager-get-secrets@v2
|
|
||||||
with:
|
|
||||||
secret-ids: |
|
|
||||||
,${{ steps.resolve-secret.outputs.latest-id }}
|
|
||||||
parse-json-secrets: true
|
|
||||||
|
|
||||||
# Runs only if the latest fetch failed; this one is NOT continue-on-error,
|
|
||||||
# so if the fallback also fails the job fails loudly.
|
|
||||||
- name: Fetch staging secret (fallback to second-latest)
|
|
||||||
id: fetch-fallback
|
|
||||||
if: steps.fetch-latest.outcome == 'failure' && steps.resolve-secret.outputs.second-id != ''
|
|
||||||
uses: aws-actions/aws-secretsmanager-get-secrets@v2
|
|
||||||
with:
|
|
||||||
secret-ids: |
|
|
||||||
,${{ steps.resolve-secret.outputs.second-id }}
|
|
||||||
parse-json-secrets: true
|
|
||||||
|
|
||||||
# If the latest fetch failed and the fallback was skipped (no second tag),
|
|
||||||
# no secret keys were loaded — fail here instead of letting the job run
|
|
||||||
# without staging config (e.g. every live LLM test would silently skip via
|
|
||||||
# require_provider_key and the run would go green).
|
|
||||||
- name: Verify staging secrets were loaded
|
|
||||||
if: steps.fetch-latest.outcome != 'success' && steps.fetch-fallback.outcome != 'success'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "::error::No staging secret could be fetched (latest failed; fallback skipped or failed)"
|
|
||||||
exit 1
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- 2-3 sentences about what problem this PR solves and how -->
|
|
||||||
|
|
||||||
## Proofs
|
|
||||||
|
|
||||||
<!-- Add screenshots, logs, files as a proof that this change works -->
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
- [ ] This PR is correlated to an existing issue, and I understand it will be closed if that issue does not have the `maintainer-approved` label.
|
|
||||||
|
|
||||||
<!-- Fixes #XXX -->
|
|
||||||
|
|
@ -1,286 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issue gate — shared logic for `.github/workflows/issue-gate.yml` (immediate
|
|
||||||
* feedback on pull request events) and `.github/workflows/pr-sweeper.yml`
|
|
||||||
* (deferred re-check, close, and stale-draft cleanup).
|
|
||||||
*
|
|
||||||
* Both workflows `require` this file through actions/github-script, so it must
|
|
||||||
* stay dependency-free: neither job runs an install step.
|
|
||||||
*
|
|
||||||
* See CONTRIBUTING.md for the policy this enforces.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const REQUIRED_LABEL = 'maintainer-approved';
|
|
||||||
const GATE_LABEL = 'needs-approved-issue';
|
|
||||||
const EXEMPT_LABEL = 'gate-exempt';
|
|
||||||
const MARKER = '<!-- issue-gate -->';
|
|
||||||
const DISCORD = 'http://discord.gg/honcho';
|
|
||||||
|
|
||||||
// Hours a labelled pull request has before the sweeper closes it. Measured from
|
|
||||||
// the notice comment, so the clock starts when the author was actually told —
|
|
||||||
// not when the pull request was opened.
|
|
||||||
const GRACE_HOURS = 72;
|
|
||||||
|
|
||||||
// Days without activity before a draft from outside the org is closed.
|
|
||||||
const DRAFT_STALE_DAYS = 30;
|
|
||||||
|
|
||||||
const hasLabel = (pr, name) => (pr.labels || []).some((l) => l.name === name);
|
|
||||||
|
|
||||||
const isBot = (account) => Boolean(account) && account.type === 'Bot';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Why this pull request is exempt from the gate, or null if it is not.
|
|
||||||
*
|
|
||||||
* Single source of truth: every caller that acts on a pull request runs this.
|
|
||||||
*/
|
|
||||||
const exemptReason = async ({ github, owner, repo, pr }) => {
|
|
||||||
if (isBot(pr.user)) return 'author is a bot';
|
|
||||||
if (hasLabel(pr, EXEMPT_LABEL)) return `carries the ${EXEMPT_LABEL} label`;
|
|
||||||
|
|
||||||
const username = pr.user && pr.user.login;
|
|
||||||
if (!username) return null;
|
|
||||||
|
|
||||||
const permission = await repoPermission({ github, owner, repo, username });
|
|
||||||
if (WRITE_PERMISSIONS.includes(permission)) {
|
|
||||||
return `author has ${permission} permission`;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Repo roles that skip the gate. `read` / `triage` do not.
|
|
||||||
const WRITE_PERMISSIONS = ['admin', 'maintain', 'write'];
|
|
||||||
|
|
||||||
/** Highest repo permission for `username`, or null if they are not a collaborator. */
|
|
||||||
async function repoPermission({ github, owner, repo, username }) {
|
|
||||||
try {
|
|
||||||
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
|
|
||||||
owner, repo, username,
|
|
||||||
});
|
|
||||||
return data.permission;
|
|
||||||
} catch (err) {
|
|
||||||
if (err && err.status === 404) return null;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const CLOSING_ISSUES = `
|
|
||||||
query($owner: String!, $repo: String!, $number: Int!) {
|
|
||||||
repository(owner: $owner, name: $repo) {
|
|
||||||
pullRequest(number: $number) {
|
|
||||||
closingIssuesReferences(first: 20) {
|
|
||||||
nodes {
|
|
||||||
number
|
|
||||||
state
|
|
||||||
labels(first: 50) { nodes { name } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decide whether a pull request clears the gate.
|
|
||||||
*
|
|
||||||
* Reads GitHub's own resolved issue links rather than parsing the body, so both
|
|
||||||
* `Fixes #123` and the sidebar "Development" link count. A bare `#123` mention
|
|
||||||
* deliberately does not — that is a reference, not a claim to close.
|
|
||||||
*
|
|
||||||
* @returns {Promise<{passed: boolean, skipped?: string, issue?: number, reason?: string}>}
|
|
||||||
*/
|
|
||||||
async function checkGate({ github, owner, repo, pr }) {
|
|
||||||
if (pr.state !== 'open') return { passed: true, skipped: 'pull request is not open' };
|
|
||||||
if (pr.draft) return { passed: true, skipped: 'pull request is a draft' };
|
|
||||||
const exempt = await exemptReason({ github, owner, repo, pr });
|
|
||||||
if (exempt) return { passed: true, skipped: exempt };
|
|
||||||
|
|
||||||
const data = await github.graphql(CLOSING_ISSUES, { owner, repo, number: pr.number });
|
|
||||||
const issues = data.repository.pullRequest.closingIssuesReferences.nodes;
|
|
||||||
|
|
||||||
if (issues.length === 0) {
|
|
||||||
return { passed: false, reason: 'This pull request is not linked to an issue.' };
|
|
||||||
}
|
|
||||||
|
|
||||||
const approved = issues.find(
|
|
||||||
(i) => i.state === 'OPEN' && i.labels.nodes.some((l) => l.name === REQUIRED_LABEL),
|
|
||||||
);
|
|
||||||
if (approved) return { passed: true, issue: approved.number };
|
|
||||||
|
|
||||||
const detail = issues
|
|
||||||
.map((i) => `#${i.number} (${i.state === 'CLOSED' ? 'closed' : 'not approved'})`)
|
|
||||||
.join(', ');
|
|
||||||
return {
|
|
||||||
passed: false,
|
|
||||||
reason:
|
|
||||||
`The linked ${issues.length === 1 ? 'issue is' : 'issues are'} not open with the ` +
|
|
||||||
`\`${REQUIRED_LABEL}\` label: ${detail}.`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function noticeBody({ owner, repo, reason }) {
|
|
||||||
return [
|
|
||||||
MARKER,
|
|
||||||
'Thanks for the contribution. This pull request does not clear our issue gate yet.',
|
|
||||||
'',
|
|
||||||
`**${reason}**`,
|
|
||||||
'',
|
|
||||||
`Every pull request to Honcho needs to be linked to an open issue carrying the \`${REQUIRED_LABEL}\` label. We do this so the review queue only holds work we have already agreed should be built — it means nobody spends time on a change we cannot merge.`,
|
|
||||||
'',
|
|
||||||
'To get this moving:',
|
|
||||||
'',
|
|
||||||
`1. Find or open an issue describing the change. [Approved issues are here](https://github.com/${owner}/${repo}/issues?q=is%3Aissue+is%3Aopen+label%3A${REQUIRED_LABEL}).`,
|
|
||||||
`2. Make the case for it in [Discord](${DISCORD}) — maintainers are most active there, and it is by far the fastest route to a decision.`,
|
|
||||||
`3. Once the issue has the label, link it: put \`Fixes #<number>\` in this pull request's description, or use **Development** in the sidebar.`,
|
|
||||||
'',
|
|
||||||
`**This will close automatically in ${GRACE_HOURS} hours if it is still unlinked.** Nothing is lost if that happens — link the issue, reopen, and it goes into the review queue.`,
|
|
||||||
'',
|
|
||||||
`See [CONTRIBUTING.md](https://github.com/${owner}/${repo}/blob/main/CONTRIBUTING.md) for the full process. If you think this is wrong, say so here and a maintainer will take a look.`,
|
|
||||||
].join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Every gate notice this bot posted on a pull request, oldest first.
|
|
||||||
*
|
|
||||||
* Authorship is part of the test, not decoration. MARKER is an invisible HTML
|
|
||||||
* comment, so anyone who can comment on a public repository can paste it. If
|
|
||||||
* user comments counted, a third party could post one on someone else's pull
|
|
||||||
* request: `runGate` posts a notice only when none exists, so the author would
|
|
||||||
* never be told, and `runSweep` would then measure the grace window from the
|
|
||||||
* stranger's timestamp and close them unwarned.
|
|
||||||
*/
|
|
||||||
async function findNotices({ github, owner, repo, number }) {
|
|
||||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
||||||
owner, repo, issue_number: number, per_page: 100,
|
|
||||||
});
|
|
||||||
return comments.filter((c) => isBot(c.user) && (c.body || '').includes(MARKER));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Drop the gate label and delete the notice.
|
|
||||||
*
|
|
||||||
* Deleting matters: `runGate` posts a notice only when none exists, and the
|
|
||||||
* sweeper measures grace from the notice timestamp. A notice left behind after
|
|
||||||
* the gate clears would make a later re-block look weeks old and be closed with
|
|
||||||
* no warning.
|
|
||||||
*/
|
|
||||||
async function clearGate({ github, owner, repo, pr }) {
|
|
||||||
if (hasLabel(pr, GATE_LABEL)) {
|
|
||||||
await github.rest.issues
|
|
||||||
.removeLabel({ owner, repo, issue_number: pr.number, name: GATE_LABEL })
|
|
||||||
.catch(() => {});
|
|
||||||
}
|
|
||||||
for (const notice of await findNotices({ github, owner, repo, number: pr.number })) {
|
|
||||||
await github.rest.issues
|
|
||||||
.deleteComment({ owner, repo, comment_id: notice.id })
|
|
||||||
.catch(() => {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Entry point for `.github/workflows/issue-gate.yml`.
|
|
||||||
* Labels and explains. Never closes — that is the sweeper's job.
|
|
||||||
*/
|
|
||||||
async function runGate({ github, core, context }) {
|
|
||||||
const pr = context.payload.pull_request;
|
|
||||||
const { owner, repo } = context.repo;
|
|
||||||
const result = await checkGate({ github, owner, repo, pr });
|
|
||||||
|
|
||||||
if (result.passed) {
|
|
||||||
core.info(
|
|
||||||
result.skipped ? `Skipping gate: ${result.skipped}` : `Gate passed via #${result.issue}`,
|
|
||||||
);
|
|
||||||
await clearGate({ github, owner, repo, pr });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
core.warning(`Gate failed: ${result.reason}`);
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner, repo, issue_number: pr.number, labels: [GATE_LABEL],
|
|
||||||
});
|
|
||||||
|
|
||||||
const notices = await findNotices({ github, owner, repo, number: pr.number });
|
|
||||||
if (notices.length > 0) return;
|
|
||||||
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner, repo, issue_number: pr.number,
|
|
||||||
body: noticeBody({ owner, repo, reason: result.reason }),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Entry point for `.github/workflows/pr-sweeper.yml`. */
|
|
||||||
async function runSweep({ github, core, context, dryRun }) {
|
|
||||||
const { owner, repo } = context.repo;
|
|
||||||
|
|
||||||
const act = async (what, fn) => {
|
|
||||||
core.info(dryRun ? `[dry run] ${what}` : what);
|
|
||||||
if (!dryRun) await fn();
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = (pr, body) => async () => {
|
|
||||||
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body });
|
|
||||||
await github.rest.pulls.update({ owner, repo, pull_number: pr.number, state: 'closed' });
|
|
||||||
};
|
|
||||||
|
|
||||||
const prs = await github.paginate(github.rest.pulls.list, {
|
|
||||||
owner, repo, state: 'open', per_page: 100,
|
|
||||||
});
|
|
||||||
core.info(`${prs.length} open pull requests${dryRun ? ' (dry run)' : ''}`);
|
|
||||||
|
|
||||||
// Re-check everything wearing the gate label. Never close blind: a pull request
|
|
||||||
// linked through the sidebar fires no webhook, so the gate workflow cannot have
|
|
||||||
// noticed it — this pass is the only thing that will.
|
|
||||||
for (const pr of prs.filter((p) => hasLabel(p, GATE_LABEL))) {
|
|
||||||
const result = await checkGate({ github, owner, repo, pr });
|
|
||||||
|
|
||||||
if (result.passed) {
|
|
||||||
const why = result.skipped || `via #${result.issue}`;
|
|
||||||
await act(`#${pr.number}: gate now clear (${why})`, async () => {
|
|
||||||
await clearGate({ github, owner, repo, pr });
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner, repo, issue_number: pr.number,
|
|
||||||
body: 'The issue link is in place — this pull request has cleared the gate and is waiting on review.',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [notice] = await findNotices({ github, owner, repo, number: pr.number });
|
|
||||||
if (!notice) {
|
|
||||||
core.info(`#${pr.number}: labelled but never notified — leaving it for the gate workflow`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hours = (Date.now() - Date.parse(notice.created_at)) / 3_600_000;
|
|
||||||
if (hours < GRACE_HOURS) {
|
|
||||||
core.info(`#${pr.number}: ${Math.round(GRACE_HOURS - hours)}h of grace left`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
await act(`#${pr.number}: closing — notified ${Math.round(hours)}h ago, still failing`, close(pr,
|
|
||||||
`Closing this: ${GRACE_HOURS} hours have passed and the gate is still not clear. This is not a judgement on the code. Link an approved issue and reopen — it goes straight into the review queue.`,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stale drafts. The gate skips drafts entirely, so they never carry the label;
|
|
||||||
// this pass keys off inactivity and applies the shared exemptions itself.
|
|
||||||
for (const pr of prs.filter((p) => p.draft)) {
|
|
||||||
const exempt = await exemptReason({ github, owner, repo, pr });
|
|
||||||
if (exempt) {
|
|
||||||
core.info(`#${pr.number}: leaving stale draft alone — ${exempt}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const days = (Date.now() - Date.parse(pr.updated_at)) / 86_400_000;
|
|
||||||
if (days < DRAFT_STALE_DAYS) continue;
|
|
||||||
|
|
||||||
await act(`#${pr.number}: closing stale draft — ${Math.round(days)}d without activity`, close(pr,
|
|
||||||
`Closing this draft after ${DRAFT_STALE_DAYS} days without activity, to keep the pull request list readable. Reopen whenever you pick it back up — nothing here is lost.`,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
checkGate, runGate, runSweep, noticeBody, findNotices, exemptReason,
|
|
||||||
REQUIRED_LABEL, GATE_LABEL, EXEMPT_LABEL, MARKER, GRACE_HOURS, DRAFT_STALE_DAYS,
|
|
||||||
};
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// Self-check for the gate decision logic. No framework, no install:
|
|
||||||
// node .github/scripts/issue-gate.test.js
|
|
||||||
// Covers checkGate() only — the side-effecting halves (runGate/runSweep) are
|
|
||||||
// exercised against the real API via `pr-sweeper.yml`'s dry_run dispatch.
|
|
||||||
|
|
||||||
const assert = require('node:assert');
|
|
||||||
const {
|
|
||||||
checkGate, findNotices, runSweep, REQUIRED_LABEL, EXEMPT_LABEL, MARKER,
|
|
||||||
} = require('./issue-gate.js');
|
|
||||||
|
|
||||||
const pull = (over = {}) => ({
|
|
||||||
number: 1, state: 'open', draft: false,
|
|
||||||
user: { type: 'User', login: 'alice' }, labels: [],
|
|
||||||
...over,
|
|
||||||
});
|
|
||||||
|
|
||||||
const notCollaborator = () => {
|
|
||||||
const err = new Error('Not Found');
|
|
||||||
err.status = 404;
|
|
||||||
throw err;
|
|
||||||
};
|
|
||||||
|
|
||||||
// `linked` is the list of issues GitHub resolves as closing references.
|
|
||||||
const stub = (linked, permission) => ({
|
|
||||||
graphql: async () => ({
|
|
||||||
repository: { pullRequest: { closingIssuesReferences: {
|
|
||||||
nodes: linked.map((i) => ({
|
|
||||||
number: i.number, state: i.state || 'OPEN',
|
|
||||||
labels: { nodes: (i.labels || []).map((name) => ({ name })) },
|
|
||||||
})),
|
|
||||||
} } },
|
|
||||||
}),
|
|
||||||
rest: {
|
|
||||||
repos: {
|
|
||||||
getCollaboratorPermissionLevel: async () => {
|
|
||||||
if (!permission) return notCollaborator();
|
|
||||||
return { data: { permission } };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const run = (linked, over, permission) =>
|
|
||||||
checkGate({ github: stub(linked, permission), owner: 'o', repo: 'r', pr: pull(over) });
|
|
||||||
|
|
||||||
const cases = [
|
|
||||||
['no linked issue fails', () => run([]), (r) => r.passed === false],
|
|
||||||
['linked but unapproved fails', () => run([{ number: 7 }]), (r) => r.passed === false],
|
|
||||||
['linked and approved passes',
|
|
||||||
() => run([{ number: 7, labels: [REQUIRED_LABEL] }]),
|
|
||||||
(r) => r.passed === true && r.issue === 7],
|
|
||||||
['approved but closed fails',
|
|
||||||
() => run([{ number: 7, state: 'CLOSED', labels: [REQUIRED_LABEL] }]),
|
|
||||||
(r) => r.passed === false],
|
|
||||||
['picks the approved one out of several',
|
|
||||||
() => run([{ number: 7 }, { number: 8, labels: [REQUIRED_LABEL] }]),
|
|
||||||
(r) => r.passed === true && r.issue === 8],
|
|
||||||
|
|
||||||
// Exemptions.
|
|
||||||
['write permission skips', () => run([], {}, 'write'), (r) => r.passed === true],
|
|
||||||
['maintain permission skips', () => run([], {}, 'maintain'), (r) => r.passed === true],
|
|
||||||
['bot skips', () => run([], { user: { type: 'Bot' } }), (r) => r.passed === true],
|
|
||||||
['draft skips', () => run([], { draft: true }), (r) => r.passed === true],
|
|
||||||
[`${EXEMPT_LABEL} skips`, () => run([], { labels: [{ name: EXEMPT_LABEL }] }), (r) => r.passed === true],
|
|
||||||
|
|
||||||
['triage permission is still gated', () => run([], {}, 'triage'), (r) => r.passed === false],
|
|
||||||
['MEMBER association without write is still gated',
|
|
||||||
() => run([], { author_association: 'MEMBER' }),
|
|
||||||
(r) => r.passed === false],
|
|
||||||
['CONTRIBUTOR with write skips',
|
|
||||||
() => run([], { author_association: 'CONTRIBUTOR' }, 'write'),
|
|
||||||
(r) => r.passed === true],
|
|
||||||
];
|
|
||||||
|
|
||||||
// --- findNotices: only the bot's own notices count -------------------------
|
|
||||||
// A stranger pasting the invisible MARKER into a comment must not suppress the
|
|
||||||
// notice or become the grace-window clock.
|
|
||||||
const commentsStub = (comments) => ({
|
|
||||||
paginate: async () => comments,
|
|
||||||
rest: { issues: { listComments: null } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const noticeCases = [
|
|
||||||
['a user comment carrying MARKER is not a notice',
|
|
||||||
[{ id: 1, user: { type: 'User' }, body: `sneaky ${MARKER}`, created_at: 'x' }], 0],
|
|
||||||
['a bot comment carrying MARKER is a notice',
|
|
||||||
[{ id: 2, user: { type: 'Bot' }, body: `${MARKER}\nnotice`, created_at: 'x' }], 1],
|
|
||||||
['a bot comment without MARKER is not a notice',
|
|
||||||
[{ id: 3, user: { type: 'Bot' }, body: 'unrelated', created_at: 'x' }], 0],
|
|
||||||
['a user MARKER does not mask the real bot notice',
|
|
||||||
[{ id: 4, user: { type: 'User' }, body: MARKER, created_at: 'x' },
|
|
||||||
{ id: 5, user: { type: 'Bot' }, body: MARKER, created_at: 'y' }], 1],
|
|
||||||
];
|
|
||||||
|
|
||||||
// --- runSweep: the stale-draft pass must honour every exemption ------------
|
|
||||||
const draft = (over) => ({
|
|
||||||
number: 9, draft: true, state: 'open', labels: [],
|
|
||||||
user: { type: 'User', login: 'alice' },
|
|
||||||
updated_at: new Date(Date.now() - 400 * 86400_000).toISOString(),
|
|
||||||
...over,
|
|
||||||
});
|
|
||||||
|
|
||||||
async function sweepClosed(pr, permission) {
|
|
||||||
const closed = [];
|
|
||||||
const github = {
|
|
||||||
paginate: async (route) => (route === 'pulls' ? [pr] : []),
|
|
||||||
rest: {
|
|
||||||
pulls: {
|
|
||||||
list: 'pulls',
|
|
||||||
update: async ({ pull_number }) => closed.push(pull_number),
|
|
||||||
},
|
|
||||||
issues: { listComments: 'comments', createComment: async () => {} },
|
|
||||||
repos: {
|
|
||||||
getCollaboratorPermissionLevel: async () => {
|
|
||||||
if (!permission) return notCollaborator();
|
|
||||||
return { data: { permission } };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
await runSweep({
|
|
||||||
github, core: { info() {}, warning() {} },
|
|
||||||
context: { repo: { owner: 'o', repo: 'r' } }, dryRun: false,
|
|
||||||
});
|
|
||||||
return closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sweepCases = [
|
|
||||||
['stale draft from an outside author closes', draft({}), 1],
|
|
||||||
['stale draft from a bot is left alone', draft({ user: { type: 'Bot' } }), 0],
|
|
||||||
['stale draft from a writer is left alone', draft({}), 0, 'write'],
|
|
||||||
[`stale draft with ${EXEMPT_LABEL} is left alone`, draft({ labels: [{ name: EXEMPT_LABEL }] }), 0],
|
|
||||||
['recent draft is left alone', draft({ updated_at: new Date().toISOString() }), 0],
|
|
||||||
];
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
let failed = 0;
|
|
||||||
for (const [name, comments, want] of noticeCases) {
|
|
||||||
const got = (await findNotices({ github: commentsStub(comments), owner: 'o', repo: 'r', number: 1 })).length;
|
|
||||||
if (got === want) console.log(` ok ${name}`);
|
|
||||||
else { failed++; console.log(` FAIL ${name} -> ${got} notices, wanted ${want}`); }
|
|
||||||
}
|
|
||||||
for (const [name, pr, want, permission] of sweepCases) {
|
|
||||||
const got = (await sweepClosed(pr, permission)).length;
|
|
||||||
if (got === want) console.log(` ok ${name}`);
|
|
||||||
else { failed++; console.log(` FAIL ${name} -> closed ${got}, wanted ${want}`); }
|
|
||||||
}
|
|
||||||
for (const [name, thunk, ok] of cases) {
|
|
||||||
const result = await thunk();
|
|
||||||
if (ok(result)) {
|
|
||||||
console.log(` ok ${name}`);
|
|
||||||
} else {
|
|
||||||
failed++;
|
|
||||||
console.log(` FAIL ${name} -> ${JSON.stringify(result)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.strictEqual(failed, 0, `${failed} case(s) failed`);
|
|
||||||
console.log(`\n${cases.length + noticeCases.length + sweepCases.length} passed`);
|
|
||||||
})();
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/
|
||||||
|
|
||||||
|
name: Fly Deploy (Production Environment)
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Version to deploy (without v prefix)"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
default: 'manual'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-honcho-prod-image:
|
||||||
|
name: Deploy Honcho Image (Production Environment)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
concurrency:
|
||||||
|
group: deploy-prod-group
|
||||||
|
cancel-in-progress: true
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: superfly/flyctl-actions/setup-flyctl@1.5
|
||||||
|
- run: |
|
||||||
|
# Determine the image label based on trigger type
|
||||||
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||||
|
IMAGE_LABEL="deployment-${{ github.event.inputs.version }}"
|
||||||
|
else
|
||||||
|
IMAGE_LABEL="deployment-${{ github.ref_name }}"
|
||||||
|
fi
|
||||||
|
flyctl deploy -a honcho-prod-image --remote-only --build-only --push --no-cache --image-label "$IMAGE_LABEL"
|
||||||
|
env:
|
||||||
|
FLY_API_TOKEN: ${{ secrets.FLY_PROD_API_TOKEN }}
|
||||||
|
|
||||||
|
prompt-service:
|
||||||
|
name: Push to Service (Production Environment)
|
||||||
|
needs: deploy-honcho-prod-image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Send POST request
|
||||||
|
env:
|
||||||
|
GITHUB_REF_NAME: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
# Determine version and image label based on trigger type
|
||||||
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||||
|
TAG="${{ github.event.inputs.version }}"
|
||||||
|
IMAGE_LABEL="honcho-prod-image:deployment-${{ github.event.inputs.version }}"
|
||||||
|
else
|
||||||
|
TAG=${GITHUB_REF_NAME#v}
|
||||||
|
IMAGE_LABEL="honcho-prod-image:deployment-${GITHUB_REF_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl --fail -X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer ${{ secrets.PROD_ENV_WEBHOOK_SECRET }}" \
|
||||||
|
-d "{\"version\":\"$TAG\",\"image_label\":\"$IMAGE_LABEL\"}" \
|
||||||
|
"${{ secrets.PROD_ENV_URL }}/webhooks/v1/add_honcho_version"
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/
|
||||||
|
|
||||||
|
name: Fly Deploy (Test Environment)
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Version to deploy (without v prefix)"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
default: 'manual'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-honcho-image:
|
||||||
|
name: Deploy Honcho Image (Test Environment)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
concurrency:
|
||||||
|
group: deploy-test-group
|
||||||
|
cancel-in-progress: true
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: superfly/flyctl-actions/setup-flyctl@1.5
|
||||||
|
- run: |
|
||||||
|
# Determine the image label based on trigger type
|
||||||
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||||
|
IMAGE_LABEL="deployment-${{ github.event.inputs.version }}"
|
||||||
|
else
|
||||||
|
IMAGE_LABEL="deployment-${{ github.ref_name }}"
|
||||||
|
fi
|
||||||
|
flyctl deploy -a honcho-image --remote-only --build-only --push --no-cache --image-label "$IMAGE_LABEL"
|
||||||
|
env:
|
||||||
|
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||||
|
|
||||||
|
prompt-service:
|
||||||
|
name: Push to Service (Test Environment)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: deploy-honcho-image
|
||||||
|
steps:
|
||||||
|
- name: Send POST request
|
||||||
|
env:
|
||||||
|
GITHUB_REF_NAME: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
# Determine version and image label based on trigger type
|
||||||
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||||
|
TAG="${{ github.event.inputs.version }}"
|
||||||
|
IMAGE_LABEL="honcho-image:deployment-${{ github.event.inputs.version }}"
|
||||||
|
else
|
||||||
|
TAG=${GITHUB_REF_NAME#v}
|
||||||
|
IMAGE_LABEL="honcho-image:deployment-${GITHUB_REF_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl --fail -X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer ${{ secrets.TEST_ENV_WEBHOOK_SECRET }}" \
|
||||||
|
-d "{\"version\":\"$TAG\",\"image_label\":\"$IMAGE_LABEL\"}" \
|
||||||
|
"${{ secrets.TEST_ENV_URL }}/webhooks/v1/add_honcho_version"
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
name: Issue Gate
|
|
||||||
|
|
||||||
# Labels pull requests that are not linked to an issue carrying the
|
|
||||||
# `maintainer-approved` label, and comments explaining how to fix it.
|
|
||||||
#
|
|
||||||
# This workflow never closes anything. `pr-sweeper.yml` re-checks later and closes
|
|
||||||
# only after the grace period — that gives contributors time to link an issue, and
|
|
||||||
# gives maintainers time to wave through a one-line fix. It is also the only thing
|
|
||||||
# that can notice a sidebar issue link, which fires no webhook of its own.
|
|
||||||
#
|
|
||||||
# `pull_request_target` is required so the job has write access on pull requests
|
|
||||||
# from forks. It must therefore NEVER run code from the pull request. The checkout
|
|
||||||
# below is safe because on `pull_request_target` actions/checkout defaults to the
|
|
||||||
# BASE ref, which is repo-trusted code. Never point it at `pr.head.sha`.
|
|
||||||
#
|
|
||||||
# Not triggered on `synchronize`: re-running on every push would be noise.
|
|
||||||
# Drafts are ignored until marked ready.
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, edited, reopened, ready_for_review]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
gate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const gate = require(`${process.env.GITHUB_WORKSPACE}/.github/scripts/issue-gate.js`);
|
|
||||||
await gate.runGate({ github, core, context });
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
name: Live LLM Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
# Runs on main pushes that can affect the LLM transport (narrower than
|
|
||||||
# unified-tests' src/** — live provider calls aren't worth burning on
|
|
||||||
# changes that can't reach the backends).
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- 'src/llm/**'
|
|
||||||
- 'src/config.py'
|
|
||||||
- 'tests/live_llm/**'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'uv.lock'
|
|
||||||
- '.python-version'
|
|
||||||
- '.github/workflows/live-llm-tests.yml'
|
|
||||||
# Manual trigger for PRs: add the `run-live-llm` label to run the suite
|
|
||||||
# against the PR's merge commit. The label is purged as soon as the run
|
|
||||||
# starts so it can be re-added to trigger another run.
|
|
||||||
pull_request:
|
|
||||||
types: [labeled]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
# Cap spend: at most one active run per PR (per ref for push/dispatch).
|
|
||||||
# Re-triggering a PR run cancels the in-flight one instead of stacking live
|
|
||||||
# provider calls; pushes to main queue instead of cancelling so main CI
|
|
||||||
# results aren't lost.
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: ${{ github.event_name != 'push' }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Only code owners (.github/CODEOWNERS) may trigger the suite manually via
|
|
||||||
# the label or workflow_dispatch; the gate also purges the trigger label so
|
|
||||||
# it can be re-added to trigger another run.
|
|
||||||
gate:
|
|
||||||
name: Gate manual trigger
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
uses: ./.github/workflows/manual-trigger-gate.yml
|
|
||||||
with:
|
|
||||||
label: run-live-llm
|
|
||||||
allow-workflow-dispatch: true
|
|
||||||
|
|
||||||
live-llm-tests:
|
|
||||||
name: Run Live LLM Tests
|
|
||||||
needs: gate
|
|
||||||
# always() lets this run on push events, where the gate's jobs are skipped.
|
|
||||||
# Manual triggers (label / workflow_dispatch) additionally require the
|
|
||||||
# gate's CODEOWNERS check to have passed.
|
|
||||||
if: >-
|
|
||||||
always() &&
|
|
||||||
(github.event_name == 'push' ||
|
|
||||||
((github.event_name == 'workflow_dispatch' ||
|
|
||||||
github.event.label.name == 'run-live-llm') &&
|
|
||||||
needs.gate.outputs.authorized == 'true'))
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 20
|
|
||||||
environment: unified-tests
|
|
||||||
permissions:
|
|
||||||
id-token: write # Required for OIDC authentication with AWS
|
|
||||||
contents: read
|
|
||||||
env:
|
|
||||||
PYTHONUNBUFFERED: "1"
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Configure AWS credentials
|
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
|
||||||
role-to-assume: ${{ vars.AWS_OIDC_ROLE_ARN }}
|
|
||||||
aws-region: us-east-1
|
|
||||||
role-duration-seconds: 3600
|
|
||||||
|
|
||||||
# Resolves secret ids from the newest release tags, fetches the newest
|
|
||||||
# available staging secret into the job env, and fails if none loaded.
|
|
||||||
- name: Load staging secrets
|
|
||||||
uses: ./.github/actions/load-staging-secrets
|
|
||||||
with:
|
|
||||||
secret-prefix: ${{ secrets.STAGING_SECRET_PREFIX }}
|
|
||||||
|
|
||||||
# Configure the test environment. Sentry/CloudEvents endpoints aren't
|
|
||||||
# reachable from CI. LIVE_LLM_ANTHROPIC_45_PLUS_MODELS must be set for
|
|
||||||
# the Anthropic tests to materialize — the claude_4_5_plus family has no
|
|
||||||
# default models, so with only the API key they'd silently collect as
|
|
||||||
# empty parameter sets.
|
|
||||||
- name: Configure test environment
|
|
||||||
run: |
|
|
||||||
{
|
|
||||||
# The staging dotenv carries AUTH_USE_AUTH=true without a usable
|
|
||||||
# JWT secret; src/config.py validates the pair at import time, so
|
|
||||||
# disable auth (this suite never runs the API server anyway).
|
|
||||||
echo "AUTH_USE_AUTH=false"
|
|
||||||
echo "SENTRY_ENABLED=false"
|
|
||||||
echo "TELEMETRY_ENABLED=false"
|
|
||||||
echo "LIVE_LLM_ANTHROPIC_45_PLUS_MODELS=claude-sonnet-4-5"
|
|
||||||
} >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
- name: Install uv
|
|
||||||
uses: astral-sh/setup-uv@v2
|
|
||||||
with:
|
|
||||||
enable-cache: true
|
|
||||||
cache-dependency-glob: "uv.lock"
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version-file: ".python-version"
|
|
||||||
|
|
||||||
- name: Install the project
|
|
||||||
run: uv sync --all-extras
|
|
||||||
|
|
||||||
# -n 0 overrides the `-n auto` xdist default from pyproject: ~15 short
|
|
||||||
# tests gain nothing from parallelism, serial execution avoids bursting
|
|
||||||
# every provider at once, and flake diagnosis gets ordered output.
|
|
||||||
- name: Run live LLM tests
|
|
||||||
run: uv run --frozen pytest tests/live_llm/ --live-llm -n 0 -v
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
name: Manual Trigger Gate
|
|
||||||
|
|
||||||
# Shared gate for workflows that can be triggered manually on PRs by adding a
|
|
||||||
# label (and optionally via workflow_dispatch): verifies the actor is a code
|
|
||||||
# owner and purges the trigger label so it can be re-added for another run.
|
|
||||||
#
|
|
||||||
# Callers must grant `pull-requests: write` on the calling job so the
|
|
||||||
# remove-label job can delete the label, and should gate downstream jobs on
|
|
||||||
# the `authorized` output rather than this workflow's conclusion.
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
label:
|
|
||||||
description: PR label that triggers the calling workflow
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
allow-workflow-dispatch:
|
|
||||||
description: Whether workflow_dispatch events may pass the gate
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
outputs:
|
|
||||||
authorized:
|
|
||||||
description: >-
|
|
||||||
'true' when the manual trigger's actor passed the CODEOWNERS check.
|
|
||||||
Empty on events where the check did not run (e.g. push).
|
|
||||||
value: ${{ jobs.check-actor.outputs.authorized }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Only code owners (.github/CODEOWNERS) may trigger the calling workflow
|
|
||||||
# manually.
|
|
||||||
check-actor:
|
|
||||||
name: Verify actor is a code owner
|
|
||||||
if: >-
|
|
||||||
(inputs.allow-workflow-dispatch && github.event_name == 'workflow_dispatch') ||
|
|
||||||
(github.event_name == 'pull_request' && github.event.label.name == inputs.label)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
outputs:
|
|
||||||
authorized: ${{ steps.codeowners.outputs.authorized }}
|
|
||||||
steps:
|
|
||||||
- name: Check actor against CODEOWNERS on main
|
|
||||||
id: codeowners
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
ACTOR: ${{ github.actor }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
# Usernames are case-insensitive on GitHub; compare lowercased.
|
|
||||||
owners="$(gh api -H "Accept: application/vnd.github.raw" \
|
|
||||||
"repos/${{ github.repository }}/contents/.github/CODEOWNERS?ref=main" \
|
|
||||||
| sed 's/#.*//' | grep -oE '@[A-Za-z0-9-]+' | tr -d '@' \
|
|
||||||
| tr '[:upper:]' '[:lower:]' | sort -u)"
|
|
||||||
actor_lc="$(printf '%s' "$ACTOR" | tr '[:upper:]' '[:lower:]')"
|
|
||||||
if printf '%s\n' "$owners" | grep -qxF "$actor_lc"; then
|
|
||||||
echo "@${ACTOR} is a code owner; proceeding"
|
|
||||||
echo "authorized=true" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "::error::@${ACTOR} is not listed in .github/CODEOWNERS on main — only code owners may trigger this workflow manually"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Purge the trigger label first thing. Best-effort: failing to remove the
|
|
||||||
# label (e.g. read-only token on a fork PR) doesn't block the tests.
|
|
||||||
remove-label:
|
|
||||||
name: Remove trigger label
|
|
||||||
if: github.event_name == 'pull_request' && github.event.label.name == inputs.label
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- name: Remove trigger label
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
run: |
|
|
||||||
if ! gh api --method DELETE \
|
|
||||||
"repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/${{ inputs.label }}"; then
|
|
||||||
echo "::warning::Could not remove the ${{ inputs.label }} label (it may have been removed already)"
|
|
||||||
fi
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
name: PR Sweeper
|
|
||||||
|
|
||||||
# Deferred half of the issue gate. Every six hours:
|
|
||||||
#
|
|
||||||
# 1. Re-check every pull request carrying `needs-approved-issue`. Clear the ones
|
|
||||||
# that now link an approved issue; close the ones still failing 72h after they
|
|
||||||
# were told. The re-check is the point — linking an issue through the sidebar
|
|
||||||
# fires no webhook, so `issue-gate.yml` never sees it.
|
|
||||||
# 2. Close drafts from outside the org after 30 days without activity.
|
|
||||||
#
|
|
||||||
# Runs on `schedule`, so it never touches pull request code and needs none of the
|
|
||||||
# `pull_request_target` precautions. Dispatch manually with dry_run to see what it
|
|
||||||
# would do before it does it.
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '17 */6 * * *'
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
dry_run:
|
|
||||||
description: 'Log intended actions without closing anything'
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
sweep:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/github-script@v7
|
|
||||||
env:
|
|
||||||
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || 'false' }}
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const gate = require(`${process.env.GITHUB_WORKSPACE}/.github/scripts/issue-gate.js`);
|
|
||||||
await gate.runSweep({ github, core, context, dryRun: process.env.DRY_RUN === 'true' });
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
name: Build and Push to GCP Artifact Registry (production)
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- v*
|
|
||||||
|
|
||||||
env:
|
|
||||||
GCP_PROJECT_ID: ${{ secrets.PROD_GCP_PROJECT_ID }}
|
|
||||||
GCP_AR_LOCATION: ${{ secrets.PROD_GCP_AR_LOCATION }}
|
|
||||||
GCP_AR_REPO: ${{ secrets.PROD_GCP_AR_REPO }}
|
|
||||||
IMAGE_NAME: ${{ secrets.PROD_IMAGE_NAME }}
|
|
||||||
GCP_SA_KEY: ${{ secrets.PROD_GCP_SA_KEY }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
version: ${{ steps.version.outputs.version }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Resolve and verify version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
VERSION="${GITHUB_REF_NAME#v}"
|
|
||||||
# A running instance serves this version at /openapi.json, so it must
|
|
||||||
# match the version being deployed.
|
|
||||||
PYPROJECT_VERSION="$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)"
|
|
||||||
if [[ "$VERSION" != "$PYPROJECT_VERSION" ]]; then
|
|
||||||
echo "::error::pyproject.toml version '$PYPROJECT_VERSION' does not match tag '$GITHUB_REF_NAME'. Bump pyproject.toml before tagging."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Authenticate to GCP
|
|
||||||
uses: google-github-actions/auth@v2
|
|
||||||
with:
|
|
||||||
credentials_json: ${{ env.GCP_SA_KEY }}
|
|
||||||
|
|
||||||
- name: Set up Cloud SDK
|
|
||||||
uses: google-github-actions/setup-gcloud@v2
|
|
||||||
|
|
||||||
- name: Configure Docker for Artifact Registry
|
|
||||||
run: gcloud auth configure-docker ${{ env.GCP_AR_LOCATION }}-docker.pkg.dev --quiet
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
env:
|
|
||||||
VERSION: ${{ steps.version.outputs.version }}
|
|
||||||
run: |
|
|
||||||
BASE="${{ env.GCP_AR_LOCATION }}-docker.pkg.dev/${{ env.GCP_PROJECT_ID }}/${{ env.GCP_AR_REPO }}/${{ env.IMAGE_NAME }}"
|
|
||||||
TAG="$BASE:deployment-v${VERSION}"
|
|
||||||
docker build -t "$TAG" .
|
|
||||||
docker push "$TAG"
|
|
||||||
|
|
||||||
prompt-service:
|
|
||||||
name: Push to Service (Production Environment)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build-and-push
|
|
||||||
steps:
|
|
||||||
- name: Send POST request
|
|
||||||
env:
|
|
||||||
VERSION: ${{ needs.build-and-push.outputs.version }}
|
|
||||||
run: |
|
|
||||||
# Name and tag only; the registry path is supplied downstream.
|
|
||||||
IMAGE_LABEL="${{ env.IMAGE_NAME }}:deployment-v${VERSION}"
|
|
||||||
|
|
||||||
curl --fail --connect-timeout 10 --max-time 60 -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer ${{ secrets.PROD_ENV_WEBHOOK_SECRET }}" \
|
|
||||||
-d "{\"version\":\"$VERSION\",\"image_label\":\"$IMAGE_LABEL\"}" \
|
|
||||||
"${{ secrets.PROD_ENV_URL }}/webhooks/v1/add_honcho_version"
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
name: Build and Push to GCP Artifact Registry (Staging)
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- v*
|
|
||||||
|
|
||||||
env:
|
|
||||||
GCP_PROJECT_ID: ${{ secrets.STAGING_GCP_PROJECT_ID }}
|
|
||||||
GCP_AR_LOCATION: ${{ secrets.STAGING_GCP_AR_LOCATION }}
|
|
||||||
GCP_AR_REPO: ${{ secrets.STAGING_GCP_AR_REPO }}
|
|
||||||
IMAGE_NAME: ${{ secrets.STAGING_IMAGE_NAME }}
|
|
||||||
GCP_SA_KEY: ${{ secrets.STAGING_GCP_SA_KEY }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-push:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
version: ${{ steps.version.outputs.version }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Resolve and verify version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
VERSION="${GITHUB_REF_NAME#v}"
|
|
||||||
# A running instance serves this version at /openapi.json, so it must
|
|
||||||
# match the version being deployed.
|
|
||||||
PYPROJECT_VERSION="$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)"
|
|
||||||
if [[ "$VERSION" != "$PYPROJECT_VERSION" ]]; then
|
|
||||||
echo "::error::pyproject.toml version '$PYPROJECT_VERSION' does not match tag '$GITHUB_REF_NAME'. Bump pyproject.toml before tagging."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Authenticate to GCP
|
|
||||||
uses: google-github-actions/auth@v2
|
|
||||||
with:
|
|
||||||
credentials_json: ${{ env.GCP_SA_KEY }}
|
|
||||||
|
|
||||||
- name: Set up Cloud SDK
|
|
||||||
uses: google-github-actions/setup-gcloud@v2
|
|
||||||
|
|
||||||
- name: Configure Docker for Artifact Registry
|
|
||||||
run: gcloud auth configure-docker ${{ env.GCP_AR_LOCATION }}-docker.pkg.dev --quiet
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
env:
|
|
||||||
VERSION: ${{ steps.version.outputs.version }}
|
|
||||||
run: |
|
|
||||||
BASE="${{ env.GCP_AR_LOCATION }}-docker.pkg.dev/${{ env.GCP_PROJECT_ID }}/${{ env.GCP_AR_REPO }}/${{ env.IMAGE_NAME }}"
|
|
||||||
TAG="$BASE:deployment-v${VERSION}"
|
|
||||||
docker build -t "$TAG" .
|
|
||||||
docker push "$TAG"
|
|
||||||
|
|
||||||
prompt-service:
|
|
||||||
name: Push to Service (Staging Environment)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build-and-push
|
|
||||||
steps:
|
|
||||||
- name: Send POST request
|
|
||||||
env:
|
|
||||||
VERSION: ${{ needs.build-and-push.outputs.version }}
|
|
||||||
run: |
|
|
||||||
# Name and tag only; the registry path is supplied downstream.
|
|
||||||
IMAGE_LABEL="${{ env.IMAGE_NAME }}:deployment-v${VERSION}"
|
|
||||||
|
|
||||||
curl --fail --connect-timeout 10 --max-time 60 -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer ${{ secrets.TEST_ENV_WEBHOOK_SECRET }}" \
|
|
||||||
-d "{\"version\":\"$VERSION\",\"image_label\":\"$IMAGE_LABEL\"}" \
|
|
||||||
"${{ secrets.TEST_ENV_URL }}/webhooks/v1/add_honcho_version"
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
name: Start Fly Runner
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
outputs:
|
|
||||||
runner-ready:
|
|
||||||
description: "Whether the runner is ready"
|
|
||||||
value: ${{ jobs.start-runner.outputs.runner-ready }}
|
|
||||||
machine-id:
|
|
||||||
description: "The Fly machine ID that was started"
|
|
||||||
value: ${{ jobs.start-runner.outputs.machine-id }}
|
|
||||||
runner-labels:
|
|
||||||
description: "Labels to target the self-hosted runner"
|
|
||||||
value: ${{ jobs.start-runner.outputs.runner-labels }}
|
|
||||||
runner-name:
|
|
||||||
description: "Resolved GitHub runner name"
|
|
||||||
value: ${{ jobs.start-runner.outputs.runner-name }}
|
|
||||||
|
|
||||||
env:
|
|
||||||
FLY_RUNNER_APP: ivysaur
|
|
||||||
FLY_RUNNER_REGION: iad
|
|
||||||
FLY_RUNNER_IMAGE: registry.fly.io/ivysaur:latest
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
start-runner:
|
|
||||||
name: Start Fly Runner
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
outputs:
|
|
||||||
runner-ready: ${{ steps.wait-for-runner.outputs.ready }}
|
|
||||||
machine-id: ${{ steps.machine-management.outputs.machine-id }}
|
|
||||||
runner-labels: ${{ steps.generate-labels.outputs.labels }}
|
|
||||||
runner-name: ${{ steps.wait-for-runner.outputs.runner-name }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Generate unique runner labels
|
|
||||||
id: generate-labels
|
|
||||||
run: |
|
|
||||||
UNIQUE_LABELS='"self-hosted","${{ github.run_id }}"'
|
|
||||||
echo "Generated unique labels: $UNIQUE_LABELS"
|
|
||||||
echo "labels=$UNIQUE_LABELS" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Setup Fly CLI
|
|
||||||
uses: superfly/flyctl-actions/setup-flyctl@master
|
|
||||||
|
|
||||||
- name: Get Fly app info
|
|
||||||
id: get-app-info
|
|
||||||
env:
|
|
||||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }}
|
|
||||||
run: |
|
|
||||||
echo "Getting app info for ${FLY_RUNNER_APP}..."
|
|
||||||
flyctl status -a "${FLY_RUNNER_APP}"
|
|
||||||
|
|
||||||
- name: Set GH_TOKEN in Fly secrets
|
|
||||||
env:
|
|
||||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }}
|
|
||||||
run: |
|
|
||||||
echo "Setting GH_TOKEN in Fly secrets..."
|
|
||||||
flyctl secrets set GH_TOKEN="${{ secrets.GH_TOKEN_ACTIONS }}" -a "${FLY_RUNNER_APP}"
|
|
||||||
|
|
||||||
- name: Create Fly machine
|
|
||||||
id: machine-management
|
|
||||||
env:
|
|
||||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Always create a fresh ephemeral machine
|
|
||||||
FULL_OUTPUT=$(flyctl machines run "${FLY_RUNNER_IMAGE}" \
|
|
||||||
-a "${FLY_RUNNER_APP}" \
|
|
||||||
--region "${FLY_RUNNER_REGION}" \
|
|
||||||
--env RUN_ID=${{ github.run_id }} \
|
|
||||||
--env TEST_TYPE="honcho-unified-runner" \
|
|
||||||
--vm-size shared-cpu-8x \
|
|
||||||
--vm-memory 8192 )
|
|
||||||
|
|
||||||
MACHINE_ID=$(echo "$FULL_OUTPUT" | grep "Machine ID:" | awk '{print $3}')
|
|
||||||
echo "Created machine: $MACHINE_ID"
|
|
||||||
echo "machine-id=$MACHINE_ID" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Wait for runner to be online
|
|
||||||
id: wait-for-runner
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_ACTIONS }}
|
|
||||||
MAX_WAIT: 420
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
if [ -z "${GITHUB_TOKEN}" ]; then
|
|
||||||
echo "GH_TOKEN secret is required to poll the Actions runner API."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
EXPECTED_RUNNER_NAME="honcho-unified-runner-${{ github.run_id }}"
|
|
||||||
echo "Waiting for runner named ${EXPECTED_RUNNER_NAME} to come online..."
|
|
||||||
|
|
||||||
WAITED=0
|
|
||||||
RUNNER_NAME=""
|
|
||||||
while [ $WAITED -lt $MAX_WAIT ]; do
|
|
||||||
RESPONSE=$(curl -s \
|
|
||||||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
|
||||||
-H "Accept: application/vnd.github.v3+json" \
|
|
||||||
"https://api.github.com/repos/${{ github.repository }}/actions/runners")
|
|
||||||
|
|
||||||
if echo "$RESPONSE" | grep -q '"message"'; then
|
|
||||||
echo "API Error: $(echo "$RESPONSE" | jq -r '.message')"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Find runner with exact name and is online and not busy
|
|
||||||
RUNNER_LINE=$(echo "$RESPONSE" | jq -r --arg runner_name "$EXPECTED_RUNNER_NAME" '.runners[]? | select(.name == $runner_name) | select(.status == "online") | select(.busy == false) | "\(.name)|\(.id)"' | head -n 1)
|
|
||||||
|
|
||||||
if [ -n "$RUNNER_LINE" ]; then
|
|
||||||
RUNNER_NAME=$(echo "$RUNNER_LINE" | cut -d'|' -f1)
|
|
||||||
echo "✅ Found runner: ${RUNNER_NAME}"
|
|
||||||
echo "ready=true" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "runner-name=${RUNNER_NAME}" >> "$GITHUB_OUTPUT"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "⏳ Waiting for runner... (${WAITED}s elapsed)"
|
|
||||||
sleep 15
|
|
||||||
WAITED=$((WAITED + 15))
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Runner failed to come online within ${MAX_WAIT} seconds"
|
|
||||||
echo "ready=false" >> "$GITHUB_OUTPUT"
|
|
||||||
exit 1
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
name: Static Analysis
|
name: Static Analysis
|
||||||
on:
|
on: [push]
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
@ -16,7 +12,7 @@ jobs:
|
||||||
- name: "Set up Python"
|
- name: "Set up Python"
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version-file: ".python-version"
|
python-version-file: "pyproject.toml"
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v2
|
uses: astral-sh/setup-uv@v2
|
||||||
with:
|
with:
|
||||||
|
|
@ -26,11 +22,3 @@ jobs:
|
||||||
run: uv sync --all-extras --dev
|
run: uv sync --all-extras --dev
|
||||||
- name: run basedpyright
|
- name: run basedpyright
|
||||||
run: uv run basedpyright
|
run: uv run basedpyright
|
||||||
|
|
||||||
issue-gate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# The gate runs from `pull_request_target`, where a crash is invisible until
|
|
||||||
# a contributor's PR is silently ungated. Check it here instead.
|
|
||||||
- run: node .github/scripts/issue-gate.test.js
|
|
||||||
|
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
name: Unified Tests (Fly Runner)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- 'src/**'
|
|
||||||
- 'tests/**'
|
|
||||||
# Manual trigger for PRs: add the `run-unified-tests` label to run the suite
|
|
||||||
# against the PR's merge commit. The label is purged as soon as the run
|
|
||||||
# starts so it can be re-added to trigger another run.
|
|
||||||
pull_request:
|
|
||||||
types: [labeled]
|
|
||||||
|
|
||||||
# Cap spend: at most one active run per PR (per ref for push). Re-triggering
|
|
||||||
# a PR run cancels the in-flight one instead of stacking Fly machines; pushes
|
|
||||||
# to main queue instead of cancelling so main CI results aren't lost. The
|
|
||||||
# cleanup-machine job runs `if: always()`, which still executes on cancelled
|
|
||||||
# runs, so a cancelled run's Fly machine and runner are still torn down.
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: ${{ github.event_name != 'push' }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
actions: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Only code owners (.github/CODEOWNERS) may trigger the suite manually via
|
|
||||||
# the label; the gate also purges the trigger label so it can be re-added
|
|
||||||
# to trigger another run.
|
|
||||||
gate:
|
|
||||||
name: Gate manual trigger
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
uses: ./.github/workflows/manual-trigger-gate.yml
|
|
||||||
with:
|
|
||||||
label: run-unified-tests
|
|
||||||
|
|
||||||
start-runner:
|
|
||||||
name: Start Fly Runner
|
|
||||||
needs: gate
|
|
||||||
# always() lets this run on push events, where the gate's jobs are skipped.
|
|
||||||
# Label adds other than run-unified-tests trigger the workflow but skip
|
|
||||||
# every job here; the run-unified-tests label additionally requires the
|
|
||||||
# gate's CODEOWNERS check to have passed.
|
|
||||||
if: >-
|
|
||||||
always() &&
|
|
||||||
(github.event_name == 'push' ||
|
|
||||||
(github.event.label.name == 'run-unified-tests' &&
|
|
||||||
needs.gate.outputs.authorized == 'true'))
|
|
||||||
uses: ./.github/workflows/start-fly-runner.yml
|
|
||||||
secrets: inherit
|
|
||||||
|
|
||||||
unified-tests:
|
|
||||||
name: Run Unified Tests
|
|
||||||
runs-on: ${{ fromJSON(format('[{0}]', needs.start-runner.outputs.runner-labels)) }}
|
|
||||||
needs: start-runner
|
|
||||||
# !cancelled() so this doesn't inherit gate's skip on push events.
|
|
||||||
if: >-
|
|
||||||
!cancelled() &&
|
|
||||||
needs.start-runner.outputs.runner-ready == 'true'
|
|
||||||
timeout-minutes: 90
|
|
||||||
environment: unified-tests
|
|
||||||
permissions:
|
|
||||||
id-token: write # Required for OIDC authentication with AWS
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
PYTHONUNBUFFERED: "1"
|
|
||||||
TEST_DISCORD_WEBHOOK_URL: ${{ secrets.TEST_DISCORD_WEBHOOK_URL }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Configure AWS credentials
|
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
|
||||||
role-to-assume: ${{ vars.AWS_OIDC_ROLE_ARN }}
|
|
||||||
aws-region: us-east-1
|
|
||||||
role-duration-seconds: 43200 # 12 hours
|
|
||||||
|
|
||||||
# Resolves secret ids from the newest release tags, fetches the newest
|
|
||||||
# available staging secret into the job env, and fails if none loaded.
|
|
||||||
- name: Load staging secrets
|
|
||||||
uses: ./.github/actions/load-staging-secrets
|
|
||||||
with:
|
|
||||||
secret-prefix: ${{ secrets.STAGING_SECRET_PREFIX }}
|
|
||||||
|
|
||||||
# Layer test-specific overrides on top of the staging secret. The staging
|
|
||||||
# dotenv tracks the deployed release and can drift from what main's config
|
|
||||||
# expects; the TESTING_SECRET_ID secret holds only the keys (flat JSON,
|
|
||||||
# exact env var names) the unified tests need to pin. The get-secrets
|
|
||||||
# action refuses to inject an env var that already exists, so the
|
|
||||||
# overrides are fetched under a prefix alias here and promoted over the
|
|
||||||
# staging values in the next step.
|
|
||||||
- name: Fetch testing secret overrides
|
|
||||||
uses: aws-actions/aws-secretsmanager-get-secrets@v2
|
|
||||||
with:
|
|
||||||
secret-ids: |
|
|
||||||
HONCHO_TEST_OVERRIDE,${{ secrets.TESTING_SECRET_ID }}
|
|
||||||
parse-json-secrets: true
|
|
||||||
|
|
||||||
# Re-export each HONCHO_TEST_OVERRIDE_* var under its real name; the
|
|
||||||
# later $GITHUB_ENV write wins over the value loaded from the staging
|
|
||||||
# secret. Values are already masked by the fetch step above.
|
|
||||||
- name: Apply testing secret overrides
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
applied=0
|
|
||||||
while IFS= read -r -d '' entry; do
|
|
||||||
name="${entry%%=*}"
|
|
||||||
value="${entry#*=}"
|
|
||||||
case "$name" in
|
|
||||||
HONCHO_TEST_OVERRIDE_*)
|
|
||||||
target="${name#HONCHO_TEST_OVERRIDE_}"
|
|
||||||
{
|
|
||||||
echo "${target}<<__HONCHO_OVERRIDE_EOF__"
|
|
||||||
printf '%s\n' "$value"
|
|
||||||
echo "__HONCHO_OVERRIDE_EOF__"
|
|
||||||
} >> "$GITHUB_ENV"
|
|
||||||
echo "Overriding ${target}"
|
|
||||||
applied=$((applied + 1))
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done < <(env -0)
|
|
||||||
echo "Applied ${applied} override(s)"
|
|
||||||
|
|
||||||
# Configure the test environment. Disables auth/Sentry/CloudEvents telemetry
|
|
||||||
# (their endpoints aren't reachable from CI), and points REASONING_TRACES_FILE
|
|
||||||
# at a shared path so the API + deriver record full LLM I/O for auditing — the
|
|
||||||
# runner uploads it to S3. Written after the fetch steps so these win over the
|
|
||||||
# values loaded from Secrets Manager (last $GITHUB_ENV write wins). Stale
|
|
||||||
# config keys loaded from the staging secret (e.g. settings that have since
|
|
||||||
# been renamed or removed on main) must always be ignored by the app config.
|
|
||||||
- name: Configure test environment
|
|
||||||
run: |
|
|
||||||
{
|
|
||||||
echo "AUTH_USE_AUTH=false"
|
|
||||||
echo "SENTRY_ENABLED=false"
|
|
||||||
echo "TELEMETRY_ENABLED=false"
|
|
||||||
echo "REASONING_TRACES_FILE=unified-reasoning-traces.jsonl"
|
|
||||||
} >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
- name: Verify Docker is available
|
|
||||||
run: docker info
|
|
||||||
|
|
||||||
- name: Verify uv and Python
|
|
||||||
run: |
|
|
||||||
uv --version
|
|
||||||
python3.13 --version
|
|
||||||
which python3.13
|
|
||||||
|
|
||||||
- name: Install the project
|
|
||||||
run: uv sync --all-extras
|
|
||||||
|
|
||||||
- name: Run unified tests
|
|
||||||
run: uv run python -m tests.unified.run
|
|
||||||
|
|
||||||
cleanup-machine:
|
|
||||||
name: Cleanup Fly Machine and Runner
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [start-runner, unified-tests]
|
|
||||||
if: always() && needs.start-runner.outputs.machine-id != ''
|
|
||||||
env:
|
|
||||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_ACTIONS }}
|
|
||||||
FLY_RUNNER_APP: ivysaur
|
|
||||||
steps:
|
|
||||||
- name: Setup Fly CLI
|
|
||||||
uses: superfly/flyctl-actions/setup-flyctl@1.5
|
|
||||||
|
|
||||||
- name: Cleanup fly machine
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
MACHINE_ID="${{ needs.start-runner.outputs.machine-id }}"
|
|
||||||
if [ -z "$MACHINE_ID" ]; then
|
|
||||||
echo "No machine ID provided, skipping Fly cleanup."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🧹 Cleaning up machine: $MACHINE_ID"
|
|
||||||
flyctl machines stop "$MACHINE_ID" -a "$FLY_RUNNER_APP" || echo "Machine may already be stopped"
|
|
||||||
flyctl machines destroy "$MACHINE_ID" -a "$FLY_RUNNER_APP" --force || echo "Failed to destroy machine"
|
|
||||||
|
|
||||||
- name: Cleanup GitHub runner
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
RUNNER_NAME="${{ needs.start-runner.outputs.runner-name }}"
|
|
||||||
FALLBACK_LABEL="${{ github.run_id }}"
|
|
||||||
|
|
||||||
echo "🗑️ Cleaning up GitHub runner (name: ${RUNNER_NAME:-unknown}, label: ${FALLBACK_LABEL})"
|
|
||||||
|
|
||||||
RUNNERS_RESPONSE=$(curl -s \
|
|
||||||
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
||||||
-H "Accept: application/vnd.github+json" \
|
|
||||||
"https://api.github.com/repos/${{ github.repository }}/actions/runners")
|
|
||||||
|
|
||||||
if echo "$RUNNERS_RESPONSE" | grep -q '"message"'; then
|
|
||||||
echo "⚠️ Failed to fetch runners: $(echo "$RUNNERS_RESPONSE" | jq -r '.message')"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
RUNNER_ID=""
|
|
||||||
if [ -n "$RUNNER_NAME" ]; then
|
|
||||||
RUNNER_ID=$(echo "$RUNNERS_RESPONSE" | jq -r --arg name "$RUNNER_NAME" '.runners[]? | select(.name == $name) | .id')
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$RUNNER_ID" ]; then
|
|
||||||
RUNNER_ID=$(echo "$RUNNERS_RESPONSE" | jq -r --arg label "$FALLBACK_LABEL" '.runners[]? | select([.labels[].name] | index($label)) | .id' | head -n 1)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$RUNNER_ID" ] || [ "$RUNNER_ID" = "null" ]; then
|
|
||||||
echo "⚠️ Runner not found, nothing to delete."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
DELETE_RESPONSE=$(curl -s -w "%{http_code}" \
|
|
||||||
-X DELETE \
|
|
||||||
-H "Accept: application/vnd.github+json" \
|
|
||||||
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
||||||
"https://api.github.com/repos/${{ github.repository }}/actions/runners/$RUNNER_ID")
|
|
||||||
|
|
||||||
HTTP_CODE="${DELETE_RESPONSE: -3}"
|
|
||||||
if [ "$HTTP_CODE" = "204" ]; then
|
|
||||||
echo "✅ Successfully deleted runner."
|
|
||||||
else
|
|
||||||
echo "⚠️ Failed to delete runner. HTTP code: $HTTP_CODE"
|
|
||||||
echo "Response: ${DELETE_RESPONSE%???}"
|
|
||||||
fi
|
|
||||||
|
|
@ -11,7 +11,6 @@ on:
|
||||||
- '**.jsx'
|
- '**.jsx'
|
||||||
- 'pyproject.toml'
|
- 'pyproject.toml'
|
||||||
- 'uv.lock'
|
- 'uv.lock'
|
||||||
- '.python-version'
|
|
||||||
- 'sdks/typescript/package.json'
|
- 'sdks/typescript/package.json'
|
||||||
- 'sdks/typescript/bun.lock'
|
- 'sdks/typescript/bun.lock'
|
||||||
- '.github/workflows/unittest.yml'
|
- '.github/workflows/unittest.yml'
|
||||||
|
|
@ -25,7 +24,6 @@ on:
|
||||||
- '**.jsx'
|
- '**.jsx'
|
||||||
- 'pyproject.toml'
|
- 'pyproject.toml'
|
||||||
- 'uv.lock'
|
- 'uv.lock'
|
||||||
- '.python-version'
|
|
||||||
- 'sdks/typescript/package.json'
|
- 'sdks/typescript/package.json'
|
||||||
- 'sdks/typescript/bun.lock'
|
- 'sdks/typescript/bun.lock'
|
||||||
- '.github/workflows/unittest.yml'
|
- '.github/workflows/unittest.yml'
|
||||||
|
|
@ -40,6 +38,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
python: ${{ steps.filter.outputs.python }}
|
python: ${{ steps.filter.outputs.python }}
|
||||||
|
typescript: ${{ steps.filter.outputs.typescript }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dorny/paths-filter@v3
|
- uses: dorny/paths-filter@v3
|
||||||
|
|
@ -50,8 +49,9 @@ jobs:
|
||||||
- '**.py'
|
- '**.py'
|
||||||
- 'pyproject.toml'
|
- 'pyproject.toml'
|
||||||
- 'uv.lock'
|
- 'uv.lock'
|
||||||
- '.python-version'
|
|
||||||
- 'migrations/**'
|
- 'migrations/**'
|
||||||
|
- '.github/workflows/unittest.yml'
|
||||||
|
typescript:
|
||||||
- 'sdks/typescript/**'
|
- 'sdks/typescript/**'
|
||||||
- '.github/workflows/unittest.yml'
|
- '.github/workflows/unittest.yml'
|
||||||
|
|
||||||
|
|
@ -88,14 +88,7 @@ jobs:
|
||||||
- name: "Set up Python"
|
- name: "Set up Python"
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version-file: ".python-version"
|
python-version-file: "pyproject.toml"
|
||||||
|
|
||||||
- name: Install bun
|
|
||||||
uses: oven-sh/setup-bun@v2
|
|
||||||
|
|
||||||
- name: Install TypeScript SDK dependencies
|
|
||||||
run: bun install
|
|
||||||
working-directory: sdks/typescript
|
|
||||||
|
|
||||||
- name: Install the project
|
- name: Install the project
|
||||||
run: uv sync --all-extras --dev
|
run: uv sync --all-extras --dev
|
||||||
|
|
@ -108,40 +101,46 @@ jobs:
|
||||||
SENTRY_ENABLED: false
|
SENTRY_ENABLED: false
|
||||||
LLM_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
LLM_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||||
LLM_ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
LLM_ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
LLM_OPENAI_COMPATIBLE_API_KEY: test-key
|
|
||||||
LLM_OPENAI_COMPATIBLE_BASE_URL: http://localhost:8000
|
|
||||||
DERIVER_PROVIDER: openai
|
DERIVER_PROVIDER: openai
|
||||||
DERIVER_MODEL: test
|
DERIVER_MODEL: test
|
||||||
DIALECTIC_LEVELS__minimal__PROVIDER: openai
|
DIALECTIC_PROVIDER: openai
|
||||||
DIALECTIC_LEVELS__minimal__MODEL: test
|
DIALECTIC_MODEL: test
|
||||||
DIALECTIC_LEVELS__minimal__THINKING_BUDGET_TOKENS: 0
|
|
||||||
DIALECTIC_LEVELS__minimal__MAX_TOOL_ITERATIONS: 2
|
|
||||||
DIALECTIC_LEVELS__low__PROVIDER: openai
|
|
||||||
DIALECTIC_LEVELS__low__MODEL: test
|
|
||||||
DIALECTIC_LEVELS__low__THINKING_BUDGET_TOKENS: 0
|
|
||||||
DIALECTIC_LEVELS__low__MAX_TOOL_ITERATIONS: 5
|
|
||||||
DIALECTIC_LEVELS__medium__PROVIDER: openai
|
|
||||||
DIALECTIC_LEVELS__medium__MODEL: test
|
|
||||||
DIALECTIC_LEVELS__medium__THINKING_BUDGET_TOKENS: 0
|
|
||||||
DIALECTIC_LEVELS__medium__MAX_TOOL_ITERATIONS: 4
|
|
||||||
DIALECTIC_LEVELS__high__PROVIDER: openai
|
|
||||||
DIALECTIC_LEVELS__high__MODEL: test
|
|
||||||
DIALECTIC_LEVELS__high__THINKING_BUDGET_TOKENS: 0
|
|
||||||
DIALECTIC_LEVELS__high__MAX_TOOL_ITERATIONS: 4
|
|
||||||
DIALECTIC_LEVELS__max__PROVIDER: openai
|
|
||||||
DIALECTIC_LEVELS__max__MODEL: test
|
|
||||||
DIALECTIC_LEVELS__max__THINKING_BUDGET_TOKENS: 0
|
|
||||||
DIALECTIC_LEVELS__max__MAX_TOOL_ITERATIONS: 10
|
|
||||||
DIALECTIC_QUERY_GENERATION_PROVIDER: openai
|
DIALECTIC_QUERY_GENERATION_PROVIDER: openai
|
||||||
DIALECTIC_QUERY_GENERATION_MODEL: test
|
DIALECTIC_QUERY_GENERATION_MODEL: test
|
||||||
SUMMARY_PROVIDER: openai
|
SUMMARY_PROVIDER: openai
|
||||||
SUMMARY_MODEL: test
|
SUMMARY_MODEL: test
|
||||||
|
|
||||||
|
test-typescript:
|
||||||
|
needs: changes
|
||||||
|
if: ${{ needs.changes.outputs.typescript == 'true' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup bun
|
||||||
|
uses: oven-sh/setup-bun@v1
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- name: Install TypeScript SDK dependencies
|
||||||
|
run: |
|
||||||
|
cd sdks/typescript
|
||||||
|
bun install
|
||||||
|
|
||||||
|
- name: Run TypeScript SDK tests
|
||||||
|
run: |
|
||||||
|
cd sdks/typescript
|
||||||
|
bun run test
|
||||||
|
env:
|
||||||
|
HONCHO_API_KEY: test-key
|
||||||
|
HONCHO_BASE_URL: http://localhost:8000
|
||||||
|
|
||||||
# Status check for branch protection rules
|
# Status check for branch protection rules
|
||||||
# This job always runs and reports success only if all required jobs pass
|
# This job always runs and reports success only if all required jobs pass
|
||||||
test-status:
|
test-status:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [changes, test-python]
|
needs: [changes, test-python, test-typescript]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: Check test results
|
- name: Check test results
|
||||||
|
|
@ -150,4 +149,8 @@ jobs:
|
||||||
echo "Python tests failed or were cancelled"
|
echo "Python tests failed or were cancelled"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if [[ "${{ needs.changes.outputs.typescript }}" == "true" && "${{ needs.test-typescript.result }}" != "success" && "${{ needs.test-typescript.result }}" != "skipped" ]]; then
|
||||||
|
echo "TypeScript tests failed or were cancelled"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
echo "All required tests passed!"
|
echo "All required tests passed!"
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
.worktrees/
|
|
||||||
api/**/*.db
|
api/**/*.db
|
||||||
api/data
|
api/data
|
||||||
api/docker-compose.yml
|
api/docker-compose.yml
|
||||||
|
|
||||||
*.db
|
*.db
|
||||||
data
|
data
|
||||||
redis-data
|
docker-compose.yml
|
||||||
/docker-compose.yml
|
compose.yml
|
||||||
/compose.yml
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -182,7 +180,6 @@ docs/node_modules
|
||||||
|
|
||||||
timing_logs.csv
|
timing_logs.csv
|
||||||
|
|
||||||
config.json
|
|
||||||
config.toml
|
config.toml
|
||||||
.aider*
|
.aider*
|
||||||
|
|
||||||
|
|
@ -191,8 +188,3 @@ CRUSH.md
|
||||||
|
|
||||||
metrics.jsonl
|
metrics.jsonl
|
||||||
AGENTS.md
|
AGENTS.md
|
||||||
lancedb_data/
|
|
||||||
grafana-data/
|
|
||||||
|
|
||||||
# Claude Code addon stuff
|
|
||||||
.omc
|
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,12 @@ repos:
|
||||||
language: system
|
language: system
|
||||||
files: ^(src/|tests/|sdks/python/|scripts/).*\.py$
|
files: ^(src/|tests/|sdks/python/|scripts/).*\.py$
|
||||||
require_serial: true
|
require_serial: true
|
||||||
pass_filenames: true
|
pass_filenames: false
|
||||||
|
|
||||||
# Run main application tests
|
# Run main application tests
|
||||||
- id: pytest-main
|
- id: pytest-main
|
||||||
name: pytest (main app)
|
name: pytest (main app)
|
||||||
entry: uv run pytest -x tests/ --ignore=tests/alembic/
|
entry: uv run pytest tests/ --ignore=tests/alembic/
|
||||||
language: system
|
language: system
|
||||||
files: ^(src/|tests/).*\.py$
|
files: ^(src/|tests/).*\.py$
|
||||||
stages: [pre-push]
|
stages: [pre-push]
|
||||||
|
|
@ -74,11 +74,11 @@ repos:
|
||||||
# Run Alembic tests only when migrations change
|
# Run Alembic tests only when migrations change
|
||||||
- id: pytest-alembic
|
- id: pytest-alembic
|
||||||
name: pytest (alembic migrations)
|
name: pytest (alembic migrations)
|
||||||
entry: uv run python scripts/run_alembic_tests.py
|
entry: uv run pytest tests/alembic/
|
||||||
language: system
|
language: system
|
||||||
files: ^(migrations/versions/.*\.py|tests/alembic/.*\.py)$
|
files: ^(migrations/.*\.py|tests/alembic/.*\.py)$
|
||||||
stages: [pre-push]
|
stages: [pre-push]
|
||||||
pass_filenames: true
|
pass_filenames: false
|
||||||
require_serial: true
|
require_serial: true
|
||||||
|
|
||||||
# Ensure each alembic migration revision has a corresponding test file
|
# Ensure each alembic migration revision has a corresponding test file
|
||||||
|
|
@ -99,10 +99,10 @@ repos:
|
||||||
stages: [pre-push]
|
stages: [pre-push]
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|
||||||
# TypeScript build with bun (tests run via pytest)
|
# TypeScript build/test with bun
|
||||||
- id: typescript-check
|
- id: typescript-check
|
||||||
name: TypeScript build
|
name: TypeScript build and test
|
||||||
entry: bash -c 'if [ -f "sdks/typescript/package.json" ]; then cd sdks/typescript && bun run build; fi'
|
entry: bash -c 'if [ -f "sdks/typescript/package.json" ]; then cd sdks/typescript && bun run build && bun run test; fi'
|
||||||
language: system
|
language: system
|
||||||
files: ^sdks/typescript/.*\.(js|ts|jsx|tsx|json)$
|
files: ^sdks/typescript/.*\.(js|ts|jsx|tsx|json)$
|
||||||
stages: [pre-push]
|
stages: [pre-push]
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
3.13
|
3.11
|
||||||
|
|
|
||||||
459
CHANGELOG.md
|
|
@ -5,459 +5,6 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
## [3.1.1] - 2026-09-02
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Server `requires-python` is `>=3.13`, matching the production image. Self-hosters on 3.10–3.12 need to upgrade; SDK and CLI floors are unchanged (#1090)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Concurrent `create_documents` writers to the same collection deadlocked on `times_derived` reinforcement UPDATEs issued in batch order; the error was swallowed per-document, the batch was lost, and the queue item was marked processed. Writers now lock target rows with `SELECT ... ORDER BY id FOR UPDATE` before applying, abort the batch on `SQLAlchemyError` instead of continuing through a dead session, and retry transient errors (deadlock, serialization failure, lock/statement timeout, lost connection) up to `MAX_RETRYABLE_ATTEMPTS` instead of burning the item (#1033)
|
|
||||||
- Scope backfill no longer embeds, writes, and syncs every planned copy at once. A 14k-document session is ~580MB of vectors; several concurrent backfills OOM-killed the deriver at its 1000Mi limit and crash-looped because the work units never completed. Phases 2–4 now run per chunk of 500 specs, reload source embeddings per chunk, and drop them once synced. Membership is locked across chunk writes so a concurrent leave cannot commit between the check and the inserts (#1104)
|
|
||||||
- Model-generated observations with NUL bytes (`\u0000`) no longer fail the exact-content dedup pre-fetch with a Postgres `DataError` that dropped the whole observer batch. Ingress already stripped NUL from user content; the deriver now strips it so stored text matches embedded text. All-NUL content is dropped rather than stored empty (#1095)
|
|
||||||
- `search_messages` no longer forwards `top_k=0` to Turbopuffer (which requires 1..10000). Zero/negative limits short-circuit to empty results; tool limits are floored at 1. The documents path was already guarded (#970); this closes the message path (#1084)
|
|
||||||
- OpenAI-compatible tool-call turns with `content=null` keep null through history replay instead of being coerced to `""`. Providers that bind reasoning state to the exact assistant message shape were breaking on the empty string. Tool-less null still becomes `""` (#1064)
|
|
||||||
- The production image now ships `pyproject.toml` in the runtime stage, so the service reports its real version instead of `unknown` in OpenAPI and telemetry (#1074)
|
|
||||||
|
|
||||||
## [3.1.0] - 2026-08-25
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Scopes: a named grouping of sessions that acts as a visibility boundary on recall, implemented as a facade over an observer peer (`scope.{name}` with `{"kind": "scope"}`). Developers manage them exclusively through `/v3/workspaces/{workspace_id}/scopes` (create-or-get, list, get, add/list/remove session membership) and an optional `scopes` field on session create — never through the observer/observed mechanics. Scope peers cannot author messages, cannot be a chat or representation `target`, are excluded from `peers.list` by default (`PeerGet.kind` = `"scope"` / `"all"` switches the view), and are rejected on the generic session-peer routes. Workspace-level key required; peer- and session-scoped keys get 401. Legacy peers occupying a reserved `scope.` name without the kind flag are refused with 409, never adopted (#884)
|
|
||||||
- `scope` read option on chat, representation, session context, and workspace search. A single scope swaps the observer to the backing scope peer so conclusion recall, peer cards, and message tools stay inside that scope's membership. A list of scopes takes the union of member sessions (capped at `MAX_SESSION_ALLOWLIST_ENTRIES`) and executes via the session-allowlist path. Empty scopes fail closed. `scope` is mutually exclusive with `filters` and `session_id`. Workspace- or admin-level key required (403 otherwise). Scope peers are also rejected as `peer_target` / `peer_perspective` on session context and as the path peer or `target` on `GET /peers/{id}/context` (#897)
|
|
||||||
- Scope backfill-by-copy and removal reconciliation. Adding a session that already has messages copies its explicit-level documents into the scope's collections (no LLM re-derivation; idempotent via `copied_from`). Removing a session soft-deletes those copies and fail-closed cascades to derived documents whose `source_ids` intersect anything removed, then enqueues a `card_refresh` dream with `rebuild=True` plus an omni dream. `GET /v3/workspaces/{workspace_id}/scopes/{scope_id}/status` reports per-session backfill state (`pending` / `completed` / `failed`, plus `docs_copied`) (#904)
|
|
||||||
- Workspace-level chat at `POST /v3/workspaces/{workspace_id}/chat`: agentic dialectic over the whole workspace instead of a single (observer, observed) pair. Prefetches workspace stats and the top active peers' self cards, then searches pair-scoped memory with `[observer->observed]` attribution. Supports `session_id`, `scope`, `reasoning_level`, `response_format`, and SSE streaming (#931)
|
|
||||||
- MCP workspace discovery: tools accept `workspace_id`, the worker honors an optional `X-Honcho-Workspace-ID` connection header, and `list_workspace` / `create_workspace` tools let clients pick or create a workspace instead of relying on the SDK default (#1020)
|
|
||||||
- MCP `search` also queries conclusions in parallel with messages when `peer_id` is given, returning `{messages, conclusions}`. The conclusions leg degrades to `[]` on error so search never gets worse than before (#974)
|
|
||||||
- Prometheus metrics for physical DB connections, visible even under `DB_POOL_CLASS=null`: `db_connections_open` (gauge) and `db_connections_established` (counter), hooked to SQLAlchemy connection-lifecycle events and registered on both the API and the deriver (#1055)
|
|
||||||
- Bounded-label Prometheus series are zero-initialized at process start so an absent series means a broken scrape rather than "nothing happened" (#927)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Workspace and pair chat system prompts now describe Honcho, peers, and the harness on their own terms, and render only the tools the request actually offers. The pair prompt no longer advertises a write tool that is not in the loadout (#1066)
|
|
||||||
- Deriver idle polling backoff is longer and no longer reset by periodic reconciler work, so downstream connection pools can cull idle DB connections (#1015)
|
|
||||||
- LLM provider SDKs are lazy-loaded so idle API and deriver processes no longer pay for every provider at import time (#1011)
|
|
||||||
- Production image is a multi-stage build: LanceDB/PyArrow move behind an optional `lancedb` extra (`INSTALL_LANCEDB=true` to restore them), FastAPI's unused cloud CLI is dropped, and the venv is copied into the runtime image with final ownership so Docker does not double the layer. Default unpacked image is about 663 MB (was 1.7 GB) (#1014)
|
|
||||||
- Redis Cluster cache keys hash-tag the namespace so one deployment's keys land on a single shard instead of opening a connection to every node. No behaviour change on a non-cluster backend; existing keys age out by TTL (#1058)
|
|
||||||
- Deriver extraction prompt no longer leaks its own few-shot examples into extracted conclusions (#1028)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Observer-scoped `get_observation_context` no longer materializes every session the observer has ever joined into a `session_name IN (...)` list (twice in one statement). Past ~32k sessions that hit psycopg's bind-parameter ceiling and 500'd. The observer half is now a correlated `EXISTS` over `session_peers`, two bind parameters regardless of membership size (#1065)
|
|
||||||
- Re-adding an already-active session peer no longer advances `joined_at`, so `peer_perspective` search keeps messages from the original join. Genuine leave-and-rejoin still starts a new window (#1059)
|
|
||||||
- Transient embedding-provider errors (for example an OpenAI-compatible 200 with empty `data: []`) were relabeled as token-limit errors. Only genuine oversize input raises `EmbeddingTokenLimitError`; other provider errors propagate unchanged (#791)
|
|
||||||
- The filter DSL now fails closed with a 422 instead of a 500 on bad shapes, coerces operands by column type (so `{"session_id": {"ne": "abc"}}` is a string inequality rather than "invalid numeric"), and treats `NOT` / `ne` as null-safe (`IS NOT TRUE` / `IS DISTINCT FROM`) so negation no longer drops rows whose field is unset. Closed-set columns like `level` reject unknown values. Session-allowlist entries must be well-formed ids (`*` is 422, not a silent widen) (#947)
|
|
||||||
- `ne` on JSONB metadata keys is null-safe: a missing key is not equal to the compared value, so `{"metadata": {"foo": {"ne": "bar"}}}` includes rows where `foo` is unset (#1036)
|
|
||||||
- Oversized texts in `simple_batch_embed` are truncated to the embedding token cap instead of failing the whole batch. Representation processing reports failed observer saves in `RepresentationCompletedEvent` and raises when every observer save fails (#1019)
|
|
||||||
- Assistant `reasoning_content` (DeepSeek / some OpenRouter models) is preserved across tool-loop turns. Previously the tool loop dropped thinking content before building the next assistant history message, so continuation requests failed. `reasoning_details` still takes precedence when both are present (#1034)
|
|
||||||
- `create_observations` now honors `DERIVER_DEDUPLICATE` instead of hardcoding `deduplicate=True`, matching the representation write path (#1018)
|
|
||||||
- `provider_params.timeout` is forwarded to the OpenAI-compatible embedding client, not just the LLM client (#1024)
|
|
||||||
- Conclusions semantic-search validation errors name the field and the constraint instead of returning a generic 422 (#960)
|
|
||||||
- OpenAI-compatible embedding calls request `encoding_format=float` so providers that default to base64 do not break pgvector inserts (#938)
|
|
||||||
- Gemini batch embedding works for `gemini-embedding-2*` models, which rejected the previous request shape (#745)
|
|
||||||
- MCP OAuth with no advertised scopes no longer defaults to read-only (which 403'd chat and search POSTs). Protected-resource metadata advertises read and write (#1004)
|
|
||||||
|
|
||||||
## [3.0.12] - 2026-08-10
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Session allowlist on the Dialectic and representation via a constrained `filters` body on `POST /peers/{peer_id}/chat` and `/representation`, supporting only the `session_id` key (a session id, a bare list, or `{"in": [...]}`). Unsupported keys and shapes are rejected with 422 rather than silently ignored, it composes with `session_id` (which must be included in the allowlist when both are given), and it is capped at 1,000 sessions per request. Enforcement is uniform and fail-closed at every recall chokepoint: scoped conclusion recall is restricted to `level == "explicit"` (dream-derived conclusions carry a single `session_name` but are synthesized across all sessions, so that stamp can't be scoped on), `get_reasoning_chain` is unavailable under an allowlist, and an empty allowlist short-circuits to empty results everywhere. Workspace keys pass the allowlist as-given; peer-scoped JWTs must be an active member of every allowlisted session (401 otherwise) (#882)
|
|
||||||
- Bare-list membership sugar in the filter DSL: `{"session_id": ["s1", "s2"]}` is now shorthand for `{"session_id": {"in": [...]}}` on regular columns generically. JSONB metadata columns are excluded and keep containment semantics. Strictly additive, since a bare list on a regular column previously compiled to a type-mismatched equality that matched nothing (#881)
|
|
||||||
- Optional structured outputs on the Dialectic: `response_format` (a JSON Schema with root type `object`) on peer chat makes `content` a JSON string conforming to that schema. Only a conservative subset of JSON Schema is supported, with DoS guards and non-recursive `$ref` support (#896)
|
|
||||||
- Combined tool calling and structured output in the LLM transport layer, with per-backend request shaping: OpenAI routes tool-carrying structured requests through `create()` with an explicit `json_schema` response format (`parse()` 500s on non-strict function tools), Anthropic skips the `{` JSON prefill when tools are present so `tool_use` blocks stay reachable, and Gemini injects a schema instruction into the final turn instead of using native `response_schema` (rejected alongside function calling before Gemini 3). All backends skip structured-output parsing on tool-call turns, which carry no consumable content (#907)
|
|
||||||
- `card_refresh` dream type: a lightweight dream that runs only the peer-card update, for event-driven refreshes such as membership changes and cold starts. Handled by a new `CardRefreshSpecialist` restricted to `get_recent_observations`, `search_memory`, and `update_peer_card` (no observation-mutating tools) with a tool-iteration cap of `min(6, DREAM.MAX_TOOL_ITERATIONS)`. `POST /v3/workspaces/{workspace_id}/schedule_dream` accepts `dream_type=card_refresh` plus a `rebuild` flag, which omits the existing card from the prompt so the specialist rebuilds it solely from observations present in the collection. Card refreshes never advance the omni dream guard pair (`last_dream_at` / `last_dream_document_count`) (#883)
|
|
||||||
- Full-fidelity LLM trace stream, with Langfuse as one projection over it: each call is captured once (`CapturedLLMCall`) and fanned out to a CloudEvents trace stream (`llm.call.traced` / `trace.content`) and a Langfuse exporter, both reconstructing trace → run → step → generation from the same source of truth. Adds `TELEMETRY_TRACE_PAYLOADS_ENABLED` (default `false`), `TELEMETRY_TRACE_MAX_BYTES` (default 262144, per-message cap with oversized content clipped), `TELEMETRY_TRACE_PURPOSES` (JSON list of `CallPurpose` values; empty means all), and `LANGFUSE_EXPORTER_MODE` (`exporter` by default; `inline` is kept for one release for side-by-side validation). Embedding calls are traced, dreamer branches nest under one dream trace, tool calls become spans under their step, and high-volume events are sampled deterministically. `TRACE_ENDPOINT` is dropped (#845)
|
|
||||||
- Redis Cluster support via `CACHE_CLUSTER` (for example GCP Memorystore for Redis Cluster), alongside a new `CACHE_LOCK_WAIT_CHECK_INTERVAL_SECONDS` (#905)
|
|
||||||
- `EMBEDDING_MODEL_CONFIG__MAX_BATCH_SIZE` caps texts per embedding request for OpenAI-compatible providers with smaller limits than OpenAI's, such as DashScope `text-embedding-v4` (10) and Alibaba Bailian `qwen3.7-text-embedding` (20). When unset, native provider defaults are preserved (OpenAI 2048, Gemini 100) (#983)
|
|
||||||
- Per-request provider timeouts via `provider_params.timeout` on any model config, validated at config load so a bad value fails at startup with the exact config path instead of surfacing per-request as a retried 500. Good values normalize to float seconds; Gemini's is converted to milliseconds (#832)
|
|
||||||
- `RepresentationCompletedEvent` now reports deduplication counts: `exact_dup_in_batch_count`, `exact_dup_existing_count`, `semantic_dup_rejected_count`, and `semantic_dup_replaced_count` (#910)
|
|
||||||
- OAuth discovery for MCP clients: the MCP worker serves `/.well-known/oauth-protected-resource` (RFC 9728) without auth so clients can discover the authorization server, and a 401 now carries `WWW-Authenticate: Bearer resource_metadata="..."` (exposed cross-origin) to start the flow (#923)
|
|
||||||
- Prometheus metrics for the immediate-embed fast path: tasks shed because `EMBEDDING_MAX_PENDING_EMBED_TASKS` was reached, and the current in-flight task count (#892)
|
|
||||||
- Docs: a detailed system architecture diagram, a Codex integration guide (#879), a structured-outputs page (#896), a section on filtering conclusions by reasoning level (#851), a health-check endpoint reference, and SDK updates (#867)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- **Breaking config change:** `DERIVER_REPRESENTATION_BATCH_MAX_TOKENS` is split into two settings that were previously conflated — `DERIVER_REPRESENTATION_BATCH_WORK_UNIT_TARGET_TOKENS` (default 512), the producer-side minimum a work unit accumulates before the deriver claims it, where `0` disables the gate; and `DERIVER_REPRESENTATION_BATCH_TARGET_INPUT_TOKENS` (default 1024), the consumer-side maximum context-window tokens per deriver LLM call. Deployments setting the old name must migrate (#889)
|
|
||||||
- The immediate-embed fast path now applies backpressure: `EMBEDDING_MAX_PENDING_EMBED_TASKS` (default 50) caps in-flight embed tasks, and once saturated, message creation skips the fast path entirely and the reconciler embeds on its next cycle. `0` disables the fast path (#892)
|
|
||||||
- Explicit-level documents are now kept session-pure, so memory can be built by copying explicit documents between collections. Enforcement refuses rather than rewrites: `create_documents` rejects explicit documents with a null `session_name`, exact dedup keys on (content, level, session-for-explicit), semantic dedup scopes candidate search to the same level and — for explicit documents — the same session, and the generic `create_observations` tool rejects `level='explicit'` outside message-ingestion (deriver) context. Derived levels keep cross-session consolidation (#883)
|
|
||||||
- Sentry's `before_send` filter is centralized as `default_before_send` in `src/telemetry/sentry.py` instead of living only in the API's `main.py`, so the deriver gets the same non-actionable-exception filtering. All Sentry events also carry a `namespace` tag for correlation (#934, #870)
|
|
||||||
- The minimal deriver's extraction examples no longer teach inferences its own output schema forbids. The `EXAMPLES` block demonstrated deriving a specific birthday from "I just had my 25th birthday last Saturday", deriving residence from a single visit ("I took my dog for a walk in NYC" → "alice lives in NYC"), and a "+ general knowledge" deductive output the deriver has no channel for. The replacements stay inside the schema's contract and teach the boundary: the dog/NYC message is kept and shown extracting correctly, and a separate example shows "lives in NYC" is valid when actually stated (#985)
|
|
||||||
- Dreamer specialists are instructed not to output summaries (#894)
|
|
||||||
- `session_name` is deprecated for scoping in favor of the session allowlist. It is not removed and not aliased: it also pins the query to one session, bypasses observer scoping, and drives session-history injection into the dialectic prompt, so it has no drop-in replacement (#882)
|
|
||||||
- The MCP worker no longer requires the `X-Honcho-User-Name` or `X-Honcho-Assistant-Name` headers (#923)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Session scoping was applied to only one of the three working-representation query paths: `session_name` reached the recent-documents query, but the semantic and most-derived paths ignored it, so `limit_to_session` leaked cross-session conclusions into perspectives. The allowlist is now threaded uniformly through all three paths and pushed down to pgvector and external vector stores (#881)
|
|
||||||
- Empty membership lists failed open in the vector-store filter builders, silently widening scope: LanceDB dropped empty `IN` clauses and Turbopuffer emitted a bare `In []` with undocumented semantics. Both now emit an explicit always-false predicate, and `_build_filter_conditions` checks `is not None` rather than truthiness so an empty list is no longer treated like `None` (#881, #882)
|
|
||||||
- Session-scoped CRUD helpers ignored the session allowlist entirely, so a caller could read a session the allowlist forbids. The API routes guarded this with a 422, but the dialectic tools call these CRUD functions directly and bypassed it. `_semantic_search_messages` (covering `search_messages` and `search_messages_temporal`), `grep_messages`, `get_messages_by_date_range`, `get_recent_history`, and `get_observation_context` now return `[]` when `session_name` is set and outside the allowlist (#882)
|
|
||||||
- The cache client logged the full Redis URL — including the password — at INFO and WARNING on every connection attempt and failure, exposing the live credential in container logs and downstream aggregation. Credentials are now redacted across userinfo, the `?password=` (redis-py) and `?secret=` (cashews) query params, scheme-less URLs whose password is invisible to `.port`/`.password` parsing, and malformed URLs, whose fallback previously echoed the raw input verbatim (#869)
|
|
||||||
- A `top_k` of `0` reached the vector store, where Turbopuffer rejects it with a 400 (`top_k must be between 1 and 10000`). A non-positive `top_k` now returns `[]` before the embedding call, and the semantic budget floors at 1 so an explicitly requested search isn't silently allocated zero (#970)
|
|
||||||
- Gemini clients had no HTTP timeout, so a stalled socket wedged the deriver worker's uvloop event loop, which the in-process reconciler shares. A 10-minute timeout is now set on both the Gemini LLM client and the Gemini embedding client (#903)
|
|
||||||
- Dreamer conclusions were dated to ingestion time rather than their latest source observation, and their timestamps are now normalized (#890)
|
|
||||||
- Langfuse I/O annotation was gated on `LANGFUSE_PUBLIC_KEY` instead of `langfuse_inline_enabled`, so in the default `exporter` mode it called `update_current_generation()` with no active span — logging "No active span in current context" roughly 14 times per dialectic run and building throwaway `model_dump` payloads on every LLM call. Separately, `AgentToolSummaryCreatedEvent` hardcoded `run_id="deriver"` / `iteration=0`, polluting `run_id` grouping in the CloudEvents stream with a phantom run; both fields are now optional and the resource id is keyed on `message_id:summary_type` (schema_version 2 → 3) (#845)
|
|
||||||
- Assistant tool calls were dropped from the captured trace stream for OpenAI and Gemini: `build_captured_messages` read only `{role, content, tool_call_id}`, but those providers keep tool calls outside `content`, so replayed tool-call turns landed as empty content and Gemini lost its text and tool results entirely. Tool calls are now normalized per provider into a unified `tool_calls` field and folded into the content hash. Gemini's `thought_signature` is bytes, so `model_dump(mode="json")` raised `UnicodeDecodeError` inside `emit_trace`, silently dropping whole tool-calling iterations from the trace stream (billing and Langfuse were unaffected); it is now base64-encoded on the telemetry path while replay keeps the raw bytes (#845)
|
|
||||||
- `EmbeddingClient.encoding` forced full client construction, raising "OpenAI API key is required" even though tiktoken needs no credentials. The document dedup tie-break only needs `.encoding` for token counting, so any test hitting that path failed in environments without embedding keys — notably CI for pull requests from forks. The encoding is now resolved from the configured model directly, falling back to `cl100k_base`, and the underlying client's encoding is reused only when it has already been constructed (#955)
|
|
||||||
- The Docker build failed under Podman because the uv build inputs weren't copied (#878)
|
|
||||||
- LanceDB was installed on macOS Intel, where it doesn't work. A PEP 508 marker excludes `darwin/x86_64` and the LanceDB vector-store import is wrapped so a misconfiguration surfaces as a clear config error (#496)
|
|
||||||
- Prompt checks requiring the literal token "json" for `json_object` mode are now satisfied in lowercase (#887)
|
|
||||||
- Reverted an unintended `RepresentationCompletedEvent` schema-version increment
|
|
||||||
- Documented preinstalling pgvector as a privileged role for deployments where the `DB_CONNECTION_URI` role deliberately cannot create extensions (managed Postgres, Kubernetes operators, NixOS). `CREATE EXTENSION IF NOT EXISTS vector` does not help there, because Postgres checks the privilege before checking whether the extension exists. Docker Compose is unaffected, since the bundled stack connects as the `postgres` superuser (#984)
|
|
||||||
|
|
||||||
## [3.0.11] - 2026-06-24
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `api_request_duration_seconds` Prometheus histogram tracking per-route request latency, labeled by method and endpoint (#837)
|
|
||||||
- LLM `provider_params` passthroughs (`extra_body` / `extra_headers` / `extra_query`) are now forwarded to the underlying provider transport across all backends, with shape validation that rejects non-mapping values (#821)
|
|
||||||
- `structured_output_mode` model-config option to use `json_object` mode for OpenAI-compatible providers that lack native Structured Outputs support (used by the deriver) (#820)
|
|
||||||
- OpenRouter app-attribution headers (`HTTP-Referer` / `X-Openrouter-Title`) are now sent on OpenAI-compatible clients when the configured base URL is OpenRouter, so requests are attributed to "Honcho" in OpenRouter's dashboard (#805)
|
|
||||||
- Langfuse traces are now tagged with user and session IDs for easier trace filtering (#814)
|
|
||||||
- `DERIVER_REPRESENTATION_BATCH_MAX_AGE_SECONDS` (default 1800s) lets sub-threshold representation work units flush once their oldest unprocessed queue item ages out. Set it to `0` to keep the legacy behavior where sub-threshold tails wait indefinitely unless `DERIVER_FLUSH_ENABLED=true` (#826)
|
|
||||||
- Conclusion responses now include a `level` field (`explicit`, `deductive`, `inductive`, `contradiction`); list/query endpoints support filtering by `level` via `filters`, with reserved filter keys protected from being overridden by user-supplied filters (#851)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Peer-scoped JWTs now get read-only access to the sessions their peer is an active member of (session context, summaries, peers, their own per-session config, search, and message reads). Session-scoped JWTs remain confined to their session and cannot reach peer routes (#679)
|
|
||||||
- Compacted Honcho's log output, with guarded ms/s metric formatting that falls back to a plain string for non-numeric values (#836)
|
|
||||||
- Sentry now drops noisy infra/scrape transactions: the reconciler opens a transaction only once a batch has rows (idle cycles emit none), and a `traces_sampler` returns `0.0` for `/metrics`, `/health`, `/openapi.json`, `/docs`, `/redoc`, and the deriver metrics server. `SENTRY.TRACES_SAMPLE_RATE` still governs real traffic (#834)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Peer- and session-scoped JWTs were effectively workspace-scoped: authorization walked the route's declared scope and fell through to a workspace match, so a `{w, p: alice}` token could act on any peer in the workspace. JWTs are now authorized by their narrowest claim and never widen to workspace access (#679)
|
|
||||||
- The keys API now rejects creating a peer- or session-scoped key without a workspace. Such keys were minted successfully but failed verification on every request (#679)
|
|
||||||
- Agent-supplied observation IDs carrying the display-format `id:` prefix are now normalized (prefix and trailing whitespace stripped) before `source_ids` are stored and on `get_reasoning_chain` lookups, fixing corrupted provenance links and broken reasoning-chain traversal (#795)
|
|
||||||
- Fixed a `create_tree` keyword-argument mismatch in the Dreamer's surprisal tree construction (#749)
|
|
||||||
- Providers that omit output-token counts (observed with Gemini on tool-loop completions) returned `output_tokens=None`, which raised a Pydantic validation error that aborted the call and crashed the Dreamer's induction phase before inductive conclusions were persisted. `None` is now coerced to `0` so token accounting degrades gracefully (#809)
|
|
||||||
- Document creation now performs exact (case-insensitive, whitespace-trimmed) content deduplication before the existing semantic dedup step: exact duplicates within a batch collapse to a single insert, and an exact match against a live document reinforces it (atomic `times_derived` increment) instead of creating a new row (#861)
|
|
||||||
- The OpenAI backend passed `tool_choice` through raw while the Anthropic and Gemini backends translate Honcho's canonical vocabulary to their native form, so on a mixed-provider fallback chain (for example Gemini primary → OpenAI backup) a canonical `"any"` reached OpenAI unchanged and was rejected as an invalid param. The OpenAI backend now converts it, mirroring the others: `any`/`required` → `required`, `auto`/`none` pass through, and a tool-name string or `{"name": ...}` dict becomes a function selection (#850)
|
|
||||||
- Langfuse `@observe` auto-capture serialized every argument of `honcho_llm_call_inner` into the generation span input, including `client_override` (a live `AsyncOpenAI`/`genai` client) and `selected_config` (which carries `api_key`). Auto-capture deep-copied the client into a half-constructed object whose teardown raised (`AsyncHttpxClientWrapper ... no attribute '_state'` on OpenAI, flooding stderr; `BaseApiClient ... no attribute '_http_options'` on Gemini), and it leaked `ModelConfig.api_key` into traces. Capture is now an explicit allowlist: `capture_input`/`capture_output` are disabled and curated, serializable input and output are stamped instead, with tuning knobs surfaced as `model_parameters` via a secret-bearing denylist and per-call token usage mirrored as `usage_details` (#849)
|
|
||||||
|
|
||||||
## [3.0.10] - 2026-06-15
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Messages are now embedded via a background task rather than blocking API request
|
|
||||||
- Read-only DB session mode (`get_read_db` / `tracked_db(..., read_only=True)`) so reads don't hold a transaction open across the work
|
|
||||||
- `CORS_ORIGINS` env var to configure CORS allowed origins without editing source; defaults match the prior hardcoded list, so self-hosted deployments behind custom domains can whitelist their frontend (#697)
|
|
||||||
- `scripts/generate_jwt.py` — utility for minting scoped or admin Honcho JWTs (`--admin`, `--workspace`/`--peer`/`--session`, `--expires` with human-friendly durations, `--print-only`) without calling the keys API (#757)
|
|
||||||
- `STALE_WORK_UNIT_CLEANUP_INTERVAL_SECONDS` (default 60s) — minimum jittered spacing between deriver stale-work-unit cleanup runs, so cleanup no longer runs on every seconds-scale poll (`0.0` keeps the legacy every-poll behavior) (#773)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Optimized the deriver and dreamer prompt cache prefixes to improve prompt-cache hit rates (#806)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `times_derived` is now properly reinforced when a duplicate conclusion is detected. It had been pinned at 1 for nearly every conclusion (the reject-new branch dropped the increment and the new-wins branch reset the count to 1), so `ORDER BY times_derived DESC` fell back to arbitrary heap order and froze stale conclusions to the front of injected context. Reinforcement is now an atomic increment and both most-derived queries gained a `created_at DESC` recency tiebreaker (#768)
|
|
||||||
- Webhook creation now correctly rejects private/internal IP addresses (#793)
|
|
||||||
|
|
||||||
## [3.0.9] - 2026-06-02
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Connection acquisition is now a single attempt with no server-side retry, on a vanilla `AsyncSession`. A new `DB_CONNECT_TIMEOUT_SECONDS` (default 2s) bounds the attempt so a saturated or unreachable pooler fails fast instead of holding a client connection open to re-knock. A saturated DB now surfaces to the caller — the API returns an error and the deriver backs off and retries on a later poll — which lets the pooler drain rather than amplifying saturation.
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Deriver poll jitter so instances that start together don't poll in lockstep: `DERIVER_POLLING_STARTUP_JITTER_SECONDS` (random delay before the first poll, default 30s) and `DERIVER_POLLING_JITTER_RATIO` (±fraction applied to every poll sleep, default 0.5). Both disable at `0.0`; the underlying backoff schedule is unchanged.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Reverted the connection-checkout retry and `HonchoAsyncSession` custom session introduced in 3.0.8. Removed the `DB_CONNECTION_RETRY_ENABLED` / `DB_CONNECTION_RETRY_MAX_DELAY_SECONDS` / `DB_CONNECTION_RETRY_BACKOFF_INITIAL_SECONDS` / `DB_CONNECTION_RETRY_BACKOFF_MAX_SECONDS` settings, the `db_connection_acquisitions{outcome=...}` Prometheus counter, and the `db.pool.acquire` Sentry span. Alerting built on `db_connection_acquisitions` should migrate to `db_pool_connections` / `db_queries_in_flight`.
|
|
||||||
|
|
||||||
## [3.0.8] - 2026-06-01
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Connection-checkout retry with bounded exponential backoff (tenacity) on `get_db`/`tracked_db`: transient transaction-pooler (Supavisor) rejections — SQLAlchemy `TimeoutError` and `OperationalError` — now retry with backoff instead of surfacing as 500s under client-connection saturation. Gated by
|
|
||||||
`DB_CONNECTION_RETRY_ENABLED` with configurable delay/backoff knobs; ~10s default budget (#758)
|
|
||||||
- `HonchoAsyncSession` — a lazy `AsyncSession` that checks out its pooled connection (with retry) on the first DB-touching call rather than at construction. Request handlers doing non-DB work (embedding, file, LLM) before their first query no longer pin a pooler connection across it. Only the checkout is retried;
|
|
||||||
the statement still runs exactly once, so writes are never duplicated (#758)
|
|
||||||
- Adaptive deriver queue polling: the poll interval backs off when the queue is idle or erroring (base → max, doubling each cycle) and snaps back to base the moment work is claimed, cutting steady-state query load against the DB. Gated by `DERIVER_POLLING_BACKOFF_ENABLED` with configurable max/multiplier (#758)
|
|
||||||
- New Prometheus `db_pool_connections` gauge (checked_out / checked_in / size / overflow), labeled `api`|`deriver`, registered in both the API lifespan and the deriver metrics server (#758)
|
|
||||||
- New Prometheus `db_connection_acquisitions{outcome=ok|retried|exhausted}` counter — the alertable early-warning signal that connection checkouts are retrying through pooler rejection, before requests start failing (#758)
|
|
||||||
- New Prometheus `db_queries_in_flight` gauge — statements actually executing on the wire (via SQLAlchemy cursor-execute events). Paired with `checked_out`, the gap reveals connections held but parked (the "idle in transaction during an external call" antipattern). Gated on `METRICS.ENABLED` for zero overhead when
|
|
||||||
off (#758)
|
|
||||||
- Explicit `SqlalchemyIntegration` in both the API and deriver Sentry inits; connection acquisition wrapped in a `db.pool.acquire` span with live pool stats captured on retry exhaustion (#758)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Default `POOL_TIMEOUT` lowered to 5s, with validation that it stays under the connection-retry budget when a pooled (non-null) `POOL_CLASS` is configured; `config.toml.example` and the v2/v3 configuration docs updated to match (#758)
|
|
||||||
- `HonchoAsyncSession` wraps every DB-touching session method (execute / scalar / scalars / flush / merge / refresh / commit / get / get_one / stream / stream_scalars / delete) so the lazy-checkout-with-retry guarantee has no holes; the acquired flag resets on `close()`/`reset()` so a reused session re-acquires on
|
|
||||||
next use (#758)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Roll the session back on a retryable checkout failure before retrying — a failed autobegin could otherwise leave it pending-rollback, making the next connection attempt raise instead of cleanly re-checking-out (#758)
|
|
||||||
- Guard `DBPoolCollector.collect()` so a pool-read/import hiccup can't raise and abort the entire `/metrics` scrape (Prometheus drops all metrics if any collector raises) (#758)
|
|
||||||
- Clamp the pool overflow gauge to ≥ 0 (it could report negative before the pool fills) (#758)
|
|
||||||
- Removed a double-sleep in the deriver idle poll so the backoff cap is a true cap rather than 2× (#758)
|
|
||||||
|
|
||||||
## [3.0.7] - 2026-05-21
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- New `src/llm/` module as the single owner of provider runtime: clients, backends, history adapters, tool loop, request builder, credentials, and caching policy (#459)
|
|
||||||
- `AttemptPlan` dataclass captures per-retry provider selection (client, model, reasoning_effort, thinking_budget_tokens, selected_config) and pins it across stream-final retries so streaming doesn't bounce back to primary after the tool loop has settled on fallback (#459)
|
|
||||||
- Gemini JSON-schema sanitizer for `function_declarations` — strips keywords Gemini's validator rejects (`additionalProperties`, `allOf`, etc.) while preserving semantics for all other backends (#459)
|
|
||||||
- Dreamer specialists derive `effective_max_tokens` from `model_config.max_output_tokens` with a per-specialist default fallback (#459)
|
|
||||||
- New cloudevent `LLMCallCompletedEvent` (`llm.call.completed`) fires once per provider hit with full cost-attribution context: transport/provider_label, model, token counts with cache breakdown, finish_reason, outcome, `is_final_attempt`, retry/fallback state, duration, tool-call shape, streaming flag, and agent correlation (`run_id` + iteration). Includes a `CallPurpose` closed enum (`deriver.representation`, `dialectic.answer`, `dream.deduction|induction`, `summary.short|long`) (#637)
|
|
||||||
- `RepresentationCompletedEvent` now carries `total_input_tokens` for full-trace cost attribution (#637)
|
|
||||||
- Per-emitter `honcho_version` injection on all CloudEvents plus emitter health metrics (#637)
|
|
||||||
- `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE` (default 1.0) — deterministic per-`run_id` sampler so an entire agent trace is kept or dropped together; aggregate envelopes bypass the sampler (#637)
|
|
||||||
- Deriver custom instructions: per-workspace/peer guidance threaded into the deriver prompt with a `MAX_CUSTOM_INSTRUCTIONS_TOKENS` budget (default 2000); deriver `MAX_INPUT_TOKENS` raised 23000 → 25000 to make room (#609)
|
|
||||||
- Configurable embedding dimensions: `EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE` (`auto`/`always`/`never`) controls whether the OpenAI `dimensions=` parameter is forwarded; `auto` (default) sends it when the operator explicitly set `EMBEDDING_VECTOR_DIMENSIONS` and the model is not on the known-rejecting allowlist (#678)
|
|
||||||
- New `honcho-cli` package — Python CLI for inspecting and managing peers, sessions, and configuration against a Honcho deployment (#424)
|
|
||||||
- `HONCHO_API_URL` env var support in the MCP Worker, enabling self-hosted Honcho deployments to point the Worker at their own instance instead of `https://api.honcho.dev` (#575)
|
|
||||||
- API ID `max_length` increased from 100 to 512 across `WorkspaceCreate`, `PeerCreate`, and `SessionCreate` to align the API contract with the underlying DB schema (#684)
|
|
||||||
- Regression tests covering fallback-config thinking-param reach, provider_params → extra_params boundary, OpenAI reasoning-model parameter routing, Gemini blocked finish_reason handling, and fail-fast `max_tool_iterations` validation (#459)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- All LLM orchestration moved out of `src/utils/clients.py` into `src/llm/` with modules split by responsibility (api, executor, tool_loop, runtime, registry, conversation, request_builder, credentials, caching, backends, history_adapters) (#459)
|
|
||||||
- Default `ModelConfig` factories (deriver, summary, dreamer specialists, dialectic levels) normalized to `openai/gpt-5.4-mini` with no extra parameters set by default; operators add transport/thinking overrides explicitly (#459)
|
|
||||||
- OpenAI reasoning-model routing widened via `_uses_max_completion_tokens` heuristic covering `gpt-5.x` and `o1/o3/o4` — these models receive `max_completion_tokens` instead of `max_tokens` (#459)
|
|
||||||
- Override client factories switched from unbounded `@cache` to `@lru_cache(maxsize=128)` for predictable memory growth on long-running processes (#459)
|
|
||||||
- `get_backend` now delegates to `client_for_model_config`, so the live-test path and production path share one missing-API-key validation (#459)
|
|
||||||
- Blocked Gemini responses (`SAFETY`, `RECITATION`, `PROHIBITED_CONTENT`, `BLOCKLIST`) raise `LLMError` in the streaming path too (previously only the non-streaming path), ensuring retry/fallback logic fires uniformly (#459)
|
|
||||||
- Transport-change env overrides now strip transport-specific thinking params (thinking_budget_tokens vs. reasoning_effort) during config merge, including at the dialectic-level merge, so switching from Anthropic → OpenAI doesn't leave orphaned Anthropic-only params that the OpenAI backend would reject (#459)
|
|
||||||
- `max_tool_iterations` out-of-range inputs now raise `ValidationException` instead of being silently clamped (#459)
|
|
||||||
- Public API schemas (`WorkspaceCreate`, `PeerCreate`, `SessionCreate`) and SDK validation (`api_types.py`, `validation.ts`) accept IDs up to 512 chars (was 100) (#684)
|
|
||||||
- Peer card prompts reframed as stable identity markers (replaces the prior "biographical/profile facts" language). Induction specialist is now opted out of peer card writes (`can_update_peer_card = False`) so only deduction touches the card (#686)
|
|
||||||
- Vector store queries no longer fetch embedding vectors — only document metadata is returned, reducing payload size and DB load (pgvector, lancedb, turbopuffer) (#682)
|
|
||||||
- Langfuse trace metadata now includes `namespace`, `model`, and `provider` so traces can be filtered by deployment slice (#565)
|
|
||||||
- Deriver: model-aware tokenizer (replaces the previously hardcoded encoding) and explicit guard on empty message content (#647)
|
|
||||||
- Dialectic level defaults now merge correctly with per-level overrides in `src/config` (#656)
|
|
||||||
- Default dialectic tool choice switched from forced/required to `auto` (#630)
|
|
||||||
- Vector sync given a substantial retry budget to tolerate transient embedding provider outages (#604)
|
|
||||||
- `AgentToolConclusionsDeletedEvent` payload now carries `levels` for parity with the rest of the conclusion event surface (#612)
|
|
||||||
- Turbopuffer vector store: `InternalServerError` caught and surfaced as a warning rather than a hard failure; unused `upsert_with_retry` and `VectorUpsertResult` removed; explicit silent and explicit-error paths for vector DB server errors (#561)
|
|
||||||
- Troubleshooting docs updated to reflect nested-env-var form for per-component thinking-budget overrides (#459)
|
|
||||||
- README refresh (#681)
|
|
||||||
- CLAUDE.md refreshed against the current `src/` layout (#680)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fallback `ModelConfig` temperature and `thinking_budget_tokens` reach the backend on the final retry — previously the primary's values were pre-populated into caller kwargs early and clobbered fallback values via `effective_config_for_call(update=...)` (#459)
|
|
||||||
- Stream-final retries pin to the `AttemptPlan` that succeeded rather than re-running provider selection through the outer `current_attempt` ContextVar (which could roll streaming back to primary after the tool loop had already switched to fallback) (#459)
|
|
||||||
- OpenAI structured-output calls continue to use `chat.completions.parse()` with strict schema enforcement, while tool-calling paths use `chat.completions.create()` without `strict:True` for broader proxy compatibility (OpenRouter, vLLM, Ollama) (#459)
|
|
||||||
- Gemini `cached_content` reuse keys now include `system_instruction` and `tool_config` so cache hits don't cross configurations that differ only in those fields (#459)
|
|
||||||
- Removed strict parameter validation for thinking params on Anthropic and OpenAI transports — was rejecting valid per-transport configs (#686)
|
|
||||||
- `reverse` query parameter is now honored on the v3 workspace list (`POST /v3/workspaces/list`), peer list (`POST /v3/workspaces/{workspace_id}/peers/list`), workspace-scoped session list (`POST /v3/workspaces/{workspace_id}/sessions/list`), and peer-scoped session list (`POST /v3/workspaces/{workspace_id}/peers/{peer_id}/sessions`). Honcho SDKs at 2.1.0+ were already sending `reverse=true` for these routes but the server silently ignored it. Ties on `created_at` now fall back to the internal nanoid `id` so ordering remains stable across pages (#685)
|
|
||||||
- LLM client factories now receive `base_url` from `LLMSettings` for default providers — previously the override path honored `base_url` but the default path didn't, so operators pointing at OpenAI-compatible proxies via `LLM__OPENAI_BASE_URL` were ignored (#643, fixes #641)
|
|
||||||
- Internal N+1 query in dialectic agent tool execution — collapsed per-iteration DB lookups into a single fetch (#652)
|
|
||||||
- Dreamer threshold and time-guard semantics: `check_and_schedule_dream` count filter now includes only `documents.level == 'explicit'` (dreamer-created levels are output, not input, and were inflating the threshold and creating a feedback loop); `last_dream_at` write relocated from `enqueue_dream` into `process_dream` so duplicate enqueues or failed runs no longer reset the 8-hour time guard (#573)
|
|
||||||
- Deriver: blank observations are filtered out before embedding (previously triggered noisy embedding calls and persisted empty rows); blank-observation filtering unified across tool paths (#615)
|
|
||||||
- Surprisal module: filter for level observations changed from `{"level": levels}` to `{"level": {"in": levels}}` — `apply_filter()` requires operator syntax, so the prior call silently returned 0 results and made the entire Surprisal phase of the Dream cycle a no-op (#581, fixes #559)
|
|
||||||
- Removed hardcoded `stop_sequences` override from Deriver `ModelConfig` (was clobbering operator-configured stop sequences) (#587)
|
|
||||||
- Removed stale `stop_sequences` from tests (#607)
|
|
||||||
- Embedding client: `embed()` now wraps single-string input in an array, restoring compatibility with OpenAI-compatible third-party providers that reject scalar input (#586)
|
|
||||||
- Docker Compose: deriver service startup gated on the API service healthcheck (prevents races where the deriver starts before the API has run migrations) (#689)
|
|
||||||
- Docker image: `HEALTHCHECK` directive removed from the shared base image — it probed an HTTP endpoint only the API serves, permanently marking deriver containers as unhealthy. Service-level health checks now belong in each service's own configuration (k8s readiness/liveness probes on the API Deployment only) (#530)
|
|
||||||
- `tests/unified`: `--test-dir`/`--test-file` arguments now use an argparse mutually-exclusive group instead of manual validation (#650)
|
|
||||||
- CrewAI example updated for the latest CrewAI protocol (#631)
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- `src/utils/clients.py` deleted; its responsibilities are split across `src/llm/registry.py`, `src/llm/credentials.py`, and the backend-specific modules (#459)
|
|
||||||
- `HEALTHCHECK` directive removed from the shared Docker image (#530)
|
|
||||||
|
|
||||||
## [3.0.6] - 2026-04-10
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Tightened transaction scopes across search, agent tools, queue manager, and webhook delivery to minimize DB connection hold time during external operations (#525)
|
|
||||||
- Search operations refactored to two-phase pattern — external work (embeddings, LLM calls) completes before opening a transaction (#525)
|
|
||||||
- Agent tool executor performs external operations before acquiring DB sessions (#525)
|
|
||||||
- Queue manager transaction scope reduced to only the critical section (#525)
|
|
||||||
- Webhook delivery no longer holds a DB session parameter (#525)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Session leakage in non-session-scoped dialectic chat calls (#526)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Health check endpoint (`/health`) for container orchestration and load balancer probes (#510)
|
|
||||||
|
|
||||||
## [3.0.5] - 2026-04-03
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- explicit rollback on all transactions to force connection closed
|
|
||||||
|
|
||||||
## [3.0.4] - 2026-04-02
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- JSONB metadata validation enforces 100 key limit and max depth of 5 (#419)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Schemas refactored from single `schemas.py` into `schemas/api.py`, `schemas/configuration.py`, and `schemas/internal.py` with backwards-compatible re-exports (#419)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Missing `deleted_at` filter on `RepresentationManager._query_documents_recent()` and `._query_documents_most_derived()` allowed soft-deleted documents to leak into the deriver's working representation (#456)
|
|
||||||
- `CleanupStaleItemsCompletedEvent` emitted spuriously when no queue item was actually deleted (#454)
|
|
||||||
- Empty JSON file uploads caused unhandled errors; now returns normalized error responses (#434)
|
|
||||||
- Memory leak: `_observation_locks` switched to `WeakValueDictionary` to prevent unbounded growth (#419)
|
|
||||||
- SQL injection in `dependencies.py`: parameterized `set_config` calls to prevent injection via request context (#419)
|
|
||||||
- NUL byte crashes: string inputs (message content, queries, peer cards) now stripped at schema level (#419)
|
|
||||||
- Filter recursion depth capped at 5 to prevent stack overflow (#419)
|
|
||||||
- Dedup-skipped observations now correctly reflected in created counts (#477)
|
|
||||||
- External vector store support for message search — routes queries through configured external vector store with oversampling and
|
|
||||||
deduplication to handle chunked embeddings (#479)
|
|
||||||
- Dialectic agent no longer holds a DB connection during LLM calls — embeddings are pre-computed before tool execution, DB sessions isolated in `extract_preferences`, `query_documents` no longer accepts a DB session parameter (#477)
|
|
||||||
|
|
||||||
## [3.0.3] - 2026-02-25
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Consolidated session context into a single DB session with 40/60 token budget allocation between summary and messages
|
|
||||||
- Observation validation via `ObservationInput` Pydantic schema with partial-success support and batch embedding with per-observation fallback
|
|
||||||
- Peer card hard cap of 40 facts with case-insensitive deduplication and whitespace normalization
|
|
||||||
- Safe integer coercion (`_safe_int`) for all LLM tool inputs to handle non-integer values like `"Infinity"`
|
|
||||||
- Embedding pre-computation and reuse across multiple search calls in dialectic and representation flows
|
|
||||||
- Peer existence validation in dialectic chat endpoints — raises ResourceNotFoundException instead of silently failing
|
|
||||||
- Logging filter to suppress noisy `GET /metrics` access logs
|
|
||||||
- Oolong long-context aggregation benchmark (synth and real variants, 1K–4M token context windows)
|
|
||||||
- MolecularBench fact quality evaluation (ambiguity, decontextuality, minimality scoring)
|
|
||||||
- CoverageBench information recall evaluation (gold fact extraction, coverage matching, QA verification)
|
|
||||||
- LoCoMo summary-as-context baseline evaluation
|
|
||||||
- Webhook delivery tests, dependency lifecycle tests, queue cleanup tests, summarizer fallback tests
|
|
||||||
- Parallel test execution via pytest-xdist with worker-specific databases
|
|
||||||
- `test_reasoning_levels.py` script for LOCOM dataset testing across reasoning levels
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Workspace deletion is now async — returns 202 Accepted, validates no active sessions (409 Conflict), cascade-deletes in background
|
|
||||||
- Redis caching layer now stores plain-dict instead of ORM objects, with v2-prefixed keys, storage, resilient `safe_cache_set`/`safe_cache_delete` helpers, and deferred post-commit cache invalidation
|
|
||||||
- All `get_or_create_*` CRUD operations now use savepoints (`db.begin_nested()`) instead of commit/rollback for race condition prevention
|
|
||||||
- Reconciler vector sync uses direct ORM mutation instead of batch parameterized UPDATE statements
|
|
||||||
- Summarizer enforces hard word limit in prompt and creates fallback text for empty summaries with `summary_tokens = 0`
|
|
||||||
- Blocked Gemini responses (SAFETY, RECITATION, PROHIBITED_CONTENT, BLOCKLIST) now raise `LLMError` to trigger retry/backup-provider logic
|
|
||||||
- Gemini client explicitly sets `max_output_tokens` from `max_tokens` parameter
|
|
||||||
- All deriver and metrics collector logging replaced with structured `logging.getLogger(__name__)` calls
|
|
||||||
- Dreamer specialist prompts updated to enforce durable-facts-only peer cards with max 40 entries and deduplication
|
|
||||||
- `GetOrCreateResult` changed from `NamedTuple` to `dataclass` with `async post_commit()` method
|
|
||||||
- FastAPI upgraded from 0.111.0 to 0.131.0; added pyarrow dependency
|
|
||||||
- Queue status filtering to only show user-facing tasks (representation, summary, dream); excludes internal infrastructure tasks
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- JWT timestamp bug — `JWTParams.t` was evaluated once at class definition time instead of per-instance
|
|
||||||
- Session cache invalidation on deletion was missing
|
|
||||||
- `get_peer_card()` now properly propagates `ResourceNotFoundException` instead of swallowing it
|
|
||||||
- `set_peer_card()` ensures peer exists via `get_or_create_peers()` before updating
|
|
||||||
- Backup provider failover with proper tool input type safety
|
|
||||||
- Removed `setup_admin_jwt()` from server startup
|
|
||||||
- Sentry coroutine detection switched from `asyncio.iscoroutinefunction` to `inspect.iscoroutinefunction`
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- `explicit.py` and `obex.py` benchmarks replaced by coverage.py and molecular.py
|
|
||||||
- Claude Code review automation workflow (`.github/workflows/claude.yml`)
|
|
||||||
- Coverage reporting from default pytest configuration
|
|
||||||
|
|
||||||
## [3.0.2] - 2026-01-27
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Documentation for reasoning_level and Claude Code plugin
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Gave dreaming sub-agents better prompting around peer card creation, tweaked overall prompts
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Added message-search fallback for memory search tool, necessary in fresh sessions
|
|
||||||
- Made FLUSH_ENABLED a config value
|
|
||||||
- Removed N+1 query in search_messages
|
|
||||||
|
|
||||||
## [3.0.1] - 2026-01-27
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Token counting in Explicit Agent Loop
|
|
||||||
- Backwards compatibility of queue items
|
|
||||||
|
|
||||||
## [3.0.0] - 2026-01-19
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Agentic Dreamer for intelligent memory consolidation using LLM agents
|
|
||||||
- Agentic Dialectic for query answering using LLM agents with tool use
|
|
||||||
- Reasoning levels configuration for dialectic (`minimal`, `low`, `medium`, `high`, `max`)
|
|
||||||
- Prometheus token tracking for deriver and dialectic operations
|
|
||||||
- n8n integration
|
|
||||||
- Cloud Events for auditable telemetry
|
|
||||||
- External Vector Store support for turbopuffer and lancedb with reconciliation flow
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- API route renaming for consistency
|
|
||||||
- Dreamer and dialectic now respect peer card configuration settings
|
|
||||||
- Observations renamed to Conclusions across API and SDKs
|
|
||||||
- Deriver to buffer representation tasks to normalize workloads
|
|
||||||
- Local Representation tasks to create singular QueueItems
|
|
||||||
- getContext endpoint to use `search_query` rather than force `last_user_message`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Dream scheduling bugs
|
|
||||||
- Summary creation when start_message_id > end_message_id
|
|
||||||
- Cashews upgrade to prevent NoScriptError
|
|
||||||
- Memory leak in `accumulate_metric` call
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Peer card configuration from message configuration; peer cards no longer created/updated in deriver process
|
|
||||||
|
|
||||||
## [2.5.1] - 2025-12-15
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Backwards compatibility for `message_ids` field in documents to handle legacy tuple format
|
|
||||||
|
|
||||||
## [2.5.0] - 2025-12-03
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Message level configurations
|
|
||||||
- CRUD operations for observations
|
|
||||||
- Comprehensive test cases for harness
|
|
||||||
- Peer level get_context
|
|
||||||
- Set Peer Card Method
|
|
||||||
- Manual dreaming trigger endpoint
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Configurations to support more flags for fine-grained control of the deriver, peer cards, summaries, etc.
|
|
||||||
- Working Representations to support more fine-grained parameters
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- File uploads to match `MessageCreate` structure
|
|
||||||
- Cache invalidation strategy
|
|
||||||
|
|
||||||
## [2.4.3] - 2025-11-20
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Redis caching to improve DB IO
|
|
||||||
- Backup LLM provider to avoid failures when a provider is down
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- QueueItems to use standardized columns
|
|
||||||
- Improved Deduplication logic for Representation Tasks
|
|
||||||
- More finegrained metrics for representation, summary, and peer card tasks
|
|
||||||
- DB constraint to follow standard naming conventions
|
|
||||||
|
|
||||||
## [2.4.2] - 2025-11-03
|
## [2.4.2] - 2025-11-03
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
@ -761,7 +308,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- `/list` endpoints to not require a request body
|
- `/list` endpoints to not require a request body
|
||||||
- `metamessage_type` to `label` with backwards compatibility
|
- `metamessage_type` to `label` with backwards compatability
|
||||||
- Database Provisioning to rely on alembic
|
- Database Provisioning to rely on alembic
|
||||||
- Database Session Manager to explicitly rollback transactions before closing
|
- Database Session Manager to explicitly rollback transactions before closing
|
||||||
the connection
|
the connection
|
||||||
|
|
@ -935,7 +482,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
- Authentication Middleware now implemented using built-in FastAPI Security
|
- Authentication Middleware now implemented using built-in FastAPI Security
|
||||||
module
|
module
|
||||||
- Get by name routes for users and collections now include "name" in slug
|
- Get by name routes for users and collections now include "name" in slug
|
||||||
- Python SDK moved to separate [repository](https://github.com/plastic-labs/honcho-python)
|
- Python SDK moved to separate [respository](https://github.com/plastic-labs/honcho-python)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
@ -1006,7 +553,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- session_data is now metadata
|
- session_data is now metadata
|
||||||
- session_data is a JSON field used python `dict` for compatibility
|
- session_data is a JSON field used python `dict` for compatability
|
||||||
|
|
||||||
## [0.0.2] — 2024-02-01
|
## [0.0.2] — 2024-02-01
|
||||||
|
|
||||||
|
|
|
||||||
283
CLAUDE.md
|
|
@ -6,15 +6,15 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||||
|
|
||||||
## What is Honcho?
|
## What is Honcho?
|
||||||
|
|
||||||
Honcho is an infrastructure layer for building AI agents with memory and social cognition. Its primary purposes include:
|
Honcho is an infrastructure layer for building AI agents with social cognition and theory of mind capabilities. Its primary purposes include:
|
||||||
|
|
||||||
- Imbuing agents with a sense of identity
|
- Imbuing agents with a sense of identity
|
||||||
- Personalizing user experiences through understanding user psychology
|
- Personalizing user experiences through understanding user psychology
|
||||||
- Providing a Chat Endpoint (the Dialectic agent) that injects personal context just-in-time
|
- Providing a Dialectic API that injects personal context just-in-time
|
||||||
- Supporting development of LLM-powered applications that adapt to end users
|
- Supporting development of LLM-powered applications that adapt to end users
|
||||||
- Enabling multi-peer sessions where multiple participants (users or agents) can interact
|
- Enabling multi-peer sessions where multiple participants (users or agents) can interact
|
||||||
|
|
||||||
Honcho leverages the inherent reasoning capabilities of LLMs to build coherent models of user psychology over time, enabling more personalized and effective AI interactions.
|
Honcho leverages the inherent theory-of-mind capabilities of LLMs to build coherent models of user psychology over time, enabling more personalized and effective AI interactions.
|
||||||
|
|
||||||
## Core Concepts
|
## Core Concepts
|
||||||
|
|
||||||
|
|
@ -32,27 +32,25 @@ Honcho uses a peer-based model where both users and agents are represented as "p
|
||||||
- **Peer** (formerly User): Any participant in the system (human or AI)
|
- **Peer** (formerly User): Any participant in the system (human or AI)
|
||||||
- **Session**: A conversation context that can involve multiple peers
|
- **Session**: A conversation context that can involve multiple peers
|
||||||
- **Message**: Data units that can represent communication between peers OR arbitrary data ingested by a peer to enhance its global representation
|
- **Message**: Data units that can represent communication between peers OR arbitrary data ingested by a peer to enhance its global representation
|
||||||
- **Collections & Documents**: Internal vector storage for peer representations. Collections are keyed by `(observer, observed)` peer pairs. Collections/Documents are not directly exposed via API, but the observations stored within them are exposed as **Conclusions** (see `/v3/.../conclusions` endpoints).
|
- **Collections & Documents**: Internal vector storage for theory-of-mind representations (not exposed via API)
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
### API Structure
|
### API Structure
|
||||||
|
|
||||||
All API routes follow the pattern: `/v3/{resource}/{id}/{action}`. Most "list/search" endpoints are `POST` so they can accept rich filter bodies.
|
All API routes follow the pattern: `/v1/{resource}/{id}/{action}`
|
||||||
|
|
||||||
- **Workspaces**: Create, list, update, search
|
- **Workspaces**: Create, list, update, search
|
||||||
- **Peers**: Create, list, update, chat (dialectic), messages, representation
|
- **Peers**: Create, list, update, chat (dialectic), messages, representation
|
||||||
- **Sessions**: Create, list, update, delete, clone, manage peers, get context
|
- **Sessions**: Create, list, update, delete, clone, manage peers, get context
|
||||||
- **Messages**: Create (batch up to 100), upload (file), list, get, update
|
- **Messages**: Create (batch up to 100), list, get, update
|
||||||
- **Conclusions**: Create, list, query (semantic search), delete — the API-facing name for observations stored in `(observer, observed)` collections
|
|
||||||
- **Keys**: Create scoped JWTs
|
- **Keys**: Create scoped JWTs
|
||||||
- **Webhooks**: Register endpoint, list, delete, test
|
|
||||||
|
|
||||||
### Key Features
|
### Key Features
|
||||||
|
|
||||||
#### Chat Endpoint (Dialectic agent) (`/peers/{peer_id}/chat`)
|
#### Dialectic API (`/peers/{peer_id}/chat`)
|
||||||
|
|
||||||
- Provides bespoke responses informed by the representation
|
- Provides theory-of-mind informed responses
|
||||||
- Integrates long-term facts from vector storage
|
- Integrates long-term facts from vector storage
|
||||||
- Supports streaming responses
|
- Supports streaming responses
|
||||||
- Configurable LLM providers
|
- Configurable LLM providers
|
||||||
|
|
@ -61,11 +59,18 @@ All API routes follow the pattern: `/v3/{resource}/{id}/{action}`. Most "list/se
|
||||||
|
|
||||||
1. Messages created via API (batch or single)
|
1. Messages created via API (batch or single)
|
||||||
2. Enqueued for background processing:
|
2. Enqueued for background processing:
|
||||||
- `representation`: Update peer's context
|
- `representation`: Update peer's theory of mind
|
||||||
- `summary`: Create session summaries
|
- `summary`: Create session summaries
|
||||||
3. Session-based queue processing ensures order
|
3. Session-based queue processing ensures order
|
||||||
4. Results stored internally in vector DB
|
4. Results stored internally in vector DB
|
||||||
|
|
||||||
|
#### Theory of Mind System
|
||||||
|
|
||||||
|
- Multiple implementation methods (conversational, single_prompt, long_term)
|
||||||
|
- Facts extracted from messages and stored in collections
|
||||||
|
- Representations combine short-term inference with long-term facts
|
||||||
|
- Configurable via peer and session feature flags
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
- Hierarchical config: config.toml + environment variables
|
- Hierarchical config: config.toml + environment variables
|
||||||
|
|
@ -86,27 +91,6 @@ All API routes follow the pattern: `/v3/{resource}/{id}/{action}`. Most "list/se
|
||||||
- Typechecking: `uv run basedpyright`
|
- Typechecking: `uv run basedpyright`
|
||||||
- Format code: `uv run ruff format src/`
|
- Format code: `uv run ruff format src/`
|
||||||
|
|
||||||
### SDK Testing
|
|
||||||
|
|
||||||
#### TypeScript SDK
|
|
||||||
|
|
||||||
**🚨 DO NOT RUN `bun test` DIRECTLY. IT WILL NOT WORK. 🚨**
|
|
||||||
|
|
||||||
The TypeScript SDK tests require a running Honcho server with database and Redis. Running `bun test` alone will fail immediately because there's no server. The tests are orchestrated via pytest which handles all the infrastructure setup.
|
|
||||||
|
|
||||||
**The ONLY way to run TypeScript SDK tests:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# From the monorepo root (not from sdks/typescript/)
|
|
||||||
uv run pytest tests/ -k typescript
|
|
||||||
```
|
|
||||||
|
|
||||||
**To type-check the TypeScript SDK (this is fine to run directly):**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd sdks/typescript && bun run tsc --noEmit
|
|
||||||
```
|
|
||||||
|
|
||||||
### Code Style
|
### Code Style
|
||||||
|
|
||||||
- Follow isort conventions with absolute imports preferred
|
- Follow isort conventions with absolute imports preferred
|
||||||
|
|
@ -115,171 +99,73 @@ cd sdks/typescript && bun run tsc --noEmit
|
||||||
- Line length: 88 chars (Black compatible)
|
- Line length: 88 chars (Black compatible)
|
||||||
- Explicit error handling with appropriate exception types
|
- Explicit error handling with appropriate exception types
|
||||||
- Docstrings: Use Google style docstrings
|
- Docstrings: Use Google style docstrings
|
||||||
- **Never hold a DB session during external calls** (LLM, embedding, HTTP). If a function needs both a DB session and an external call result, compute the external result first and pass it as a parameter. This avoids tying up DB connections during slow network I/O. Use `tracked_db` for short-lived, DB-only operations; pass a shared session when multiple DB-only calls can reuse one connection.
|
|
||||||
- **Never write through a read-only session** (`tracked_db(..., read_only=True)`, `get_read_db`, `ReadSessionLocal`). These run in AUTOCOMMIT mode with no transaction: writes are NOT blocked by the database — they silently commit immediately, and `begin_nested()` savepoints break. There is no runtime guard; this is enforced by convention only. Use `read_only=True` strictly for SELECT-only windows; anything that mutates (including get-or-create paths) must use a regular write session.
|
|
||||||
|
|
||||||
#### Multi-row locking and deadlocks
|
|
||||||
|
|
||||||
Tables written concurrently by more than one worker — `documents` (deriver, dreamer, scope backfill/removal, reconciler) and `queue` (every deriver replica) — deadlock when two writers touch an overlapping row set in different orders. Rules:
|
|
||||||
|
|
||||||
- **A multi-row `SELECT ... FOR UPDATE` MUST carry an explicit `ORDER BY <pk>`.** Without it Postgres locks in scan order, which differs per plan, so two writers with overlapping sets can cycle. `_apply_document_row_updates` in `src/crud/document.py` is the reference implementation.
|
|
||||||
- **`WHERE id IN (...)` does NOT impose an order**, so sorting the Python list is a no-op — the list order is discarded and the planner picks `Bitmap Heap Scan` (ctid order), `Index Scan` (id order), or `Seq Scan` per invocation. Deterministic ordering requires either a preceding `SELECT ... ORDER BY id FOR UPDATE` or `WHERE id IN (SELECT id ... ORDER BY id FOR UPDATE)`.
|
|
||||||
- **`Document.id` is a random nanoid** (`models.py`), so id order is uncorrelated with physical order — an unordered predicate `UPDATE`/`DELETE` is roughly a coin flip against an id-ordered locker per row pair, not a rare edge case. (`QueueItem.id` is an integer identity, so there id order is also chronological.)
|
|
||||||
- **Prefer no lock at all.** A single `UPDATE ... WHERE <predicate>` acquires row locks as it writes and has no separate lock phase to get wrong. Reach for `FOR UPDATE` only when a value must be read, computed in Python, and written back — that read-modify-write is the only reason `_apply_document_row_updates` locks (it replaced a server-side `func.greatest()`), and `populate_existing=True` is required with it so the identity map doesn't serve a stale pre-lock value. Server-side expressions (`func.greatest`, the JSONB `-` operator) avoid the lock entirely; see `_clear_work_unit_retry_attempts` in `src/deriver/queue_manager.py`.
|
|
||||||
- `FOR UPDATE SKIP LOCKED` (the reconciler's claim pattern) never waits, so it cannot be a deadlock partner — but holding those locks across an external call still stalls other writers. See the "never hold a DB session during external calls" rule above.
|
|
||||||
|
|
||||||
#### Auth scoping
|
|
||||||
|
|
||||||
- **`allow_member_read=True` (in `require_auth(...)`) is read-only — NEVER set it on a route that mutates state.** It lets a peer-scoped key reach a session route when its peer is an active member of the session, so on a mutating route it would hand any session member write access (message injection, config mutation, deletion). HTTP method is not a reliable read/write signal here (some read routes use POST for a richer body), so this is enforced by an explicit allowlist in `tests/routes/test_auth_route_policy.py` — adding the flag to a new route fails that test until you consciously add the route to `EXPECTED_MEMBER_READ_ROUTES`, and you must never add a mutating method there.
|
|
||||||
- **When a member-read route is keyed by another sub-resource** (e.g. `peers/{peer_id}/config`), the handler must additionally confirm a peer-scoped caller only reads its OWN resource (`jwt_params.p == peer_id`, else raise `AuthenticationException`). Membership grants session access, not access to a co-member's data. See `get_peer_config` in `src/routers/sessions.py`.
|
|
||||||
|
|
||||||
### Runtime Architecture
|
|
||||||
|
|
||||||
Honcho runs as two cooperating processes that share a Postgres database and Redis cache:
|
|
||||||
|
|
||||||
- **API server** (`uv run fastapi dev src/main.py`) — handles HTTP, enqueues background work, returns immediately. Hosts the **Dialectic** agent inline (synchronous tool loop during chat requests).
|
|
||||||
- **Deriver worker** (`uv run python -m src.deriver`) — long-running queue consumer (uvloop). Runs the **Deriver**, **Summarizer**, and **Dreamer** off the queue. Can run multiple instances (`DERIVER_WORKERS`). Also hosts an in-process **Reconciler scheduler** (`src/reconciler/`) that periodically embeds messages with `sync_state='pending'` in `MessageEmbedding` and cleans up stale queue items — embedding generation is decoupled from message creation by design.
|
|
||||||
|
|
||||||
### Agent Architecture
|
|
||||||
|
|
||||||
Honcho uses several specialized LLM agents. They share tool definitions and the LLM client abstraction in `src/utils/agent_tools.py` + `src/llm/`.
|
|
||||||
|
|
||||||
> **Terminology:** what users see as **conclusions** (the public API surface and the term we use in documentation) is called **observations** in code symbols — `create_observations`, `delete_observations`, `get_observation_context`, etc. Doc prose below uses "conclusions"; references to actual code symbols stay as "observations."
|
|
||||||
|
|
||||||
#### 1. Deriver (`src/deriver/`)
|
|
||||||
|
|
||||||
**Role**: Memory formation through content ingestion.
|
|
||||||
|
|
||||||
The Deriver processes batches of incoming messages and extracts conclusions about peers. The current architecture is "minimal deriver" — a **single LLM call** per batch using structured output, not an agentic tool loop. This trades flexibility for cost and predictability.
|
|
||||||
|
|
||||||
- **Trigger**: Messages enqueued by `src/deriver/enqueue.py` on message create; consumed by `src/deriver/queue_manager.py` → `consumer.process_item()` → `deriver.process_representation_tasks_batch()`.
|
|
||||||
- **Output**: Explicit conclusions (direct facts) and deductive conclusions (inferences) saved to `(observer, observed)` collections.
|
|
||||||
- **Entry point**: `src/deriver/__main__.py` → `queue_manager.main()`.
|
|
||||||
- **Prompts**: `src/deriver/prompts.py` (`minimal_deriver_prompt`).
|
|
||||||
- **Custom instructions**: per-workspace/peer guidance can be threaded into the prompt via reasoning configuration; `DERIVER__MAX_CUSTOM_INSTRUCTIONS_TOKENS` caps the addition (default 2000) and `DERIVER__MAX_INPUT_TOKENS` defaults to 25000 to make room.
|
|
||||||
|
|
||||||
#### 2. Dialectic (`src/dialectic/`)
|
|
||||||
|
|
||||||
**Role**: Analysis and recall for answering queries.
|
|
||||||
|
|
||||||
The Dialectic answers questions about peers by strategically gathering context from memory. It is the only tool-using agent on the synchronous request path — it loops over `DIALECTIC_TOOLS` until it has enough context to answer. (The Dreamer specialists also use tools, but run off the queue.)
|
|
||||||
|
|
||||||
- **Trigger**: API call to `POST /v3/.../peers/{peer_id}/chat`.
|
|
||||||
- **Tools** (see `DIALECTIC_TOOLS` in `src/utils/agent_tools.py`): `search_memory`, `search_messages`, `get_observation_context`, `grep_messages`, `get_messages_by_date_range`, `search_messages_temporal`, `get_reasoning_chain`. At the `minimal` reasoning level, a reduced set (`DIALECTIC_TOOLS_MINIMAL`) is used: just `search_memory` + `search_messages`.
|
|
||||||
- **Reasoning levels**: 5 tiers — `minimal`, `low`, `medium`, `high`, `max` — each with its own model config (see `DialecticLevelSettings` in `src/config.py`).
|
|
||||||
- **Output**: Natural language response grounded in gathered context. Supports SSE streaming.
|
|
||||||
- **Entry point**: `src/dialectic/chat.py` → `agentic_chat()` / `agentic_chat_stream()` → `DialecticAgent` (in `src/dialectic/core.py`).
|
|
||||||
|
|
||||||
#### 3. Dreamer (`src/dreamer/`)
|
|
||||||
|
|
||||||
**Role**: Consolidation and self-improvement of memory.
|
|
||||||
|
|
||||||
The Dreamer is an orchestrated multi-specialist system that runs during scheduled "dreams" to consolidate conclusions and build reasoning trees.
|
|
||||||
|
|
||||||
- **Trigger**: Scheduled via `DreamScheduler` (`src/dreamer/dream_scheduler.py`) or explicit dream task on the queue.
|
|
||||||
- **Strategy**: Surprisal-based prioritization (`src/dreamer/surprisal.py`) selects which conclusions to expand. The orchestrator (`orchestrator.run_dream`) runs two specialist phases:
|
|
||||||
1. **DeductionSpecialist** (`specialists.py`) — produces deductive conclusions from explicit conclusions. Tools: `get_recent_observations`, `search_memory`, `search_messages`, `create_observations_deductive`, `delete_observations`, `update_peer_card`.
|
|
||||||
2. **InductionSpecialist** — produces inductive conclusions from explicit + deductive conclusions. Tools: same discovery set + `create_observations_inductive`, `update_peer_card`.
|
|
||||||
- **Reasoning trees** (`src/dreamer/trees/`, migration `f1a2b3c4d5e6_add_reasoning_tree_columns`): each conclusion links to its premises and downstream conclusions, enabling `get_reasoning_chain` traversal at recall time.
|
|
||||||
- **Output**: Deductive/inductive conclusions, consolidated redundancies, updated peer cards.
|
|
||||||
- **Entry point**: `src/dreamer/orchestrator.py` → `process_dream()` (the package-level export from `src/dreamer/__init__.py`), which wraps `run_dream()`.
|
|
||||||
|
|
||||||
#### 4. Summarizer (`src/utils/summarizer.py`)
|
|
||||||
|
|
||||||
**Role**: Two-tier session summarization (direct LLM call — no agentic tools).
|
|
||||||
|
|
||||||
- **Trigger**: Runs as part of the queue pipeline alongside representation tasks.
|
|
||||||
- **Tiers**: short summary every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` messages (default 20); long summary every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60). Token caps configurable via `SUMMARY_MAX_TOKENS_SHORT` / `SUMMARY_MAX_TOKENS_LONG`.
|
|
||||||
|
|
||||||
#### Shared Agent Infrastructure
|
|
||||||
|
|
||||||
- **Tool definitions** (`src/utils/agent_tools.py`): unified `TOOLS` dict; per-agent lists (`DIALECTIC_TOOLS`, `DIALECTIC_TOOLS_MINIMAL`, `DREAMER_TOOLS`, `DEDUCTION_SPECIALIST_TOOLS`, `INDUCTION_SPECIALIST_TOOLS`).
|
|
||||||
- **LLM subsystem** (`src/llm/`): provider-agnostic `honcho_llm_call()`. Backends in `src/llm/backends/` (`anthropic.py`, `gemini.py`, `openai.py`). Includes prompt caching (`caching.py`), structured output (`structured_output.py`), tool loop (`tool_loop.py`), history adapters for cross-provider message formats, and a model registry. Per-retry provider selection is pinned via an `AttemptPlan` so stream-final retries don't bounce back to primary after the tool loop has settled on fallback.
|
|
||||||
- **Per-agent model config**: each agent has its own `MODEL_CONFIG` in `src/config.py` with fallback chains (see `ConfiguredModelSettings`, `FallbackModelSettings`).
|
|
||||||
- **Telemetry**: cloudevents in `src/telemetry/events/` cover API routes, dialectic, dream, deletion, reconciliation, representation, and per-call LLM accounting (`llm.py` — `LLMCallCompletedEvent` fires once per provider hit with full cost-attribution context). High-volume events are sampled deterministically per `run_id` via `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE`.
|
|
||||||
- **Prometheus metrics** (`src/telemetry/prometheus/`): every metric carries a `namespace` label and every recorder is fail-soft (a metrics error never propagates into a request or a worker loop). Counter children with a *bounded* label domain are zero-initialized per process at startup — `initialize_bounded_metrics(instance_type=...)`, called from the `src/main.py` lifespan (`api`) and `src/deriver/__main__.py` (`deriver`) — so an absent series means a broken scrape rather than "nothing happened". Two consequences worth knowing before touching telemetry:
|
|
||||||
- **Adding a `BaseEvent` subclass requires adding its `_event_type` to `ALL_EVENT_TYPES`** in `src/telemetry/events/__init__.py` (and to `HIGH_VOLUME_EVENT_TYPES` if `_volume_class == "high_volume"`). Enforced by the drift guards in `tests/telemetry/test_metric_zero_init.py`, which assert set-equality against the discovered subclasses.
|
|
||||||
- **A service-wide, non-additive gauge must be refreshed by every replica on its own timer**, and aggregated with `max()`/`avg()`, never `sum()`. `message_embeddings_pending` is the example: it reports a DB-global count, so it is driven from `ReconcilerScheduler._scheduler_loop` (runs on all replicas) rather than from the work-unit-deduped reconciliation cycle — otherwise, combined with the zero-init, every replica that never won the work unit would export a confident permanent `0`.
|
|
||||||
|
|
||||||
### Project Structure
|
### Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
├── main.py # FastAPI app: middleware, routers, lifespan, exception handlers
|
├── main.py # FastAPI app setup with middleware and exception handlers
|
||||||
├── models.py # SQLAlchemy ORM models (Workspace/Peer/Session/Message/
|
├── models.py # SQLAlchemy ORM models with proper type annotations
|
||||||
│ # MessageEmbedding/Collection/Document/QueueItem/...)
|
├── schemas.py # Pydantic validation schemas for API
|
||||||
├── config.py # Pydantic-settings configuration (very large; see README)
|
├── config.py # Configuration management
|
||||||
├── db.py # Engine + session/context management (request_context var)
|
├── db.py # Database connection and session management
|
||||||
├── dependencies.py # FastAPI DI (tracked_db, etc.)
|
├── dependencies.py # Dependency injection (DB sessions)
|
||||||
├── exceptions.py # Custom exception types (HonchoException + subclasses)
|
├── exceptions.py # Custom exception types
|
||||||
├── security.py # JWT authentication
|
├── security.py # JWT authentication
|
||||||
├── embedding_client.py # Embedding provider client (configurable dimensions
|
├── embedding_client.py # Embedding service client
|
||||||
│ # via EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE)
|
├── crud/ # Database operations
|
||||||
├── schemas/ # Pydantic schemas
|
│ ├── __init__.py
|
||||||
│ ├── api.py # Public API request/response schemas
|
│ ├── collection.py # Collection CRUD operations
|
||||||
│ ├── configuration.py # Per-resource configuration schemas
|
│ ├── deriver.py # Deriver-related CRUD operations
|
||||||
│ └── internal.py # Internal-only schemas (queue payloads, etc.)
|
│ ├── document.py # Document CRUD operations
|
||||||
├── crud/ # Per-resource DB operations
|
│ ├── message.py # Message CRUD operations
|
||||||
│ ├── collection.py, deriver.py, document.py, message.py
|
│ ├── peer.py # Peer CRUD operations
|
||||||
│ ├── peer.py, peer_card.py, representation.py (RepresentationManager)
|
│ ├── peer_card.py # Peer Card CRUD operations
|
||||||
│ ├── session.py, webhook.py, workspace.py
|
│ ├── representation.py # RepresentationManager and representation operations
|
||||||
├── routers/ # FastAPI route handlers (all under /v3)
|
│ ├── session.py # Session CRUD operations
|
||||||
│ ├── workspaces.py, peers.py (dialectic /chat lives here), sessions.py
|
│ ├── webhook.py # Webhook CRUD operations
|
||||||
│ ├── messages.py, conclusions.py, keys.py, webhooks.py
|
│ └── workspace.py # Workspace CRUD operations
|
||||||
├── dialectic/ # Dialectic agent — runs inline per chat request
|
├── dialectic/ # Dialectic API implementation
|
||||||
│ ├── chat.py # agentic_chat() / agentic_chat_stream()
|
│ ├── __init__.py
|
||||||
│ ├── core.py # DialecticAgent (the tool-loop driver)
|
│ ├── chat.py # Chat functionality
|
||||||
│ └── prompts.py
|
│ ├── prompts.py # Prompt templates
|
||||||
├── deriver/ # Background queue consumer (separate process)
|
│ └── utils.py # Dialectic utilities
|
||||||
│ ├── __main__.py # `python -m src.deriver` entry point
|
├── routers/ # API endpoints
|
||||||
│ ├── queue_manager.py # QueueManager + main() loop
|
│ ├── workspaces.py
|
||||||
│ ├── consumer.py # process_item dispatcher (representation / deletion / reconciler)
|
│ ├── peers.py
|
||||||
│ ├── deriver.py # "minimal deriver" — single-LLM-call batch processor
|
│ ├── sessions.py
|
||||||
│ ├── enqueue.py # API → queue producer
|
│ ├── messages.py
|
||||||
│ └── prompts.py
|
│ ├── keys.py
|
||||||
├── dreamer/ # Memory consolidation (runs off the queue)
|
│ └── webhooks.py # Webhook endpoints
|
||||||
│ ├── orchestrator.py # run_dream() / process_dream()
|
├── deriver/ # Background processing system
|
||||||
│ ├── specialists.py # DeductionSpecialist + InductionSpecialist
|
│ ├── __init__.py
|
||||||
│ ├── dream_scheduler.py
|
│ ├── __main__.py # Deriver entry point
|
||||||
│ ├── surprisal.py # Surprisal-based conclusion prioritization
|
│ ├── consumer.py # Message consumer
|
||||||
│ └── trees/ # Reasoning-tree primitives
|
│ ├── deriver.py # Main deriver logic
|
||||||
├── reconciler/ # In-process scheduler hosted by the deriver worker
|
│ ├── enqueue.py # Queue operations
|
||||||
│ ├── scheduler.py # ReconcilerScheduler (started from queue_manager.py)
|
│ ├── prompts.py # Deriver prompts
|
||||||
│ ├── sync_vectors.py # Embeds MessageEmbedding rows with sync_state='pending'
|
│ ├── queue_manager.py # Queue management
|
||||||
│ └── queue_cleanup.py # Removes stale queue items
|
│ ├── queue_payload.py # Queue payload schemas
|
||||||
├── llm/ # Provider-agnostic LLM client subsystem
|
│ └── utils.py # Deriver utilities
|
||||||
│ ├── api.py, backend.py, executor.py, runtime.py, registry.py
|
├── utils/ # Utilities
|
||||||
│ ├── caching.py, structured_output.py, tool_loop.py, conversation.py
|
│ ├── __init__.py
|
||||||
│ ├── history_adapters.py, request_builder.py, credentials.py, types.py
|
│ ├── clients.py # LLM client abstraction
|
||||||
│ └── backends/ # anthropic.py, gemini.py, openai.py
|
│ ├── files.py # File handling utilities
|
||||||
├── cache/ # Redis cache abstraction (cashews-backed)
|
│ ├── filter.py # Query filtering utilities
|
||||||
│ └── client.py
|
│ ├── formatting.py # Message formatting utilities
|
||||||
├── vector_store/ # Optional external vector stores (pgvector is default,
|
│ ├── logging.py # Logging configuration
|
||||||
│ │ # implemented via MessageEmbedding/Document in models+crud)
|
│ ├── search.py # Search functionality
|
||||||
│ ├── lancedb.py
|
│ ├── shared_models.py # Shared data models
|
||||||
│ └── turbopuffer.py
|
│ ├── summarizer.py # Session summarization
|
||||||
├── telemetry/ # Observability
|
│ └── types.py # Type definitions
|
||||||
│ ├── emitter.py # CloudEvents emitter
|
└── webhooks/ # Webhook system
|
||||||
│ ├── logging.py # Logging helpers + route-template extraction
|
├── events.py # Webhook event definitions
|
||||||
│ ├── metrics_collector.py, reasoning_traces.py, sentry.py
|
├── webhook_delivery.py # Webhook delivery logic
|
||||||
│ ├── events/ # Event type definitions
|
└── README.md # Webhook documentation
|
||||||
│ └── prometheus/ # Prometheus metric definitions
|
|
||||||
├── utils/ # Cross-cutting utilities
|
|
||||||
│ ├── agent_tools.py # Tool definitions + per-agent tool lists
|
|
||||||
│ ├── summarizer.py # Two-tier session summarizer
|
|
||||||
│ ├── representation.py # Representation formatting (distinct from crud/representation.py)
|
|
||||||
│ ├── search.py, filter.py, formatting.py
|
|
||||||
│ ├── tokens.py # tiktoken-based counting
|
|
||||||
│ ├── work_unit.py, queue_payload.py
|
|
||||||
│ ├── config_helpers.py, json_parser.py, files.py
|
|
||||||
│ └── types.py
|
|
||||||
└── webhooks/ # Webhook delivery
|
|
||||||
├── events.py
|
|
||||||
└── webhook_delivery.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Tests in pytest with fixtures in tests/conftest.py; subdirs mirror src/ (`tests/deriver/`, `tests/dialectic/`, etc.) plus `tests/bench/` (perf benchmarks), `tests/integration/`, `tests/live_llm/` (gated by `--live-llm`), and `tests/unified/` (the unified runner).
|
- Tests in pytest with fixtures in tests/conftest.py
|
||||||
- Use environment variables via python-dotenv (.env). Config precedence: env > .env > config.toml > defaults.
|
- Use environment variables via python-dotenv (.env)
|
||||||
|
|
||||||
### Database Design
|
### Database Design
|
||||||
|
|
||||||
|
|
@ -292,18 +178,13 @@ src/
|
||||||
|
|
||||||
### Key Architectural Decisions
|
### Key Architectural Decisions
|
||||||
|
|
||||||
1. **Peer Paradigm**: humans and AI agents are unified as "Peers"; many-to-many with Sessions. Internal vector storage (Collections/Documents) is keyed by `(observer, observed)` peer pairs — the same mechanism powers self-representation (`observer == observed`) and cross-peer modeling.
|
1. **Multi-Peer Sessions**: Sessions can have multiple participants with different observation settings
|
||||||
2. **Multi-Peer Sessions**: Sessions can have multiple participants with different observation settings.
|
2. **Flexible Theory of Mind**: Pluggable ToM implementations (conversational, single_prompt, long_term)
|
||||||
3. **API server / worker split**: API enqueues, deriver worker process consumes. Never block HTTP on LLM work. The Reconciler runs as an in-process scheduler inside the deriver, handling async embedding sync and queue cleanup.
|
3. **Background Processing**: Async queue system for expensive operations
|
||||||
4. **"Minimal" deriver**: memory formation is a single structured-output LLM call per batch, not an agentic tool loop. Predictable cost, lower latency. The Dialectic is the one true tool-using agent.
|
4. **Provider Abstraction**: Model client supports multiple LLM providers
|
||||||
5. **Provider-agnostic LLM layer** (`src/llm/`): all model calls go through `honcho_llm_call()`. Backends (`anthropic`, `gemini`, `openai`) sit behind a registry; per-agent `MODEL_CONFIG` with fallback chains is resolved at call time.
|
5. **Scoped Authentication**: JWTs can be scoped to workspace, peer, or session level
|
||||||
6. **Dialectic reasoning tiers**: 5 levels (`minimal` → `max`); each level has its own model config and tool set (`minimal` uses a reduced toolset).
|
6. **Batch Operations**: Support for bulk message creation (up to 100 messages)
|
||||||
7. **Hybrid search**: Postgres FTS (GIN index on `to_tsvector('english', content)`) + vector similarity (HNSW on `MessageEmbedding.embedding`). `MessageEmbedding` is a separate table from `Message` with its own `sync_state` so embedding is decoupled from message creation.
|
7. **Session History**: Two-tier summarization (short every 20 messages, long every 60)
|
||||||
8. **Pluggable external vector stores**: defaults to pgvector inline; can swap to turbopuffer or lancedb (`VECTOR_STORE_*` config; `src/vector_store/`).
|
|
||||||
9. **Composite-FK multi-tenancy**: `workspace_name` participates in nearly every composite FK. Cross-workspace data leakage is structurally impossible at the schema level.
|
|
||||||
10. **Scoped Authentication**: JWTs can be scoped to workspace, peer, or session level.
|
|
||||||
11. **Batch Operations**: Bulk message creation up to 100 messages per request.
|
|
||||||
12. **Session History**: Two-tier summarization — short every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` (default 20), long every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60).
|
|
||||||
|
|
||||||
### Error Handling
|
### Error Handling
|
||||||
|
|
||||||
|
|
|
||||||
474
CONTRIBUTING.md
|
|
@ -1,414 +1,176 @@
|
||||||
# Contributing to Honcho
|
# Contributing to Honcho
|
||||||
|
|
||||||
<!-- This file is mirrored at docs/v3/contributing/guidelines.mdx. Update both. -->
|
Thank you for your interest in contributing to Honcho! This guide outlines the process for contributing to the project and our development conventions.
|
||||||
|
|
||||||
Thanks for your interest in contributing. This guide covers how work gets accepted, how
|
## Getting Started
|
||||||
Honcho is put together, and what a mergeable pull request looks like.
|
|
||||||
|
|
||||||
Honcho is a small team maintaining a project that gets more proposals than we can review.
|
Before you start contributing, please:
|
||||||
The rules below exist so that the work you do has somewhere to land — not to keep you out.
|
|
||||||
|
|
||||||
## Contents
|
1. **Set up your development environment** - Follow the [Local Development guide](./README.md#local-development) in the README to get Honcho running locally.
|
||||||
|
|
||||||
- [Before you write code](#before-you-write-code)
|
2. **Join our community** - Feel free to join us in our [Discord](http://discord.gg/plasticlabs) to discuss your changes, get help, or ask questions.
|
||||||
- [What gets prioritized](#what-gets-prioritized)
|
|
||||||
- [If you're an agent](#if-youre-an-agent)
|
|
||||||
- [How Honcho works](#how-honcho-works)
|
|
||||||
- [Where to change what](#where-to-change-what)
|
|
||||||
- [Local setup](#local-setup)
|
|
||||||
- [Making the change](#making-the-change)
|
|
||||||
- [Opening the pull request](#opening-the-pull-request)
|
|
||||||
- [Reporting bugs and requesting features](#reporting-bugs-and-requesting-features)
|
|
||||||
- [Security](#security)
|
|
||||||
- [License](#license)
|
|
||||||
|
|
||||||
## Before you write code
|
3. **Review existing issues** - Check the [issues tab](https://github.com/plastic-labs/honcho/issues) to see what's already being worked on or to find something to contribute to.
|
||||||
|
|
||||||
**Every pull request needs an issue, and that issue needs the `maintainer-approved` label.**
|
## Contribution Workflow
|
||||||
|
|
||||||
A pull request that is not linked to an approved issue gets labelled
|
### 1. Fork and Clone
|
||||||
`needs-approved-issue`, with a comment explaining why. You then have 72 hours to link one
|
|
||||||
before it is closed automatically. Reopening costs nothing once the link is in place. This
|
|
||||||
is automated. We do this because an unreviewable backlog helps nobody: a PR against an
|
|
||||||
unapproved issue is work you did that we may not be able to merge, no matter how good it
|
|
||||||
is.
|
|
||||||
|
|
||||||
So, in order:
|
1. Fork the repository on GitHub
|
||||||
|
2. Clone your fork locally:
|
||||||
1. **Find approved work.** Browse
|
|
||||||
[issues labelled `maintainer-approved`](https://github.com/plastic-labs/honcho/issues?q=is%3Aissue+is%3Aopen+label%3Amaintainer-approved).
|
|
||||||
That label is the queue of things we have agreed should be built. Anything in it is fair
|
|
||||||
game — comment on the issue to claim it.
|
|
||||||
|
|
||||||
2. **Or open an issue and get it approved.** Use the
|
|
||||||
[issue templates](https://github.com/plastic-labs/honcho/issues/new/choose). Maintainers
|
|
||||||
triage and apply the label.
|
|
||||||
|
|
||||||
3. **If you feel strongly about an issue, come to [Discord](https://discord.gg/honcho).**
|
|
||||||
This is the fastest path by a wide margin. Maintainers are more active there than in the
|
|
||||||
issue tracker, and a five-minute conversation about what you want to build usually
|
|
||||||
resolves whether it fits before either side spends real time on it.
|
|
||||||
|
|
||||||
4. **Then open the PR** and link the issue — either `Fixes #123` in the description, or
|
|
||||||
**Development → link an issue** in the sidebar. Both work.
|
|
||||||
|
|
||||||
Small exceptions we will not be pedantic about: fixing a typo, a broken link, or an
|
|
||||||
obviously wrong code sample. Open the PR, explain it in one line, and we will sort out the
|
|
||||||
issue linkage.
|
|
||||||
|
|
||||||
## What gets prioritized
|
|
||||||
|
|
||||||
Roughly, work on Honcho falls along these axes. Knowing which one your idea sits on tells
|
|
||||||
you a lot about how likely it is to get approved.
|
|
||||||
|
|
||||||
| Axis | What it covers |
|
|
||||||
| --- | --- |
|
|
||||||
| **Observability** | Understanding how Honcho behaves in production — telemetry, tracing, CloudEvents, metrics. |
|
|
||||||
| **Memory quality** | Better conclusions from the same input — the deriver, dreamer, and dialectic; eval results. |
|
|
||||||
| **Developer experience** | Fitting cleanly into more application architectures — SDKs, scopes, composable peers, the CLI. |
|
|
||||||
| **Breadth of input** | Widening what Honcho can ingest and represent — multimodal and non-conversational data. |
|
|
||||||
| **Ubiquity** | Reachable wherever a developer already works — integrations, self-hosting, alternate vector-store and inference backends, local-first defaults. |
|
|
||||||
| **Reliability and cost** | Trustworthy in production — connection and concurrency hardening, queue throughput, cost per token. |
|
|
||||||
|
|
||||||
In practice, **Ubiquity** and **Developer experience** are where outside contributions land
|
|
||||||
most easily. A new integration, a self-hosting rough edge, a vector-store or inference
|
|
||||||
backend, an SDK ergonomics fix — these are additive and rarely collide with work already in
|
|
||||||
flight.
|
|
||||||
|
|
||||||
Changes to the reasoning pipeline itself — deriver prompts, dialectic tool design, dreamer
|
|
||||||
strategy — are the hardest to accept from outside. Not because they are unwelcome, but
|
|
||||||
because they are measured against eval results we run internally, and they frequently
|
|
||||||
conflict with in-flight work. Talk to us in Discord first, always.
|
|
||||||
|
|
||||||
## If you're an agent
|
|
||||||
|
|
||||||
If you are a coding agent working on this repository, read this section before writing code.
|
|
||||||
The most common failure we see is a well-formed, well-tested pull request against an issue
|
|
||||||
that was never approved. That gets closed, and the work is wasted.
|
|
||||||
|
|
||||||
- **Check the gate first.** Before writing code:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gh issue view <N> --repo plastic-labs/honcho --json number,title,state,labels
|
git clone https://github.com/YOUR_USERNAME/honcho.git
|
||||||
|
cd honcho
|
||||||
```
|
```
|
||||||
|
|
||||||
Stop if there is no issue number, if the issue is closed, or if `maintainer-approved` is
|
3. Add the upstream repository as a remote:
|
||||||
not in the labels. Report that to the person you are working with instead of proceeding.
|
|
||||||
|
|
||||||
- **Do not open a PR in order to establish the issue link afterwards.** The issue comes
|
```bash
|
||||||
first.
|
git remote add upstream https://github.com/plastic-labs/honcho.git
|
||||||
|
```
|
||||||
|
|
||||||
- **Do not report checks you did not run.** If you did not execute the test command, say so.
|
### 2. Create a Branch
|
||||||
A PR body claiming a green run that did not happen costs a maintainer more time than no
|
|
||||||
claim at all.
|
|
||||||
|
|
||||||
- **Use the checklist.** [`skills/pre-pr/SKILL.md`](./skills/pre-pr/SKILL.md) in this repo
|
Create a new branch for your feature or bug fix:
|
||||||
encodes the gate, the test-layer matrix, and the PR body format. If your harness supports
|
|
||||||
skills, invoke it rather than reimplementing the checks.
|
|
||||||
|
|
||||||
## How Honcho works
|
|
||||||
|
|
||||||
Enough architecture to find your way around. For the user-facing model — what a Peer is, what
|
|
||||||
`get_context` returns — see [Core Concepts in the README](./README.md#core-concepts) and the
|
|
||||||
[documentation](https://honcho.dev/docs/).
|
|
||||||
|
|
||||||
### Two processes
|
|
||||||
|
|
||||||
Honcho runs as two cooperating processes over a shared Postgres database and Redis cache.
|
|
||||||
|
|
||||||
| | API server | Deriver worker |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Start | `uv run fastapi dev src/main.py` | `uv run python -m src.deriver` |
|
|
||||||
| Entry | `src/main.py` | `src/deriver/__main__.py` |
|
|
||||||
| Does | Serves HTTP, enqueues background work, returns immediately | Consumes the queue: Deriver, Summarizer, Dreamer, Reconciler |
|
|
||||||
| Hosts | The Dialectic agent, inline on the request path | Everything else |
|
|
||||||
|
|
||||||
The split is the load-bearing design decision: **an HTTP request never blocks on LLM work**,
|
|
||||||
with the single exception of the Dialectic chat endpoint, which is synchronous by nature.
|
|
||||||
If you are adding something slow, it belongs in the worker.
|
|
||||||
|
|
||||||
The deriver is a separate process. If messages go in and nothing ever comes out, the usual
|
|
||||||
cause is that nobody started it.
|
|
||||||
|
|
||||||
### The path of a message
|
|
||||||
|
|
||||||
Worth tracing once, because it crosses most of the codebase:
|
|
||||||
|
|
||||||
1. `POST /v3/workspaces/{w}/sessions/{s}/messages` lands in `src/routers/messages.py`.
|
|
||||||
2. The row is written, then `enqueue()` in `src/deriver/enqueue.py` creates `queue_item`
|
|
||||||
rows — one set of work per observing peer.
|
|
||||||
3. `src/deriver/queue_manager.py` polls the queue, claiming work units so that messages in a
|
|
||||||
session are processed in order.
|
|
||||||
4. `process_item()` in `src/deriver/consumer.py` dispatches on task type — representation,
|
|
||||||
summary, deletion, reconciliation.
|
|
||||||
5. For a representation task, `process_representation_tasks_batch()` in
|
|
||||||
`src/deriver/deriver.py` makes **one structured-output LLM call for the whole batch** and
|
|
||||||
writes the resulting conclusions into the collection keyed by the
|
|
||||||
`(observer, observed)` peer pair.
|
|
||||||
6. Later, `src/dialectic/` reads those conclusions back at recall time to answer a chat
|
|
||||||
request.
|
|
||||||
|
|
||||||
Embedding is deliberately *not* on this path. `MessageEmbedding` rows are written with
|
|
||||||
`sync_state='pending'` and embedded asynchronously by the Reconciler
|
|
||||||
(`src/reconciler/sync_vectors.py`), which runs on a scheduler inside the deriver process.
|
|
||||||
|
|
||||||
### The four agents
|
|
||||||
|
|
||||||
They share tool definitions in `src/utils/agent_tools.py` and the provider-agnostic LLM
|
|
||||||
client in `src/llm/`. Each has its own `MODEL_CONFIG` with a fallback chain in
|
|
||||||
`src/config.py`.
|
|
||||||
|
|
||||||
| Agent | Where | Shape |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| **Deriver** | `src/deriver/` | A single structured-output call per message batch. Not a tool loop — this is a deliberate cost and latency tradeoff. |
|
|
||||||
| **Dialectic** | `src/dialectic/` | The one tool-using agent on the request path. Loops over tools until it can answer. Five reasoning tiers from `minimal` to `max`, each with its own model and tool set. |
|
|
||||||
| **Dreamer** | `src/dreamer/` | Off-queue consolidation. Two specialist phases (deduction, then induction) that build reasoning trees over existing conclusions. |
|
|
||||||
| **Summarizer** | `src/utils/summarizer.py` | Direct LLM call, no tools. Two tiers — short and long summaries at different message counts. |
|
|
||||||
|
|
||||||
Prompts live in `src/deriver/prompts.py`, `src/dialectic/prompts.py`, and
|
|
||||||
`src/dreamer/specialists.py`.
|
|
||||||
|
|
||||||
### A note on naming
|
|
||||||
|
|
||||||
What the public API and documentation call **conclusions** are called **observations**
|
|
||||||
throughout the code — `create_observations`, `get_observation_context`, and so on. Likewise
|
|
||||||
**collections** and **documents** are internal storage concepts that are not exposed
|
|
||||||
directly through the API. Do not rename across that boundary in a drive-by change; the
|
|
||||||
public and internal vocabularies are being reconciled deliberately.
|
|
||||||
|
|
||||||
## Where to change what
|
|
||||||
|
|
||||||
| I want to change... | Start here |
|
|
||||||
| --- | --- |
|
|
||||||
| An HTTP endpoint | `src/routers/` — one module per resource |
|
|
||||||
| A database query | `src/crud/` — mirrors the router layout |
|
|
||||||
| The database schema | `src/models.py`, plus a migration in `migrations/versions/` |
|
|
||||||
| A configuration value | `src/config.py`, and add it to `config.toml.example` and `.env.template` |
|
|
||||||
| A tool an agent can call | `src/utils/agent_tools.py` — definitions plus the per-agent tool lists |
|
|
||||||
| A prompt | `src/deriver/prompts.py`, `src/dialectic/prompts.py`, `src/dreamer/specialists.py` |
|
|
||||||
| LLM provider behavior | `src/llm/backends/` — `anthropic.py`, `gemini.py`, `openai.py` |
|
|
||||||
| Embeddings or vector storage | `src/embedding_client.py`, `src/vector_store/` |
|
|
||||||
| Telemetry or metrics | `src/telemetry/` — see the notes in `CLAUDE.md` before adding an event type |
|
|
||||||
| Authentication and scoping | `src/security.py`, `src/dependencies.py` |
|
|
||||||
| The Python or TypeScript SDK | `sdks/python/`, `sdks/typescript/` |
|
|
||||||
| The CLI | `honcho-cli/` |
|
|
||||||
| The MCP server | `mcp/` |
|
|
||||||
| Public documentation | `docs/v3/` — Mintlify; nav lives in `docs/docs.json` |
|
|
||||||
|
|
||||||
Tests in `tests/` mirror `src/`. `CLAUDE.md` at the repo root has more detail on house
|
|
||||||
conventions, and is worth skimming even if you are not using an agent.
|
|
||||||
|
|
||||||
## Local setup
|
|
||||||
|
|
||||||
To run a personal instance, install the CLI (`uv tool install honcho-cli`) and then run `honcho start --setup` (Docker + an LLM provider key — not the Honcho API key from `honcho init`) — [CLI in the README](./README.md#cli).
|
|
||||||
|
|
||||||
To **develop this repo**, clone it and:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uv sync # create the venv and install dependencies
|
|
||||||
uv run alembic upgrade head # apply migrations
|
|
||||||
```
|
|
||||||
|
|
||||||
Run both processes, in separate terminals:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uv run fastapi dev src/main.py # API server, reloads on change
|
|
||||||
uv run python -m src.deriver # background worker
|
|
||||||
```
|
|
||||||
|
|
||||||
Everything Python goes through `uv run`. Redis is optional for local development; without it
|
|
||||||
caching is simply disabled.
|
|
||||||
|
|
||||||
### Running without a model provider
|
|
||||||
|
|
||||||
`src/mock_provider/` is a deterministic, OpenAI-compatible endpoint, so you can run the full
|
|
||||||
stack with no provider account, no API key, and no spend. It answers `/v1/chat/completions`
|
|
||||||
and `/v1/embeddings` with obviously-synthetic content derived from the request, and the same
|
|
||||||
request always produces the same response. Run it from the standard image or the repo:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uv run fastapi run --host 0.0.0.0 --port 8106 src/mock_provider/main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Then point Honcho at it. All three variables are required:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export LLM_OPENAI_API_KEY=any-non-empty-string # only truthiness is checked
|
|
||||||
export LLM_OPENAI_BASE_URL=http://localhost:8106/v1
|
|
||||||
export EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL=http://localhost:8106/v1
|
|
||||||
```
|
|
||||||
|
|
||||||
The key's *value* is never checked — the mock reads no Authorization header, and Honcho only
|
|
||||||
tests it for truthiness before building the client (`src/llm/registry.py`). Set the base URL
|
|
||||||
without it and the client is never constructed, so the base URL is silently ignored. Keep the
|
|
||||||
value obviously fake, so a module that ever escapes the override 401s rather than spends.
|
|
||||||
|
|
||||||
Embeddings resolve through a separate client that reads the base URL only from the per-module
|
|
||||||
override, so without the third variable your embedding calls go to `api.openai.com` for real.
|
|
||||||
Do not set any per-module credential override (`..._OVERRIDES__API_KEY` / `API_KEY_ENV`) —
|
|
||||||
that makes the module ignore the global base URL.
|
|
||||||
|
|
||||||
Two things to know:
|
|
||||||
|
|
||||||
- **A repo `.env` beats your exported environment.** `src/config.py` calls
|
|
||||||
`load_dotenv(override=True)` at import, so a stale `.env` silently wins over the variables
|
|
||||||
above. Set `PYTHON_DOTENV_DISABLED=1` (and `HONCHO_CONFIG_TOML_DISABLED=1` for a local
|
|
||||||
`config.toml`) when you need the environment to be the only input.
|
|
||||||
- **Mock embeddings are hash-derived and carry no semantic similarity.** Two paraphrases are as
|
|
||||||
far apart as two unrelated strings. Recall against this provider must use lexical/full-text
|
|
||||||
search; anything asserting on vector ranking needs a real embedding provider.
|
|
||||||
|
|
||||||
## Making the change
|
|
||||||
|
|
||||||
### Branches and commits
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git checkout -b feature/your-feature-name
|
git checkout -b feature/your-feature-name
|
||||||
|
# or
|
||||||
|
git checkout -b fix/your-bug-fix-name
|
||||||
```
|
```
|
||||||
|
|
||||||
Prefixes: `feature/`, `fix/`, `docs/`, `refactor/`, `test/`.
|
**Branch naming conventions:**
|
||||||
|
|
||||||
Commits follow [Conventional Commits](https://www.conventionalcommits.org/), enforced by a
|
- `feature/description` - for new features
|
||||||
`commit-msg` hook:
|
- `fix/description` - for bug fixes
|
||||||
|
- `docs/description` - for documentation updates
|
||||||
|
- `refactor/description` - for code refactoring
|
||||||
|
- `test/description` - for adding or updating tests
|
||||||
|
|
||||||
|
### 3. Make Your Changes
|
||||||
|
|
||||||
|
- Write clean, readable code that follows our coding standards (see below)
|
||||||
|
- Add tests for new functionality
|
||||||
|
- Update documentation as needed
|
||||||
|
- Make sure your changes don't break existing functionality
|
||||||
|
|
||||||
|
### 4. Commit Your Changes
|
||||||
|
|
||||||
|
We follow conventional commit standards. Format your commit messages as:
|
||||||
|
|
||||||
|
```
|
||||||
|
type(scope): description
|
||||||
|
|
||||||
|
[optional body]
|
||||||
|
|
||||||
|
[optional footer]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Types:**
|
||||||
|
|
||||||
|
- `feat`: A new feature
|
||||||
|
- `fix`: A bug fix
|
||||||
|
- `docs`: Documentation only changes
|
||||||
|
- `style`: Changes that do not affect the meaning of the code
|
||||||
|
- `refactor`: A code change that neither fixes a bug nor adds a feature
|
||||||
|
- `test`: Adding missing tests or correcting existing tests
|
||||||
|
- `chore`: Changes to the build process or auxiliary tools
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git commit -m "feat(api): add new dialectic endpoint for user insights"
|
git commit -m "feat(api): add new dialectic endpoint for user insights"
|
||||||
git commit -m "fix(db): resolve connection pool timeout issue"
|
git commit -m "fix(db): resolve connection pool timeout issue"
|
||||||
|
git commit -m "docs(readme): update installation instructions"
|
||||||
```
|
```
|
||||||
|
|
||||||
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`.
|
### 5. Submit a Pull Request
|
||||||
|
|
||||||
### Pre-commit hooks
|
1. Push your branch to your fork:
|
||||||
|
|
||||||
Install them. CI runs the same checks, and it is much faster to find out locally.
|
```bash
|
||||||
|
git push origin your-branch-name
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
2. Create a pull request on GitHub from your branch to the `main` branch
|
||||||
uv run pre-commit install \
|
|
||||||
--hook-type pre-commit \
|
|
||||||
--hook-type commit-msg \
|
|
||||||
--hook-type pre-push
|
|
||||||
```
|
|
||||||
|
|
||||||
At **commit** time: ruff lint and format, biome for TypeScript, basedpyright, bandit,
|
3. Fill out the pull request template with:
|
||||||
markdownlint, and file hygiene. At **push** time: pytest, the alembic migration tests, and
|
- A clear description of what changes you've made
|
||||||
the SDK builds.
|
- The motivation for the changes
|
||||||
|
- Any relevant issue numbers (use "Closes #123" to auto-close issues)
|
||||||
|
- Screenshots or examples if applicable
|
||||||
|
|
||||||
That split matters — **a clean commit is not a clean push.** The test suite only runs at
|
## Coding Standards
|
||||||
`pre-push`, so the first time you see test failures may be well after you thought you were
|
|
||||||
done.
|
|
||||||
|
|
||||||
Run them by hand at any time:
|
### Python Code Style
|
||||||
|
|
||||||
```bash
|
- Follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guidelines
|
||||||
uv run pre-commit run --all-files
|
- Use [Black](https://black.readthedocs.io/) for code formatting (we may add this to CI in the future)
|
||||||
uv run pre-commit run ruff --all-files
|
- Use type hints where possible
|
||||||
```
|
- Write docstrings for functions and classes using Google style docstrings
|
||||||
|
|
||||||
Or the individual tools:
|
### Code Organization
|
||||||
|
|
||||||
```bash
|
- Keep functions focused and single-purpose
|
||||||
uv run ruff check src/
|
- Use meaningful variable and function names
|
||||||
uv run ruff format src/
|
- Add comments for complex logic
|
||||||
uv run basedpyright
|
- Follow existing patterns in the codebase
|
||||||
```
|
|
||||||
|
|
||||||
### Tests
|
### Testing
|
||||||
|
|
||||||
Write tests for new functionality, in the directory under `tests/` that mirrors the code you
|
- Write unit tests for new functionality
|
||||||
changed. Which layer you need depends on what you touched:
|
- Ensure existing tests pass before submitting
|
||||||
|
- Use descriptive test names that explain what is being tested
|
||||||
| What you changed | What to run |
|
- Mock external dependencies appropriately
|
||||||
| --- | --- |
|
|
||||||
| Anything in `src/` | Unit tests in the matching `tests/` tree — `uv run pytest tests/...` |
|
|
||||||
| Deriver, dialectic, dreamer, or the LLM path | Unit tests, and consider `tests/live_llm/` (gated behind `--live-llm`) |
|
|
||||||
| Queue behavior, config hierarchy, multi-turn flows, SDK contracts | `uv run python -m tests.unified.run` |
|
|
||||||
| A `/v3` endpoint or deriver queue behavior | Actually run the stack and exercise it — not just pytest |
|
|
||||||
| A migration | `uv run python scripts/run_alembic_tests.py`; every revision needs a test file |
|
|
||||||
|
|
||||||
The TypeScript SDK tests need a running server with a database and Redis, which pytest
|
|
||||||
orchestrates. Run them with `uv run pytest tests/ -k typescript` from the repo root —
|
|
||||||
`bun test` on its own will fail. To type-check the SDK alone:
|
|
||||||
`cd sdks/typescript && bun run tsc --noEmit`.
|
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
Update docs in the same PR when you change a public surface: `/v3` endpoints, SDK exports,
|
- Update relevant documentation for new features
|
||||||
or anything in `config.toml` / settings. Docs live in `docs/v3/`, and new pages need an entry
|
- Include examples in docstrings where helpful
|
||||||
in `docs/docs.json` or they will not appear in the nav.
|
- Keep README and other docs up to date with changes
|
||||||
|
|
||||||
## Opening the pull request
|
## Review Process
|
||||||
|
|
||||||
### Leave "Allow edits by maintainers" checked
|
1. **Automated checks** - Your PR will run through automated checks including tests and linting
|
||||||
|
2. **Project maintainer review** - A project maintainer will review your code for:
|
||||||
|
- Code quality and adherence to standards
|
||||||
|
- Functionality and correctness
|
||||||
|
- Test coverage
|
||||||
|
- Documentation completeness
|
||||||
|
3. **Discussion and iteration** - You may be asked to make changes or clarifications
|
||||||
|
4. **Approval and merge** - Once approved, your PR will be merged into `main`
|
||||||
|
|
||||||
This is the single most useful thing you can do to get your PR merged quickly.
|
## Types of Contributions
|
||||||
|
|
||||||
Most contributor PRs arrive nearly right, needing a rename, a missing test, or a lint fix.
|
We welcome various types of contributions:
|
||||||
If we can push that commit ourselves, it merges the same day. If we cannot, it becomes a
|
|
||||||
review comment, and then we wait — sometimes for weeks — for a round trip on a two-line
|
|
||||||
change.
|
|
||||||
|
|
||||||
GitHub checks the box by default when you fork. Leave it checked.
|
- **Bug fixes** - Help us squash bugs and improve stability
|
||||||
|
- **New features** - Add functionality that benefits the community
|
||||||
|
- **Documentation** - Improve or expand our documentation
|
||||||
|
- **Tests** - Increase test coverage and reliability
|
||||||
|
- **Performance improvements** - Help make Honcho faster and more efficient
|
||||||
|
- **Examples and tutorials** - Help other developers use Honcho
|
||||||
|
|
||||||
One caveat worth knowing: **the option does not exist on forks owned by an organization.**
|
## Issue Reporting
|
||||||
If you have the choice, fork from your personal account.
|
|
||||||
|
|
||||||
### Fill out the template
|
When reporting bugs or requesting features:
|
||||||
|
|
||||||
`.github/pull_request_template.md` asks for a description, proofs, and the issue checkbox.
|
1. Check if the issue already exists
|
||||||
|
2. Use the appropriate issue template
|
||||||
|
3. Provide clear reproduction steps for bugs
|
||||||
|
4. Include relevant environment information
|
||||||
|
5. Be specific about expected vs actual behavior
|
||||||
|
|
||||||
"Proofs" means evidence the change works: the command you ran and its result, a log snippet,
|
## Questions and Support
|
||||||
a screenshot, the failing case before and after. This is the section that most determines
|
|
||||||
how fast your PR gets reviewed. Do not add sections to the template.
|
|
||||||
|
|
||||||
Link the issue so the gate can see it: `Fixes #123` in the description, or the
|
- **General questions** - Join our [Discord](http://discord.gg/plasticlabs)
|
||||||
**Development** section of the sidebar. The gate reads GitHub's own resolved issue links, so
|
- **Bug reports** - Use GitHub issues
|
||||||
either route works — but a bare `#123` mention is only a reference and does not count.
|
- **Feature requests** - Use GitHub issues with the feature request template
|
||||||
|
- **Security issues** - Please email us privately rather than opening a public issue
|
||||||
### Review
|
|
||||||
|
|
||||||
1. Automated checks run — tests, linting, static analysis, and the issue gate.
|
|
||||||
2. A maintainer reviews for correctness, test coverage, and fit with the surrounding code.
|
|
||||||
`.github/CODEOWNERS` routes the request to whoever owns the area you touched.
|
|
||||||
3. You may be asked for changes. Or we may just push them, if you left edits enabled.
|
|
||||||
4. Once approved, we merge to `main`.
|
|
||||||
|
|
||||||
If a PR goes quiet, nudge us in [Discord](https://discord.gg/honcho).
|
|
||||||
|
|
||||||
Please respond within 7 days - we may close any PRs that have seen no activity within a 7 day
|
|
||||||
window. If you need more time, let us know in the PR comments.
|
|
||||||
|
|
||||||
## Reporting bugs and requesting features
|
|
||||||
|
|
||||||
Use the [issue templates](https://github.com/plastic-labs/honcho/issues/new/choose). There is
|
|
||||||
one per kind of report, and picking the right one is most of what gets an issue triaged
|
|
||||||
quickly:
|
|
||||||
|
|
||||||
- **Bug report** — something is broken or behaves incorrectly
|
|
||||||
- **Memory / recall quality** — the deriver or dialectic returns poor, wrong, or missing context
|
|
||||||
- **Feature request** — a new capability or API surface
|
|
||||||
- **Integration request** — plugins, framework integrations, app-store listings
|
|
||||||
- **Documentation issue** — anything wrong or missing in the docs
|
|
||||||
- **General questions** — not an issue at all; ask in [Discord](https://discord.gg/honcho)
|
|
||||||
|
|
||||||
Before opening one, search existing issues, including closed ones.
|
|
||||||
|
|
||||||
A good bug report has the Honcho version or commit, whether you are self-hosted or on
|
|
||||||
`api.honcho.dev`, the steps to reproduce, and what you expected instead. If it involves the
|
|
||||||
deriver, logs from the worker process are usually the thing we ask for first.
|
|
||||||
|
|
||||||
**Redact before you post.** Issues are public, and Honcho stores conversational data — strip
|
|
||||||
API keys, JWTs, and production user content out of any log or payload you attach.
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
Do not open a public issue for a suspected vulnerability. Report it privately through
|
|
||||||
[GitHub Private Vulnerability Reporting](https://github.com/plastic-labs/honcho/security/advisories/new),
|
|
||||||
which is the preferred channel, or by email. See [SECURITY.md](./SECURITY.md) for what to
|
|
||||||
include, and note that Honcho does not operate a bug bounty.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
By contributing to Honcho, you agree that your contributions will be licensed under the same
|
By contributing to Honcho, you agree that your contributions will be licensed under the same [AGPL-3.0 License](./LICENSE) that covers the project.
|
||||||
[AGPL-3.0 License](./LICENSE) that covers the project.
|
|
||||||
|
|
||||||
Thank you for helping make Honcho better! 🫡
|
Thank you for helping make Honcho better! 🫡
|
||||||
|
|
|
||||||
61
Dockerfile
|
|
@ -1,77 +1,46 @@
|
||||||
# syntax=docker/dockerfile:1
|
|
||||||
|
|
||||||
# https://pythonspeed.com/articles/base-image-python-docker-images/
|
# https://pythonspeed.com/articles/base-image-python-docker-images/
|
||||||
# https://testdriven.io/blog/docker-best-practices/
|
# https://testdriven.io/blog/docker-best-practices/
|
||||||
FROM python:3.13-slim-bookworm AS builder
|
FROM python:3.11-slim-bullseye
|
||||||
|
|
||||||
COPY --from=ghcr.io/astral-sh/uv:0.9.24 /uv /bin/uv
|
COPY --from=ghcr.io/astral-sh/uv:0.4.9 /uv /bin/uv
|
||||||
|
|
||||||
|
# Set Working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN addgroup --system app && adduser --system --group app
|
||||||
|
RUN chown -R app:app /app
|
||||||
|
USER app
|
||||||
|
|
||||||
# Enable bytecode compilation
|
# Enable bytecode compilation
|
||||||
ENV UV_COMPILE_BYTECODE=1
|
ENV UV_COMPILE_BYTECODE=1
|
||||||
|
|
||||||
# Copy from the cache instead of linking since it's a mounted volume
|
# Copy from the cache instead of linking since it's a mounted volume
|
||||||
ENV UV_LINK_MODE=copy
|
ENV UV_LINK_MODE=copy
|
||||||
|
|
||||||
# Python optimizations
|
# Install the project's dependencies using the lockfile and settings
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
ENV PYTHONUNBUFFERED=1
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||||
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||||
|
uv sync --frozen --no-install-project --no-dev
|
||||||
|
|
||||||
# Copy only requirements to cache them in docker layer
|
# Copy only requirements to cache them in docker layer
|
||||||
COPY uv.lock pyproject.toml /app/
|
COPY uv.lock pyproject.toml /app/
|
||||||
|
|
||||||
# Optionall include lancedb with:
|
# Sync the project
|
||||||
# docker build --build-arg INSTALL_LANCEDB=true .
|
|
||||||
ARG INSTALL_LANCEDB=false
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
if [ "$INSTALL_LANCEDB" = "true" ]; then \
|
uv sync --frozen --no-dev
|
||||||
uv sync --frozen --no-install-project --no-group dev --extra lancedb; \
|
|
||||||
elif [ "$INSTALL_LANCEDB" = "false" ]; then \
|
|
||||||
uv sync --frozen --no-install-project --no-group dev; \
|
|
||||||
else \
|
|
||||||
echo "INSTALL_LANCEDB must be 'true' or 'false'" >&2; \
|
|
||||||
exit 2; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
FROM python:3.13-slim-bookworm AS runtime
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
|
||||||
|
|
||||||
# Create the runtime user before copying dependencies with their final owner.
|
|
||||||
# A recursive chown in a later layer would copy the whole virtualenv and nearly
|
|
||||||
# double the image size.
|
|
||||||
RUN addgroup --system app \
|
|
||||||
&& adduser --system --group app \
|
|
||||||
&& chown app:app /app \
|
|
||||||
# Pre-create the LanceDB dir so a named volume mounted here inherits app
|
|
||||||
# ownership instead of defaulting to root.
|
|
||||||
&& mkdir /app/lancedb_data \
|
|
||||||
&& chown app:app /app/lancedb_data
|
|
||||||
|
|
||||||
COPY --from=builder --chown=app:app /app/.venv /app/.venv
|
|
||||||
|
|
||||||
# Place executables in the environment at the front of the path
|
# Place executables in the environment at the front of the path
|
||||||
ENV PATH="/app/.venv/bin:$PATH"
|
ENV PATH="/app/.venv/bin:$PATH"
|
||||||
ENV HOME=/app
|
|
||||||
|
|
||||||
COPY --chown=app:app src/ /app/src/
|
COPY --chown=app:app src/ /app/src/
|
||||||
COPY --chown=app:app migrations/ /app/migrations/
|
COPY --chown=app:app migrations/ /app/migrations/
|
||||||
COPY --chown=app:app scripts/ /app/scripts/
|
COPY --chown=app:app scripts/ /app/scripts/
|
||||||
COPY --chown=app:app docker/ /app/docker/
|
|
||||||
COPY --chown=app:app alembic.ini /app/alembic.ini
|
COPY --chown=app:app alembic.ini /app/alembic.ini
|
||||||
# src/_version.py reads the service version from here at runtime, so this
|
|
||||||
# is a runtime input as well as a build input.
|
|
||||||
COPY --chown=app:app pyproject.toml /app/pyproject.toml
|
|
||||||
# Copy config files - this will copy config.toml if it exists, and config.toml.example
|
# Copy config files - this will copy config.toml if it exists, and config.toml.example
|
||||||
COPY --chown=app:app config.toml* /app/
|
COPY --chown=app:app config.toml* /app/
|
||||||
|
|
||||||
# Switch to non-root user
|
|
||||||
USER app
|
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# https://stackoverflow.com/questions/29663459/python-app-does-not-print-anything-when-running-detached-in-docker
|
||||||
CMD ["fastapi", "run", "--host", "0.0.0.0", "src/main.py"]
|
CMD ["fastapi", "run", "--host", "0.0.0.0", "src/main.py"]
|
||||||
|
|
|
||||||
73
SECURITY.md
|
|
@ -1,73 +0,0 @@
|
||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
The `main` branch of this repo maps to the latest canary version of Honcho. To see which versions are supported please refer to the git tags in the repo or the [compatibility guide](https://honcho.dev/docs/changelog/compatibility-guide).
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
Do not open a public issue for a suspected vulnerability. Report it privately through one of:
|
|
||||||
|
|
||||||
1. **[GitHub Private Vulnerability Reporting](https://github.com/plastic-labs/honcho/security/advisories/new)** — preferred; it keeps the report, our replies, and any fix coordinated in one place.
|
|
||||||
2. Email [support@honcho.dev](mailto:support@honcho.dev) with `[SECURITY]` in the subject.
|
|
||||||
|
|
||||||
Include as much of the following as you have:
|
|
||||||
|
|
||||||
- **Version** — a git commit SHA, or the release tag you are running
|
|
||||||
- **Deployment** — self-hosted or the managed service at `api.honcho.dev`
|
|
||||||
- **Affected component** — API, deriver, dialectic, auth/JWT, an SDK, or the managed offering
|
|
||||||
- **Reproduction** — the exact steps, requests, or script that trigger it
|
|
||||||
- **Proof of concept** — the smallest thing that demonstrates the issue actually works
|
|
||||||
- **Impact** — what an attacker gains, and what they need to already have to get it
|
|
||||||
- **How you found it** — manual review, fuzzing, a scanner, or model-assisted analysis
|
|
||||||
|
|
||||||
Reports with a working proof of concept get looked at first. A report that only describes a
|
|
||||||
theoretical problem is much slower for us to act on, because we have to build the repro
|
|
||||||
ourselves before we can confirm anything.
|
|
||||||
|
|
||||||
Honcho stores conversational data and peer representations. **Do not attach production user
|
|
||||||
content, API keys, or JWTs** to a report — if we need a sample, we will ask for a redacted
|
|
||||||
one.
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
Test against an instance you operate. Do not run security testing against `api.honcho.dev`
|
|
||||||
or against any Honcho deployment that is not yours — self-hosting is a first-class path and
|
|
||||||
takes a few minutes to set up — install the CLI (`uv tool install honcho-cli`) then run `honcho start --setup` (Docker + an LLM provider key), or see [Self-hosting](./README.md#self-hosting).
|
|
||||||
|
|
||||||
## What to Expect
|
|
||||||
|
|
||||||
We will acknowledge your report and tell you whether we consider it in scope. If it is, we
|
|
||||||
will let you know when a fix ships.
|
|
||||||
|
|
||||||
We do not commit to a response SLA, we do not coordinate CVE assignment on request, and we
|
|
||||||
do not operate a disclosure timeline you can hold us to. This is a small team.
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
The following are not treated as vulnerabilities. Reports consisting only of these will be
|
|
||||||
closed without a detailed response:
|
|
||||||
|
|
||||||
- Automated scanner output with no working proof of concept
|
|
||||||
- Model-generated findings that have not been verified by a human against a running instance
|
|
||||||
- Missing security headers or TLS configuration with no demonstrated exploit
|
|
||||||
- Rate limiting, or resource exhaustion with no demonstrated impact beyond your own instance
|
|
||||||
- Vulnerabilities in dependencies with no demonstrated exploit path through Honcho
|
|
||||||
- Configuration weaknesses that require an already-compromised host, or that come from
|
|
||||||
deliberately insecure settings (for example running with `AUTH_USE_AUTH=false`, which is
|
|
||||||
the documented local-development default and is not intended for a public deployment)
|
|
||||||
- Social engineering, phishing, and physical access
|
|
||||||
|
|
||||||
For ordinary bugs, memory or recall quality problems, and feature requests, use the
|
|
||||||
[issue templates](https://github.com/plastic-labs/honcho/issues/new/choose) instead.
|
|
||||||
|
|
||||||
## No Bug Bounty
|
|
||||||
|
|
||||||
The Honcho project does not offer any rewards for reported bugs or
|
|
||||||
vulnerabilities. We do not aid security researchers to get such rewards for
|
|
||||||
Honcho problems from other sources.
|
|
||||||
|
|
||||||
A bug bounty gives people too strong incentives to find and make up "problems"
|
|
||||||
in bad faith that cause overload and abuse.
|
|
||||||
|
|
||||||
We still appreciate and value valid vulnerability reports.
|
|
||||||
|
|
@ -6,18 +6,16 @@
|
||||||
# Application-level settings
|
# Application-level settings
|
||||||
[app]
|
[app]
|
||||||
LOG_LEVEL = "INFO"
|
LOG_LEVEL = "INFO"
|
||||||
PERFORMANCE_LOG_FORMAT = "compact" # "compact" for single-line logs, "rich" for local panels
|
|
||||||
SESSION_OBSERVERS_LIMIT = 10
|
SESSION_OBSERVERS_LIMIT = 10
|
||||||
GET_CONTEXT_MAX_TOKENS = 100000
|
GET_CONTEXT_MAX_TOKENS = 100000
|
||||||
MAX_FILE_SIZE = 5242880 # 5MB
|
MAX_FILE_SIZE = 5242880 # 5MB
|
||||||
MAX_MESSAGE_SIZE = 25000 # Characters
|
MAX_MESSAGE_SIZE = 25000 # Characters
|
||||||
EMBED_MESSAGES = true
|
EMBED_MESSAGES = true
|
||||||
|
MAX_EMBEDDING_TOKENS = 8192
|
||||||
|
MAX_EMBEDDING_TOKENS_PER_REQUEST = 300000
|
||||||
# LANGFUSE_HOST = "https://api.langfuse.com"
|
# LANGFUSE_HOST = "https://api.langfuse.com"
|
||||||
# LANGFUSE_PUBLIC_KEY = "your-public-key-here"
|
# LANGFUSE_PUBLIC_KEY = "your-public-key-here"
|
||||||
# COLLECT_METRICS_LOCAL = false
|
|
||||||
# LOCAL_METRICS_FILE = "metrics.jsonl"
|
|
||||||
# REASONING_TRACES_FILE = "traces.jsonl" # Path to JSONL file for reasoning traces
|
|
||||||
NAMESPACE = "honcho"
|
|
||||||
|
|
||||||
# Database settings
|
# Database settings
|
||||||
[db]
|
[db]
|
||||||
|
|
@ -27,14 +25,11 @@ POOL_CLASS = "default"
|
||||||
POOL_PRE_PING = true
|
POOL_PRE_PING = true
|
||||||
POOL_SIZE = 10
|
POOL_SIZE = 10
|
||||||
MAX_OVERFLOW = 20
|
MAX_OVERFLOW = 20
|
||||||
POOL_TIMEOUT = 5 # seconds a pooled checkout waits for a free connection (QueuePool only)
|
POOL_TIMEOUT = 30 # seconds
|
||||||
POOL_RECYCLE = 300 # seconds
|
POOL_RECYCLE = 300 # seconds
|
||||||
POOL_USE_LIFO = true
|
POOL_USE_LIFO = true
|
||||||
SQL_DEBUG = false
|
SQL_DEBUG = false
|
||||||
TRACING = false
|
TRACING = false
|
||||||
# Per-connection establish timeout (seconds) so a single connection attempt
|
|
||||||
# fails fast instead of hanging when the server/pooler is unreachable.
|
|
||||||
CONNECT_TIMEOUT_SECONDS = 2
|
|
||||||
|
|
||||||
# Authentication settings
|
# Authentication settings
|
||||||
[auth]
|
[auth]
|
||||||
|
|
@ -53,239 +48,66 @@ PROFILES_SAMPLE_RATE = 0.1
|
||||||
# LLM settings
|
# LLM settings
|
||||||
[llm]
|
[llm]
|
||||||
DEFAULT_MAX_TOKENS = 2500
|
DEFAULT_MAX_TOKENS = 2500
|
||||||
MAX_TOOL_OUTPUT_CHARS = 10000 # Max chars for tool output (~2500 tokens)
|
|
||||||
MAX_MESSAGE_CONTENT_CHARS = 2000 # Max chars per message in tool results
|
|
||||||
|
|
||||||
# API Keys for LLM providers (set the ones you need)
|
# API Keys for LLM providers
|
||||||
# Supported transports: openai, anthropic, gemini
|
|
||||||
# Base URLs are set per-module via model_config.overrides.base_url
|
|
||||||
# Built-in text-generation defaults use openai / gpt-5.4-mini.
|
|
||||||
# Embeddings default to openai / text-embedding-3-small.
|
|
||||||
OPENAI_API_KEY = "your-api-key-here"
|
|
||||||
# ANTHROPIC_API_KEY = "your-api-key"
|
# ANTHROPIC_API_KEY = "your-api-key"
|
||||||
|
# OPENAI_API_KEY = "your-api-key"
|
||||||
|
# OPENAI_COMPATIBLE_API_KEY = "your-api-key"
|
||||||
# GEMINI_API_KEY = "your-api-key"
|
# GEMINI_API_KEY = "your-api-key"
|
||||||
|
# GROQ_API_KEY = "your-api-key"
|
||||||
# Embedding settings
|
# OPENAI_COMPATIBLE_BASE_URL = "your-base-url"
|
||||||
[embedding]
|
|
||||||
VECTOR_DIMENSIONS = 1536
|
|
||||||
MAX_INPUT_TOKENS = 8192
|
|
||||||
MAX_TOKENS_PER_REQUEST = 300000
|
|
||||||
|
|
||||||
[embedding.model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "text-embedding-3-small"
|
|
||||||
# Optional provider request input cap. Useful for OpenAI-compatible embedding
|
|
||||||
# APIs with smaller limits, such as DashScope text-embedding-v4.
|
|
||||||
# max_batch_size = 10
|
|
||||||
# Optional client HTTP timeout in seconds (OpenAI + Gemini).
|
|
||||||
# timeout = 90.0
|
|
||||||
|
|
||||||
# Optional module-level endpoint overrides
|
|
||||||
# [embedding.model_config.overrides]
|
|
||||||
# base_url = "https://embedding-proxy.internal.example/v1"
|
|
||||||
# api_key_env = "EMBEDDING_CUSTOM_API_KEY"
|
|
||||||
|
|
||||||
# Deriver settings
|
# Deriver settings
|
||||||
[deriver]
|
[deriver]
|
||||||
ENABLED = true
|
|
||||||
WORKERS = 1
|
WORKERS = 1
|
||||||
POLLING_SLEEP_INTERVAL_SECONDS = 1.0
|
POLLING_SLEEP_INTERVAL_SECONDS = 1.0
|
||||||
# Adaptive polling: when idle/erroring, the sleep interval grows from
|
|
||||||
# POLLING_SLEEP_INTERVAL_SECONDS toward POLLING_SLEEP_MAX_INTERVAL_SECONDS by
|
|
||||||
# POLLING_BACKOFF_MULTIPLIER each cycle, then snaps back to base when work is
|
|
||||||
# found. Cuts steady-state query load against the shared DB/pooler.
|
|
||||||
POLLING_BACKOFF_ENABLED = true
|
|
||||||
POLLING_SLEEP_MAX_INTERVAL_SECONDS = 30.0
|
|
||||||
POLLING_BACKOFF_MULTIPLIER = 2.0
|
|
||||||
# Jitter so instances that start together don't poll in lockstep. Startup:
|
|
||||||
# sleep a random delay in [0, POLLING_STARTUP_JITTER_SECONDS] before the first
|
|
||||||
# poll (0.0 disables). Per-cycle: multiply every poll sleep by a random factor
|
|
||||||
# in [1 - ratio, 1 + ratio] (0.5 -> [0.5x, 1.5x]; 0.0 disables).
|
|
||||||
POLLING_STARTUP_JITTER_SECONDS = 30.0
|
|
||||||
POLLING_JITTER_RATIO = 0.5
|
|
||||||
STALE_SESSION_TIMEOUT_MINUTES = 5
|
STALE_SESSION_TIMEOUT_MINUTES = 5
|
||||||
# Minimum (jittered) spacing between stale-work-unit cleanup runs per instance.
|
PROVIDER = "google"
|
||||||
# Staleness is a minutes-timescale condition, so cleanup doesn't need to run on
|
MODEL = "gemini-2.0-flash-lite"
|
||||||
# every seconds-scale poll (0.0 = run every poll, legacy behavior).
|
MAX_OUTPUT_TOKENS = 2500
|
||||||
STALE_WORK_UNIT_CLEANUP_INTERVAL_SECONDS = 60.0
|
THINKING_BUDGET_TOKENS = 1024 # only applied when using Anthropic
|
||||||
# QUEUE_ERROR_RETENTION_SECONDS = 2592000 # 30 days
|
|
||||||
DEDUPLICATE = true
|
|
||||||
LOG_OBSERVATIONS = false
|
|
||||||
MAX_INPUT_TOKENS = 25000
|
|
||||||
MAX_CUSTOM_INSTRUCTIONS_TOKENS = 2000
|
|
||||||
WORKING_REPRESENTATION_MAX_OBSERVATIONS = 100
|
WORKING_REPRESENTATION_MAX_OBSERVATIONS = 100
|
||||||
REPRESENTATION_BATCH_WORK_UNIT_TARGET_TOKENS = 512 # Min tokens a work unit accumulates before the deriver claims it; 0 disables the gate
|
REPRESENTATION_BATCH_MAX_TOKENS = 4096
|
||||||
REPRESENTATION_BATCH_TARGET_INPUT_TOKENS = 1024 # Max context-window tokens per deriver LLM call
|
MAX_INPUT_TOKENS = 23000
|
||||||
REPRESENTATION_BATCH_MAX_AGE_SECONDS = 1800
|
|
||||||
FLUSH_ENABLED = false # Bypass batch token threshold, process work immediately
|
|
||||||
|
|
||||||
[deriver.model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "gpt-5.4-mini"
|
|
||||||
# temperature = 0.0
|
|
||||||
# thinking_effort = "minimal"
|
|
||||||
# thinking_budget_tokens = 1024
|
|
||||||
# max_output_tokens = 4096
|
|
||||||
|
|
||||||
# Optional module-level endpoint overrides
|
|
||||||
# transport = "openai"
|
|
||||||
# model = "my-local-model"
|
|
||||||
# [deriver.model_config.overrides]
|
|
||||||
# base_url = "https://llm.internal.example/v1"
|
|
||||||
# api_key_env = "DERIVER_CUSTOM_API_KEY"
|
|
||||||
|
|
||||||
# Optional fallback model
|
|
||||||
# [deriver.model_config.fallback]
|
|
||||||
# transport = "anthropic"
|
|
||||||
# model = "claude-haiku-4-5"
|
|
||||||
# [deriver.model_config.fallback.overrides]
|
|
||||||
# base_url = "https://llm-backup.internal.example/v1"
|
|
||||||
# api_key_env = "DERIVER_CUSTOM_BACKUP_API_KEY"
|
|
||||||
# [deriver.model_config.overrides.provider_params]
|
|
||||||
# verbosity = "low"
|
|
||||||
# timeout = 3600.0
|
|
||||||
|
|
||||||
# Peer card settings
|
# Peer card settings
|
||||||
[peer_card]
|
[peer_card]
|
||||||
ENABLED = true
|
ENABLED = true
|
||||||
|
PROVIDER = "openai"
|
||||||
|
MODEL = "gpt-5-nano-2025-08-07"
|
||||||
|
MAX_OUTPUT_TOKENS = 4000
|
||||||
|
|
||||||
# Dialectic settings
|
# Dialectic settings
|
||||||
[dialectic]
|
[dialectic]
|
||||||
MAX_OUTPUT_TOKENS = 8192
|
PROVIDER = "anthropic"
|
||||||
MAX_INPUT_TOKENS = 100000
|
MODEL = "claude-sonnet-4-20250514"
|
||||||
HISTORY_TOKEN_LIMIT = 8192
|
PERFORM_QUERY_GENERATION = false
|
||||||
SESSION_HISTORY_MAX_TOKENS = 4096
|
QUERY_GENERATION_PROVIDER = "groq"
|
||||||
|
QUERY_GENERATION_MODEL = "llama-3.1-8b-instant"
|
||||||
# Per-level settings for reasoning levels
|
MAX_OUTPUT_TOKENS = 2500
|
||||||
# MAX_OUTPUT_TOKENS is optional per level; if not set, uses global MAX_OUTPUT_TOKENS
|
SEMANTIC_SEARCH_TOP_K = 10
|
||||||
[dialectic.levels.minimal]
|
SEMANTIC_SEARCH_MAX_DISTANCE = 0.85
|
||||||
MAX_TOOL_ITERATIONS = 1
|
THINKING_BUDGET_TOKENS = 1024
|
||||||
MAX_OUTPUT_TOKENS = 250
|
CONTEXT_WINDOW_SIZE = 100000
|
||||||
TOOL_CHOICE = "auto"
|
|
||||||
|
|
||||||
[dialectic.levels.minimal.model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "gpt-5.4-mini"
|
|
||||||
|
|
||||||
[dialectic.levels.low]
|
|
||||||
MAX_TOOL_ITERATIONS = 5
|
|
||||||
TOOL_CHOICE = "auto"
|
|
||||||
|
|
||||||
[dialectic.levels.low.model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "gpt-5.4-mini"
|
|
||||||
|
|
||||||
[dialectic.levels.medium]
|
|
||||||
MAX_TOOL_ITERATIONS = 2
|
|
||||||
|
|
||||||
[dialectic.levels.medium.model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "gpt-5.4-mini"
|
|
||||||
|
|
||||||
[dialectic.levels.high]
|
|
||||||
MAX_TOOL_ITERATIONS = 4
|
|
||||||
|
|
||||||
[dialectic.levels.high.model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "gpt-5.4-mini"
|
|
||||||
|
|
||||||
[dialectic.levels.max]
|
|
||||||
MAX_TOOL_ITERATIONS = 10
|
|
||||||
|
|
||||||
[dialectic.levels.max.model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "gpt-5.4-mini"
|
|
||||||
|
|
||||||
# [dialectic.levels.max.model_config.fallback]
|
|
||||||
# transport = "gemini"
|
|
||||||
# model = "gemini-2.5-pro"
|
|
||||||
|
|
||||||
# Summary settings
|
# Summary settings
|
||||||
[summary]
|
[summary]
|
||||||
ENABLED = true
|
ENABLED = true
|
||||||
MESSAGES_PER_SHORT_SUMMARY = 20
|
MESSAGES_PER_SHORT_SUMMARY = 20
|
||||||
MESSAGES_PER_LONG_SUMMARY = 60
|
MESSAGES_PER_LONG_SUMMARY = 60
|
||||||
|
PROVIDER = "google"
|
||||||
|
MODEL = "gemini-1.5-flash-latest"
|
||||||
MAX_TOKENS_SHORT = 1000
|
MAX_TOKENS_SHORT = 1000
|
||||||
MAX_TOKENS_LONG = 4000
|
MAX_TOKENS_LONG = 2000
|
||||||
|
THINKING_BUDGET_TOKENS = 512
|
||||||
[summary.model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "gpt-5.4-mini"
|
|
||||||
# thinking_effort = "minimal"
|
|
||||||
# thinking_budget_tokens = 1024
|
|
||||||
|
|
||||||
# [summary.model_config.fallback]
|
|
||||||
# transport = "anthropic"
|
|
||||||
# model = "claude-haiku-4-5"
|
|
||||||
|
|
||||||
# Dream settings
|
|
||||||
[dream]
|
|
||||||
ENABLED = true
|
|
||||||
DOCUMENT_THRESHOLD = 50
|
|
||||||
IDLE_TIMEOUT_MINUTES = 60
|
|
||||||
MIN_HOURS_BETWEEN_DREAMS = 8
|
|
||||||
ENABLED_TYPES = ["omni"]
|
|
||||||
MAX_TOOL_ITERATIONS = 20
|
|
||||||
HISTORY_TOKEN_LIMIT = 16384
|
|
||||||
|
|
||||||
[dream.deduction_model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "gpt-5.4-mini"
|
|
||||||
|
|
||||||
[dream.induction_model_config]
|
|
||||||
transport = "openai"
|
|
||||||
model = "gpt-5.4-mini"
|
|
||||||
|
|
||||||
# Surprisal-based sampling subsystem
|
|
||||||
[dream.surprisal]
|
|
||||||
ENABLED = false
|
|
||||||
TREE_TYPE = "kdtree" # Options: kdtree, balltree, rptree, covertree, lsh, graph, prototype
|
|
||||||
TREE_K = 5 # k for kNN-based trees
|
|
||||||
SAMPLING_STRATEGY = "recent" # Options: recent, random, all
|
|
||||||
SAMPLE_SIZE = 200
|
|
||||||
TOP_PERCENT_SURPRISAL = 0.10 # Top 10% of observations
|
|
||||||
MIN_HIGH_SURPRISAL_FOR_REPLACE = 10
|
|
||||||
INCLUDE_LEVELS = ["explicit", "deductive"]
|
|
||||||
|
|
||||||
# Webhook settings
|
# Webhook settings
|
||||||
[webhook]
|
[webhook]
|
||||||
SECRET = ""
|
SECRET = ""
|
||||||
MAX_WORKSPACE_LIMIT = 10
|
MAX_WORKSPACE_LIMIT = 10
|
||||||
|
|
||||||
# Prometheus metrics settings (pull-based metrics)
|
# Metrics settings
|
||||||
[metrics]
|
[metrics]
|
||||||
ENABLED = false
|
ENABLED = false
|
||||||
# NAMESPACE = "honcho" # Inherits from app.NAMESPACE if not set
|
|
||||||
|
|
||||||
# CloudEvents telemetry settings (analytics events)
|
|
||||||
[telemetry]
|
|
||||||
ENABLED = false
|
|
||||||
# ENDPOINT = "https://telemetry.honcho.dev/v1/events"
|
|
||||||
# HEADERS = '{"Authorization": "Bearer your-token"}' # JSON string for auth headers
|
|
||||||
BATCH_SIZE = 100
|
|
||||||
FLUSH_INTERVAL_SECONDS = 1.0
|
|
||||||
FLUSH_THRESHOLD = 50
|
|
||||||
MAX_RETRIES = 3
|
|
||||||
MAX_BUFFER_SIZE = 10000
|
|
||||||
# NAMESPACE = "honcho" # Inherits from app.NAMESPACE if not set
|
|
||||||
|
|
||||||
# Cache settings
|
|
||||||
[cache]
|
|
||||||
ENABLED = false
|
|
||||||
URL = "redis://localhost:6379/0?suppress=true"
|
|
||||||
# NAMESPACE = "honcho" # Inherits from app.NAMESPACE if not set
|
|
||||||
DEFAULT_TTL_SECONDS = 300
|
|
||||||
DEFAULT_LOCK_TTL_SECONDS = 5
|
|
||||||
|
|
||||||
# Vector store settings
|
|
||||||
[vector_store]
|
|
||||||
# Vector store type: "pgvector", "turbopuffer", or "lancedb"
|
|
||||||
TYPE = "pgvector"
|
|
||||||
# Migration flag: set to true when migration from pgvector is complete
|
|
||||||
MIGRATED = false
|
|
||||||
NAMESPACE = "honcho"
|
NAMESPACE = "honcho"
|
||||||
# DIMENSIONS is deprecated; embedding.vector_dimensions is authoritative.
|
|
||||||
# TURBOPUFFER_API_KEY = "your-turbopuffer-api-key"
|
|
||||||
# TURBOPUFFER_REGION = "us-east-1"
|
|
||||||
LANCEDB_PATH = "./lancedb_data"
|
|
||||||
RECONCILIATION_INTERVAL_SECONDS = 300
|
|
||||||
|
|
|
||||||
|
|
@ -1,180 +1,47 @@
|
||||||
# Honcho Docker Compose
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# cp docker-compose.yml.example docker-compose.yml
|
|
||||||
# cp .env.template .env # edit with your provider config
|
|
||||||
# docker compose up -d --build
|
|
||||||
# INSTALL_LANCEDB=true docker compose up -d --build # optional local vector store
|
|
||||||
#
|
|
||||||
# By default, ports are bound to 127.0.0.1 (localhost only).
|
|
||||||
# For development, uncomment the source mounts and monitoring services below.
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
api:
|
api:
|
||||||
|
image: honcho:latest
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
|
||||||
INSTALL_LANCEDB: ${INSTALL_LANCEDB:-false}
|
|
||||||
entrypoint: ["sh", "docker/entrypoint.sh"]
|
|
||||||
depends_on:
|
depends_on:
|
||||||
database:
|
database:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8000:8000"
|
- 8000:8000
|
||||||
healthcheck:
|
|
||||||
test:
|
|
||||||
[
|
|
||||||
"CMD",
|
|
||||||
"/app/.venv/bin/python",
|
|
||||||
"-c",
|
|
||||||
"import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=2).read()",
|
|
||||||
]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 10s
|
|
||||||
volumes:
|
volumes:
|
||||||
# Shared LanceDB data (used when VECTOR_STORE_TYPE=lancedb)
|
- .:/app
|
||||||
- lancedb-data:/app/lancedb_data
|
|
||||||
# -- Development: mount source for live reload --
|
|
||||||
# - .:/app
|
|
||||||
# - venv:/app/.venv
|
|
||||||
environment:
|
|
||||||
- DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
|
|
||||||
- CACHE_URL=redis://redis:6379/0?suppress=true
|
|
||||||
- CACHE_ENABLED=true
|
|
||||||
env_file:
|
env_file:
|
||||||
- path: .env
|
- .env
|
||||||
required: false
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
deriver:
|
deriver:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
entrypoint: ["uv", "run", "python", "-m", "src.deriver"]
|
||||||
INSTALL_LANCEDB: ${INSTALL_LANCEDB:-false}
|
|
||||||
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
|
|
||||||
depends_on:
|
depends_on:
|
||||||
api:
|
|
||||||
condition: service_healthy
|
|
||||||
database:
|
database:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
volumes:
|
volumes:
|
||||||
# Shared LanceDB data (used when VECTOR_STORE_TYPE=lancedb)
|
- .:/app
|
||||||
- lancedb-data:/app/lancedb_data
|
|
||||||
# -- Development: mount source for live reload --
|
|
||||||
# - .:/app
|
|
||||||
# - venv:/app/.venv
|
|
||||||
environment:
|
|
||||||
- DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
|
|
||||||
- CACHE_URL=redis://redis:6379/0?suppress=true
|
|
||||||
- CACHE_ENABLED=true
|
|
||||||
env_file:
|
env_file:
|
||||||
- path: .env
|
- .env
|
||||||
required: false
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
mcp:
|
|
||||||
build:
|
|
||||||
context: ./mcp
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
depends_on:
|
|
||||||
api:
|
|
||||||
condition: service_healthy
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:3000:3000"
|
|
||||||
environment:
|
|
||||||
- HONCHO_API_URL=http://api:8000
|
|
||||||
env_file:
|
|
||||||
- path: .env
|
|
||||||
required: false
|
|
||||||
healthcheck:
|
|
||||||
test:
|
|
||||||
[
|
|
||||||
"CMD",
|
|
||||||
"bun",
|
|
||||||
"-e",
|
|
||||||
"fetch('http://127.0.0.1:3000/health').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))",
|
|
||||||
]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 10s
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
database:
|
database:
|
||||||
image: pgvector/pgvector:pg15
|
image: pgvector/pgvector:pg15
|
||||||
restart: unless-stopped
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:5432:5432"
|
- 5432:5432
|
||||||
command: ["postgres", "-c", "max_connections=200"]
|
command: ["postgres", "-c", "max_connections=800"]
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_DB=postgres
|
- POSTGRES_DB=honcho
|
||||||
- POSTGRES_USER=postgres
|
- POSTGRES_USER=testuser
|
||||||
- POSTGRES_PASSWORD=postgres
|
- POSTGRES_PASSWORD=testpwd
|
||||||
# Allow passwordless connections from the host (port is bound to 127.0.0.1).
|
|
||||||
# Lets the local test suite and ad-hoc tools connect without supplying a
|
|
||||||
# password. Do NOT use this in production.
|
|
||||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||||
volumes:
|
volumes:
|
||||||
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
|
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||||
- pgdata:/var/lib/postgresql/data/
|
- ./data:/var/lib/postgresql/data/
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
|
test: ["CMD-SHELL", "pg_isready -U testuser -d honcho"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:8.2
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:6379:6379"
|
|
||||||
volumes:
|
|
||||||
- redis-data:/data
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "redis-cli ping"]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
# -- Development: monitoring stack (uncomment to enable) --
|
|
||||||
# prometheus:
|
|
||||||
# image: prom/prometheus:v3.2.1
|
|
||||||
# ports:
|
|
||||||
# - "127.0.0.1:9090:9090"
|
|
||||||
# volumes:
|
|
||||||
# - ./docker/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
|
||||||
# - prometheus-data:/prometheus
|
|
||||||
# depends_on:
|
|
||||||
# api:
|
|
||||||
# condition: service_started
|
|
||||||
# grafana:
|
|
||||||
# image: grafana/grafana:11.4.0
|
|
||||||
# ports:
|
|
||||||
# - "127.0.0.1:3000:3000"
|
|
||||||
# environment:
|
|
||||||
# - GF_SECURITY_ADMIN_USER=admin
|
|
||||||
# - GF_SECURITY_ADMIN_PASSWORD=admin
|
|
||||||
# - GF_AUTH_ANONYMOUS_ENABLED=true
|
|
||||||
# - GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
|
|
||||||
# volumes:
|
|
||||||
# - ./docker/grafana-datasource.yml:/etc/grafana/provisioning/datasources/datasource.yml:ro
|
|
||||||
# depends_on:
|
|
||||||
# prometheus:
|
|
||||||
# condition: service_started
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
pgdata:
|
|
||||||
redis-data:
|
|
||||||
lancedb-data:
|
|
||||||
# -- Development: uncomment if using source mounts --
|
|
||||||
# venv:
|
|
||||||
# prometheus-data:
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "Running database migrations..."
|
|
||||||
/app/.venv/bin/python scripts/provision_db.py
|
|
||||||
|
|
||||||
echo "Starting API server..."
|
|
||||||
exec /app/.venv/bin/fastapi run --host 0.0.0.0 --workers "${API_WORKERS:-1}" src/main.py
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
apiVersion: 1
|
|
||||||
|
|
||||||
datasources:
|
|
||||||
- name: Prometheus
|
|
||||||
type: prometheus
|
|
||||||
access: proxy
|
|
||||||
url: http://prometheus:9090
|
|
||||||
isDefault: true
|
|
||||||
editable: false
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
global:
|
|
||||||
scrape_interval: 15s
|
|
||||||
|
|
||||||
scrape_configs:
|
|
||||||
- job_name: honcho-api
|
|
||||||
static_configs:
|
|
||||||
- targets: ["api:8000"]
|
|
||||||
- job_name: honcho-deriver
|
|
||||||
static_configs:
|
|
||||||
- targets: ["deriver:9090"]
|
|
||||||
398
docs/bun.lock
|
|
@ -1,14 +1,14 @@
|
||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"configVersion": 0,
|
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "honcho-docs",
|
"name": "honcho-docs",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mintlify/scraping": "^4.0.467",
|
"@mintlify/scraping": "^4.0.284",
|
||||||
|
"honcho-ai": "^0.0.11",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mint": "^4.2.204",
|
"mint": "^4.2.123",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
|
|
||||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||||
|
|
||||||
"@ark/schema": ["@ark/schema@0.55.0", "", { "dependencies": { "@ark/util": "0.55.0" } }, "sha512-IlSIc0FmLKTDGr4I/FzNHauMn0MADA6bCjT1wauu4k6MyxhC1R9gz0olNpIRvK7lGGDwtc/VO0RUDNvVQW5WFg=="],
|
"@ark/schema": ["@ark/schema@0.49.0", "", { "dependencies": { "@ark/util": "0.49.0" } }, "sha512-GphZBLpW72iS0v4YkeUtV3YIno35Gimd7+ezbPO9GwEi9kzdUrPVjvf6aXSBAfHikaFc/9pqZOpv3pOXnC71tw=="],
|
||||||
|
|
||||||
"@ark/util": ["@ark/util@0.55.0", "", {}, "sha512-aWFNK7aqSvqFtVsl1xmbTjGbg91uqtJV7Za76YGNEwIO4qLjMfyY8flmmbhooYMuqPCO2jyxu8hve943D+w3bA=="],
|
"@ark/util": ["@ark/util@0.49.0", "", {}, "sha512-/BtnX7oCjNkxi2vi6y1399b+9xd1jnCrDYhZ61f0a+3X8x8DxlK52VgEEzyuC2UQMPACIfYrmHkhD3lGt2GaMA=="],
|
||||||
|
|
||||||
"@asyncapi/parser": ["@asyncapi/parser@3.4.0", "", { "dependencies": { "@asyncapi/specs": "^6.8.0", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json": "3.21.0", "@stoplight/json-ref-readers": "^1.2.2", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.18.3", "@stoplight/spectral-functions": "^1.7.2", "@stoplight/spectral-parsers": "^1.0.2", "@stoplight/spectral-ref-resolver": "^1.0.3", "@stoplight/types": "^13.12.0", "@types/json-schema": "^7.0.11", "@types/urijs": "^1.19.19", "ajv": "^8.17.1", "ajv-errors": "^3.0.0", "ajv-formats": "^2.1.1", "avsc": "^5.7.5", "js-yaml": "^4.1.0", "jsonpath-plus": "^10.0.0", "node-fetch": "2.6.7" } }, "sha512-Sxn74oHiZSU6+cVeZy62iPZMFMvKp4jupMFHelSICCMw1qELmUHPvuZSr+ZHDmNGgHcEpzJM5HN02kR7T4g+PQ=="],
|
"@asyncapi/parser": ["@asyncapi/parser@3.4.0", "", { "dependencies": { "@asyncapi/specs": "^6.8.0", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json": "3.21.0", "@stoplight/json-ref-readers": "^1.2.2", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.18.3", "@stoplight/spectral-functions": "^1.7.2", "@stoplight/spectral-parsers": "^1.0.2", "@stoplight/spectral-ref-resolver": "^1.0.3", "@stoplight/types": "^13.12.0", "@types/json-schema": "^7.0.11", "@types/urijs": "^1.19.19", "ajv": "^8.17.1", "ajv-errors": "^3.0.0", "ajv-formats": "^2.1.1", "avsc": "^5.7.5", "js-yaml": "^4.1.0", "jsonpath-plus": "^10.0.0", "node-fetch": "2.6.7" } }, "sha512-Sxn74oHiZSU6+cVeZy62iPZMFMvKp4jupMFHelSICCMw1qELmUHPvuZSr+ZHDmNGgHcEpzJM5HN02kR7T4g+PQ=="],
|
||||||
|
|
||||||
|
|
@ -29,8 +29,6 @@
|
||||||
|
|
||||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
|
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
|
||||||
|
|
||||||
"@canvas/image-data": ["@canvas/image-data@1.1.0", "", {}, "sha512-QdObRRjRbcXGmM1tmJ+MrHcaz1MftF2+W7YI+MsphnsCrmtyfS0d5qJbk0MeSbUeyM/jCb0hmnkXPsy026L7dA=="],
|
|
||||||
|
|
||||||
"@emnapi/runtime": ["@emnapi/runtime@1.4.5", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="],
|
"@emnapi/runtime": ["@emnapi/runtime@1.4.5", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="],
|
||||||
|
|
||||||
"@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
|
"@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
|
||||||
|
|
@ -79,35 +77,31 @@
|
||||||
|
|
||||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
|
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
|
||||||
|
|
||||||
"@inquirer/ansi": ["@inquirer/ansi@1.0.2", "", {}, "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ=="],
|
"@inquirer/checkbox": ["@inquirer/checkbox@4.2.0", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA=="],
|
||||||
|
|
||||||
"@inquirer/checkbox": ["@inquirer/checkbox@4.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/core": "^10.3.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA=="],
|
"@inquirer/confirm": ["@inquirer/confirm@5.1.14", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q=="],
|
||||||
|
|
||||||
"@inquirer/confirm": ["@inquirer/confirm@5.1.21", "", { "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ=="],
|
|
||||||
|
|
||||||
"@inquirer/core": ["@inquirer/core@10.1.15", "", { "dependencies": { "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA=="],
|
"@inquirer/core": ["@inquirer/core@10.1.15", "", { "dependencies": { "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA=="],
|
||||||
|
|
||||||
"@inquirer/editor": ["@inquirer/editor@4.2.23", "", { "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/external-editor": "^1.0.3", "@inquirer/type": "^3.0.10" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ=="],
|
"@inquirer/editor": ["@inquirer/editor@4.2.15", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "external-editor": "^3.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ=="],
|
||||||
|
|
||||||
"@inquirer/expand": ["@inquirer/expand@4.0.23", "", { "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew=="],
|
"@inquirer/expand": ["@inquirer/expand@4.0.17", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw=="],
|
||||||
|
|
||||||
"@inquirer/external-editor": ["@inquirer/external-editor@1.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA=="],
|
"@inquirer/figures": ["@inquirer/figures@1.0.13", "", {}, "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw=="],
|
||||||
|
|
||||||
"@inquirer/figures": ["@inquirer/figures@1.0.15", "", {}, "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g=="],
|
"@inquirer/input": ["@inquirer/input@4.2.1", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow=="],
|
||||||
|
|
||||||
"@inquirer/input": ["@inquirer/input@4.3.1", "", { "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g=="],
|
"@inquirer/number": ["@inquirer/number@3.0.17", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg=="],
|
||||||
|
|
||||||
"@inquirer/number": ["@inquirer/number@3.0.23", "", { "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg=="],
|
"@inquirer/password": ["@inquirer/password@4.0.17", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA=="],
|
||||||
|
|
||||||
"@inquirer/password": ["@inquirer/password@4.0.23", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA=="],
|
"@inquirer/prompts": ["@inquirer/prompts@7.7.1", "", { "dependencies": { "@inquirer/checkbox": "^4.2.0", "@inquirer/confirm": "^5.1.14", "@inquirer/editor": "^4.2.15", "@inquirer/expand": "^4.0.17", "@inquirer/input": "^4.2.1", "@inquirer/number": "^3.0.17", "@inquirer/password": "^4.0.17", "@inquirer/rawlist": "^4.1.5", "@inquirer/search": "^3.0.17", "@inquirer/select": "^4.3.1" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-XDxPrEWeWUBy8scAXzXuFY45r/q49R0g72bUzgQXZ1DY/xEFX+ESDMkTQolcb5jRBzaNJX2W8XQl6krMNDTjaA=="],
|
||||||
|
|
||||||
"@inquirer/prompts": ["@inquirer/prompts@7.10.1", "", { "dependencies": { "@inquirer/checkbox": "^4.3.2", "@inquirer/confirm": "^5.1.21", "@inquirer/editor": "^4.2.23", "@inquirer/expand": "^4.0.23", "@inquirer/input": "^4.3.1", "@inquirer/number": "^3.0.23", "@inquirer/password": "^4.0.23", "@inquirer/rawlist": "^4.1.11", "@inquirer/search": "^3.2.2", "@inquirer/select": "^4.4.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg=="],
|
"@inquirer/rawlist": ["@inquirer/rawlist@4.1.5", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA=="],
|
||||||
|
|
||||||
"@inquirer/rawlist": ["@inquirer/rawlist@4.1.11", "", { "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw=="],
|
"@inquirer/search": ["@inquirer/search@3.0.17", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-CuBU4BAGFqRYors4TNCYzy9X3DpKtgIW4Boi0WNkm4Ei1hvY9acxKdBdyqzqBCEe4YxSdaQQsasJlFlUJNgojw=="],
|
||||||
|
|
||||||
"@inquirer/search": ["@inquirer/search@3.2.2", "", { "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA=="],
|
"@inquirer/select": ["@inquirer/select@4.3.1", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA=="],
|
||||||
|
|
||||||
"@inquirer/select": ["@inquirer/select@4.4.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/core": "^10.3.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w=="],
|
|
||||||
|
|
||||||
"@inquirer/type": ["@inquirer/type@3.0.8", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw=="],
|
"@inquirer/type": ["@inquirer/type@3.0.8", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw=="],
|
||||||
|
|
||||||
|
|
@ -133,25 +127,25 @@
|
||||||
|
|
||||||
"@mdx-js/react": ["@mdx-js/react@3.1.0", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ=="],
|
"@mdx-js/react": ["@mdx-js/react@3.1.0", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ=="],
|
||||||
|
|
||||||
"@mintlify/cli": ["@mintlify/cli@4.0.808", "", { "dependencies": { "@inquirer/prompts": "^7.9.0", "@mintlify/common": "1.0.607", "@mintlify/link-rot": "3.0.750", "@mintlify/models": "0.0.240", "@mintlify/prebuild": "1.0.736", "@mintlify/previewing": "4.0.786", "@mintlify/validation": "0.1.521", "adm-zip": "^0.5.10", "chalk": "^5.2.0", "color": "^4.2.3", "detect-port": "^1.5.1", "fs-extra": "^11.2.0", "gray-matter": "^4.0.3", "ink": "^6.0.1", "inquirer": "^12.3.0", "js-yaml": "^4.1.0", "mdast-util-mdx-jsx": "^3.2.0", "react": "^19.1.0", "semver": "^7.7.2", "unist-util-visit": "^5.0.0", "yargs": "^17.6.0" }, "bin": { "mint": "bin/index.js", "mintlify": "bin/index.js" } }, "sha512-oVd+33DuORSXQPyVhX9VamME+qZkbGwSGAaas3LaNoabGxw9O9Nb34KYDvABvVOf9LqHCy/O+FhmegW+zr3upQ=="],
|
"@mintlify/cli": ["@mintlify/cli@4.0.727", "", { "dependencies": { "@mintlify/common": "1.0.537", "@mintlify/link-rot": "3.0.674", "@mintlify/models": "0.0.229", "@mintlify/prebuild": "1.0.661", "@mintlify/previewing": "4.0.710", "@mintlify/validation": "0.1.471", "chalk": "^5.2.0", "detect-port": "^1.5.1", "fs-extra": "^11.2.0", "gray-matter": "^4.0.3", "ink": "^6.0.1", "inquirer": "^12.3.0", "js-yaml": "^4.1.0", "react": "^19.1.0", "semver": "^7.7.2", "yargs": "^17.6.0" }, "bin": { "mint": "bin/index.js", "mintlify": "bin/index.js" } }, "sha512-6iplgwOC9wK1FFdSFE9NX92qhxF0TkuZADf2rVkSR/A3MQ9LzTh9iKttRWD0q66pQ8l2kwOhzwqt/tW22ZLmcA=="],
|
||||||
|
|
||||||
"@mintlify/common": ["@mintlify/common@1.0.607", "", { "dependencies": { "@asyncapi/parser": "^3.4.0", "@mintlify/mdx": "^3.0.1", "@mintlify/models": "0.0.240", "@mintlify/openapi-parser": "^0.0.8", "@mintlify/validation": "0.1.521", "@sindresorhus/slugify": "^2.1.1", "acorn": "^8.11.2", "acorn-jsx": "^5.3.2", "color-blend": "^4.0.0", "estree-util-to-js": "^2.0.0", "estree-walker": "^3.0.3", "gray-matter": "^4.0.3", "hast-util-from-html": "^2.0.3", "hast-util-to-html": "^9.0.4", "hast-util-to-text": "^4.0.2", "hex-rgb": "^5.0.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.0.0", "mdast-util-mdx": "^3.0.0", "mdast-util-mdx-jsx": "^3.1.3", "micromark-extension-gfm": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.1", "micromark-extension-mdxjs": "^3.0.0", "openapi-types": "^12.0.0", "postcss": "^8.5.6", "remark": "^15.0.1", "remark-frontmatter": "^5.0.0", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-mdx": "^3.1.0", "remark-stringify": "^11.0.0", "tailwindcss": "^3.4.4", "unified": "^11.0.5", "unist-builder": "^4.0.0", "unist-util-map": "^4.0.0", "unist-util-remove": "^4.0.0", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-9Yc8piWlOTSyapcV1MsjnsDZmaHlgP1cJ2IqtNd5+dvuy6pFz2TG8ELJxg9k9rf25KleOvI+QN3pJ8pHbnJWtg=="],
|
"@mintlify/common": ["@mintlify/common@1.0.461", "", { "dependencies": { "@asyncapi/parser": "^3.4.0", "@mintlify/mdx": "^2.0.3", "@mintlify/models": "0.0.213", "@mintlify/openapi-parser": "^0.0.7", "@mintlify/validation": "0.1.424", "@sindresorhus/slugify": "^2.1.1", "acorn": "^8.11.2", "acorn-jsx": "^5.3.2", "estree-util-to-js": "^2.0.0", "estree-walker": "^3.0.3", "gray-matter": "^4.0.3", "hast-util-from-html": "^2.0.3", "hast-util-to-html": "^9.0.4", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "mdast": "^3.0.0", "mdast-util-from-markdown": "^2.0.2", "mdast-util-mdx": "^3.0.0", "mdast-util-mdx-jsx": "^3.1.3", "micromark-extension-mdx-jsx": "^3.0.1", "openapi-types": "^12.0.0", "remark": "^15.0.1", "remark-frontmatter": "^5.0.0", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-mdx": "^3.1.0", "remark-stringify": "^11.0.0", "unified": "^11.0.5", "unist-builder": "^4.0.0", "unist-util-map": "^4.0.0", "unist-util-remove": "^4.0.0", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-tDKqkB5RolG0INcUwpLjVb8BuhUt+KyJ8FH+W0UUw267olEaKnU8u8TYVVclHHvRd6jvYzaXjGdGARx2CN3pgQ=="],
|
||||||
|
|
||||||
"@mintlify/link-rot": ["@mintlify/link-rot@3.0.750", "", { "dependencies": { "@mintlify/common": "1.0.607", "@mintlify/prebuild": "1.0.736", "@mintlify/previewing": "4.0.786", "@mintlify/validation": "0.1.521", "fs-extra": "^11.1.0", "unist-util-visit": "^4.1.1" } }, "sha512-IkrpTs29C+ouUgyp9p2zGE80AfRlgOgkffzKcDi+UBkSrSSVA82ub3CaA+Lk5Zyrhr5t5aI1wx2Z9FnLmvuNwg=="],
|
"@mintlify/link-rot": ["@mintlify/link-rot@3.0.674", "", { "dependencies": { "@mintlify/common": "1.0.537", "@mintlify/prebuild": "1.0.661", "@mintlify/previewing": "4.0.710", "@mintlify/validation": "0.1.471", "fs-extra": "^11.1.0", "unist-util-visit": "^4.1.1" } }, "sha512-QzbMAva0GdbBBG6R+pWmHrVzNdR08ug6e6a5Tnxk1NaNr8+/YR7cehu5xdAX31aO6n+CIv8Ot5HxzsqsY2vwtA=="],
|
||||||
|
|
||||||
"@mintlify/mdx": ["@mintlify/mdx@3.0.3", "", { "dependencies": { "@shikijs/transformers": "^3.11.0", "@shikijs/twoslash": "^3.12.2", "arktype": "^2.1.26", "hast-util-to-string": "^3.0.1", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.1.0", "mdast-util-mdx-jsx": "^3.2.0", "mdast-util-to-hast": "^13.2.0", "next-mdx-remote-client": "^1.0.3", "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-smartypants": "^3.0.2", "shiki": "^3.11.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@radix-ui/react-popover": "^1.1.15", "react": "^18.3.1", "react-dom": "^18.3.1" } }, "sha512-YwvZZ/2CJG+MT2sWyKOXAEk/nS5lzq3ACUerqD8xtPtnMMCgqoSQ/Y8pA32OfTAHFMsiIwqI3NNWYFLEftyrWg=="],
|
"@mintlify/mdx": ["@mintlify/mdx@2.0.3", "", { "dependencies": { "@shikijs/transformers": "^3.6.0", "hast-util-to-string": "^3.0.1", "mdast-util-mdx-jsx": "^3.2.0", "next-mdx-remote-client": "^1.0.3", "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-smartypants": "^3.0.2", "shiki": "^3.6.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "react": "^18.3.1", "react-dom": "^18.3.1" } }, "sha512-UGlwavma8QooWAlhtXpTAG5MAUZTTUKI8Qu25Wqfp1HMOPrYGvo5YQPmlqqogbMsqDMcFPLP/ZYnaZsGUYBspQ=="],
|
||||||
|
|
||||||
"@mintlify/models": ["@mintlify/models@0.0.240", "", { "dependencies": { "axios": "^1.8.3", "openapi-types": "^12.0.0" } }, "sha512-9j8UfcYw+pD5D5qhB/iPywSpnB/sgwft+mUc08mWS+Tol19smROa901Myy0yLT0NZPfoZVfSSLp8J5LTloljpA=="],
|
"@mintlify/models": ["@mintlify/models@0.0.229", "", { "dependencies": { "axios": "^1.8.3", "openapi-types": "^12.0.0" } }, "sha512-1P3R6dQFNzjTbmVDCQf/vAGFGOEUdUv6sCaJAmZCNWY2mhwgvDU/Oa2YLiNmVrAqnWDH1Pkz5nq+i7gClrdXgA=="],
|
||||||
|
|
||||||
"@mintlify/openapi-parser": ["@mintlify/openapi-parser@0.0.8", "", { "dependencies": { "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", "yaml": "^2.4.5" } }, "sha512-9MBRq9lS4l4HITYCrqCL7T61MOb20q9IdU7HWhqYMNMM1jGO1nHjXasFy61yZ8V6gMZyyKQARGVoZ0ZrYN48Og=="],
|
"@mintlify/openapi-parser": ["@mintlify/openapi-parser@0.0.7", "", { "dependencies": { "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", "yaml": "^2.4.5" } }, "sha512-3ecbkzPbsnkKVZJypVL0H5pCTR7a4iLv4cP7zbffzAwy+vpH70JmPxNVpPPP62yLrdZlfNcMxu5xKeT7fllgMg=="],
|
||||||
|
|
||||||
"@mintlify/prebuild": ["@mintlify/prebuild@1.0.736", "", { "dependencies": { "@mintlify/common": "1.0.607", "@mintlify/openapi-parser": "^0.0.8", "@mintlify/scraping": "4.0.467", "@mintlify/validation": "0.1.521", "chalk": "^5.3.0", "favicons": "^7.2.0", "fs-extra": "^11.1.0", "gray-matter": "^4.0.3", "js-yaml": "^4.1.0", "openapi-types": "^12.0.0", "sharp": "^0.33.1", "sharp-ico": "^0.1.5", "unist-util-visit": "^4.1.1", "uuid": "^11.1.0" } }, "sha512-ih38FjriVUpujDqrc6v9Yt4H1eC4ByGL5MVDSWy++RIkjRZLyPULnEOh1IcGqJ9zSYJOPtPgoIsTIta71/yZsw=="],
|
"@mintlify/prebuild": ["@mintlify/prebuild@1.0.661", "", { "dependencies": { "@mintlify/common": "1.0.537", "@mintlify/openapi-parser": "^0.0.7", "@mintlify/scraping": "4.0.396", "@mintlify/validation": "0.1.471", "chalk": "^5.3.0", "favicons": "^7.2.0", "fs-extra": "^11.1.0", "gray-matter": "^4.0.3", "js-yaml": "^4.1.0", "mdast": "^3.0.0", "openapi-types": "^12.0.0", "unist-util-visit": "^4.1.1" } }, "sha512-hcYLxhf53RV6hecJLIEdG7ajQuTtDj3vuoueeLmhcVXEDYkvKKa36EOk9/olLUDvjqsENnK601NmdmHPd80pJA=="],
|
||||||
|
|
||||||
"@mintlify/previewing": ["@mintlify/previewing@4.0.786", "", { "dependencies": { "@mintlify/common": "1.0.607", "@mintlify/prebuild": "1.0.736", "@mintlify/validation": "0.1.521", "better-opn": "^3.0.2", "chalk": "^5.1.0", "chokidar": "^3.5.3", "express": "^4.18.2", "fs-extra": "^11.1.0", "got": "^13.0.0", "gray-matter": "^4.0.3", "ink": "^6.0.1", "ink-spinner": "^5.0.0", "is-online": "^10.0.0", "js-yaml": "^4.1.0", "openapi-types": "^12.0.0", "react": "^19.1.0", "socket.io": "^4.7.2", "tar": "^6.1.15", "unist-util-visit": "^4.1.1", "yargs": "^17.6.0" } }, "sha512-OPVm66QdNdNjDcyv2iWA3/fJUfyBGvm6XKiORDhlW+dqiFX5aTDLEithsclmKz5r2JEpIqIJNSeiudpY8uGHAg=="],
|
"@mintlify/previewing": ["@mintlify/previewing@4.0.710", "", { "dependencies": { "@mintlify/common": "1.0.537", "@mintlify/prebuild": "1.0.661", "@mintlify/validation": "0.1.471", "better-opn": "^3.0.2", "chalk": "^5.1.0", "chokidar": "^3.5.3", "express": "^4.18.2", "fs-extra": "^11.1.0", "got": "^13.0.0", "gray-matter": "^4.0.3", "ink": "^6.0.1", "ink-spinner": "^5.0.0", "is-online": "^10.0.0", "js-yaml": "^4.1.0", "mdast": "^3.0.0", "openapi-types": "^12.0.0", "react": "^19.1.0", "socket.io": "^4.7.2", "tar": "^6.1.15", "unist-util-visit": "^4.1.1", "yargs": "^17.6.0" } }, "sha512-3SyO58i7kmR4W+UCcP9gq/wTKsJ0Vs+pudFnfJt5Qk1QIMTXbZGjC/ojaYDbGqSt+VOmIPwbGchQzZrAp1aXbA=="],
|
||||||
|
|
||||||
"@mintlify/scraping": ["@mintlify/scraping@4.0.467", "", { "dependencies": { "@mintlify/common": "1.0.607", "@mintlify/openapi-parser": "^0.0.8", "fs-extra": "^11.1.1", "hast-util-to-mdast": "^10.1.0", "js-yaml": "^4.1.0", "mdast-util-mdx-jsx": "^3.1.3", "neotraverse": "^0.6.18", "puppeteer": "^22.14.0", "rehype-parse": "^9.0.0", "remark-gfm": "^4.0.0", "remark-mdx": "^3.0.1", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "yargs": "^17.6.0", "zod": "^3.20.6" }, "bin": { "mintlify-scrape": "bin/cli.js" } }, "sha512-UnanSRzG5gDef9NFlSO6F00JznCAEEb1onbuidfzQ9sBPzG/a+g+0K4Z6VdbeNCFKmirx2t1qm1gujkh8XIfog=="],
|
"@mintlify/scraping": ["@mintlify/scraping@4.0.317", "", { "dependencies": { "@mintlify/common": "1.0.461", "@mintlify/openapi-parser": "^0.0.7", "fs-extra": "^11.1.1", "hast-util-to-mdast": "^10.1.0", "js-yaml": "^4.1.0", "mdast-util-mdx-jsx": "^3.1.3", "neotraverse": "^0.6.18", "puppeteer": "^22.14.0", "rehype-parse": "^9.0.0", "remark-gfm": "^4.0.0", "remark-mdx": "^3.0.1", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "yargs": "^17.6.0", "zod": "^3.20.6" }, "bin": { "mintlify-scrape": "bin/cli.js" } }, "sha512-WVgReuvckQMgWkbR8JrGQp5l1cs4WB+V8+ZWjQHhCmyjp+Kv+2zVKs9dw2/yiWjZYiYytu6if7Dise9sbLuMbQ=="],
|
||||||
|
|
||||||
"@mintlify/validation": ["@mintlify/validation@0.1.521", "", { "dependencies": { "@mintlify/mdx": "^3.0.1", "@mintlify/models": "0.0.240", "arktype": "^2.1.20", "js-yaml": "^4.1.0", "lcm": "^0.0.3", "lodash": "^4.17.21", "object-hash": "^3.0.0", "openapi-types": "^12.0.0", "uuid": "^11.1.0", "zod": "^3.20.6", "zod-to-json-schema": "^3.20.3" } }, "sha512-8icZULy+5CXGucFNo7mTWEgzif/73Lvbb8pNuo7MBL81kbvixPWKX3j5TiSLkVCUjwUGIglr9IKc9+RXxwN09A=="],
|
"@mintlify/validation": ["@mintlify/validation@0.1.471", "", { "dependencies": { "@mintlify/models": "0.0.229", "arktype": "^2.1.20", "lcm": "^0.0.3", "lodash": "^4.17.21", "openapi-types": "^12.0.0", "zod": "^3.20.6", "zod-to-json-schema": "^3.20.3" } }, "sha512-lf4zp9sJspXmDA9HH9VaJfK4ll+BaaH9XxuU2SVNuploKjRKmpHYFfN9YI42pA2bda/X32rkqDZSRI+JHdQcNg=="],
|
||||||
|
|
||||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||||
|
|
||||||
|
|
@ -209,21 +203,21 @@
|
||||||
|
|
||||||
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
||||||
|
|
||||||
"@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
"@shikijs/core": ["@shikijs/core@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-uTSXzUBQ/IgFcUa6gmGShCHr4tMdR3pxUiiWKDm8pd42UKJdYhkAYsAmHX5mTwybQ5VyGDgTjW4qKSsRvGSang=="],
|
||||||
|
|
||||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
|
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-rZRp3BM1llrHkuBPAdYAzjlF7OqlM0rm/7EWASeCcY7cRYZIrOnGIHE9qsLz5TCjGefxBFnwgIECzBs2vmOyKA=="],
|
||||||
|
|
||||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
|
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-KGQJZHlNY7c656qPFEQpIoqOuC4LrxjyNndRdzk5WKB/Ie87+NJCF1xo9KkOUxwxylk7rT6nhlZyTGTC4fCe1g=="],
|
||||||
|
|
||||||
"@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
|
"@shikijs/langs": ["@shikijs/langs@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1" } }, "sha512-TjOFg2Wp1w07oKnXjs0AUMb4kJvujML+fJ1C5cmEj45lhjbUXtziT1x2bPQb9Db6kmPhkG5NI2tgYW1/DzhUuQ=="],
|
||||||
|
|
||||||
"@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
|
"@shikijs/themes": ["@shikijs/themes@3.8.1", "", { "dependencies": { "@shikijs/types": "3.8.1" } }, "sha512-Vu3t3BBLifc0GB0UPg2Pox1naTemrrvyZv2lkiSw3QayVV60me1ujFQwPZGgUTmwXl1yhCPW8Lieesm0CYruLQ=="],
|
||||||
|
|
||||||
"@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="],
|
"@shikijs/transformers": ["@shikijs/transformers@3.8.1", "", { "dependencies": { "@shikijs/core": "3.8.1", "@shikijs/types": "3.8.1" } }, "sha512-nmTyFfBrhJk6HJi118jes0wuWdfKXeVUq1Nq+hm8h6wbk1KUfvtg+LY/uDfxZD2VDItHO3QoINIs3NtoKBmgxw=="],
|
||||||
|
|
||||||
"@shikijs/twoslash": ["@shikijs/twoslash@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0", "twoslash": "^0.3.4" }, "peerDependencies": { "typescript": ">=5.5.0" } }, "sha512-OmNKNoZ8Hevt4VKQHfJL+hrsrqLSnW/Nz7RMutuBqXBCIYZWk80HnF9pcXEwRmy9MN0MGRmZCW2rDDP8K7Bxkw=="],
|
"@shikijs/twoslash": ["@shikijs/twoslash@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0", "twoslash": "^0.3.4" }, "peerDependencies": { "typescript": ">=5.5.0" } }, "sha512-OmNKNoZ8Hevt4VKQHfJL+hrsrqLSnW/Nz7RMutuBqXBCIYZWk80HnF9pcXEwRmy9MN0MGRmZCW2rDDP8K7Bxkw=="],
|
||||||
|
|
||||||
"@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
"@shikijs/types": ["@shikijs/types@3.8.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-5C39Q8/8r1I26suLh+5TPk1DTrbY/kn3IdWA5HdizR0FhlhD05zx5nKCqhzSfDHH3p4S0ZefxWd77DLV+8FhGg=="],
|
||||||
|
|
||||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||||
|
|
||||||
|
|
@ -297,6 +291,8 @@
|
||||||
|
|
||||||
"@types/node": ["@types/node@18.19.120", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-WtCGHFXnVI8WHLxDAt5TbnCM4eSE+nI0QN2NJtwzcgMhht2eNz6V9evJrk+lwC8bCY8OWV5Ym8Jz7ZEyGnKnMA=="],
|
"@types/node": ["@types/node@18.19.120", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-WtCGHFXnVI8WHLxDAt5TbnCM4eSE+nI0QN2NJtwzcgMhht2eNz6V9evJrk+lwC8bCY8OWV5Ym8Jz7ZEyGnKnMA=="],
|
||||||
|
|
||||||
|
"@types/node-fetch": ["@types/node-fetch@2.6.12", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
|
"@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],
|
||||||
|
|
||||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||||
|
|
@ -319,10 +315,10 @@
|
||||||
|
|
||||||
"address": ["address@1.2.2", "", {}, "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA=="],
|
"address": ["address@1.2.2", "", {}, "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA=="],
|
||||||
|
|
||||||
"adm-zip": ["adm-zip@0.5.16", "", {}, "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ=="],
|
|
||||||
|
|
||||||
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||||
|
|
||||||
|
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
|
||||||
|
|
||||||
"aggregate-error": ["aggregate-error@4.0.1", "", { "dependencies": { "clean-stack": "^4.0.0", "indent-string": "^5.0.0" } }, "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w=="],
|
"aggregate-error": ["aggregate-error@4.0.1", "", { "dependencies": { "clean-stack": "^4.0.0", "indent-string": "^5.0.0" } }, "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w=="],
|
||||||
|
|
||||||
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||||
|
|
@ -349,9 +345,7 @@
|
||||||
|
|
||||||
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
|
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
|
||||||
|
|
||||||
"arkregex": ["arkregex@0.0.3", "", { "dependencies": { "@ark/util": "0.55.0" } }, "sha512-bU21QJOJEFJK+BPNgv+5bVXkvRxyAvgnon75D92newgHxkBJTgiFwQxusyViYyJkETsddPlHyspshDQcCzmkNg=="],
|
"arktype": ["arktype@2.1.22", "", { "dependencies": { "@ark/schema": "0.49.0", "@ark/util": "0.49.0" } }, "sha512-xdzl6WcAhrdahvRRnXaNwsipCgHuNoLobRqhiP8RjnfL9Gp947abGlo68GAIyLtxbD+MLzNyH2YR4kEqioMmYQ=="],
|
||||||
|
|
||||||
"arktype": ["arktype@2.1.27", "", { "dependencies": { "@ark/schema": "0.55.0", "@ark/util": "0.55.0", "arkregex": "0.0.3" } }, "sha512-enctOHxI4SULBv/TDtCVi5M8oLd4J5SVlPUblXDzSsOYQNMzmVbUosGBnJuZDKmFlN5Ie0/QVEuTE+Z5X1UhsQ=="],
|
|
||||||
|
|
||||||
"array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
|
"array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
|
||||||
|
|
||||||
|
|
@ -431,7 +425,7 @@
|
||||||
|
|
||||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||||
|
|
||||||
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
"chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="],
|
||||||
|
|
||||||
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
|
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
|
||||||
|
|
||||||
|
|
@ -441,7 +435,7 @@
|
||||||
|
|
||||||
"character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
|
"character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
|
||||||
|
|
||||||
"chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="],
|
"chardet": ["chardet@0.7.0", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="],
|
||||||
|
|
||||||
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||||
|
|
||||||
|
|
@ -469,8 +463,6 @@
|
||||||
|
|
||||||
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
|
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
|
||||||
|
|
||||||
"color-blend": ["color-blend@4.0.0", "", {}, "sha512-fYODTHhI/NG+B5GnzvuL3kiFrK/UnkUezWFTgEPBTY5V+kpyfAn95Vn9sJeeCX6omrCOdxnqCL3CvH+6sXtIbw=="],
|
|
||||||
|
|
||||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
|
|
||||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
|
|
@ -515,10 +507,6 @@
|
||||||
|
|
||||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
|
||||||
"decode-bmp": ["decode-bmp@0.2.1", "", { "dependencies": { "@canvas/image-data": "^1.0.0", "to-data-view": "^1.1.0" } }, "sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA=="],
|
|
||||||
|
|
||||||
"decode-ico": ["decode-ico@0.4.1", "", { "dependencies": { "@canvas/image-data": "^1.0.0", "decode-bmp": "^0.2.0", "to-data-view": "^1.1.0" } }, "sha512-69NZfbKIzux1vBOd31al3XnMnH+2mqDhEgLdpygErm4d60N+UwA5Sq5WFjmEDQzumgB9fElojGwWG0vybVfFmA=="],
|
|
||||||
|
|
||||||
"decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
|
"decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
|
||||||
|
|
||||||
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
|
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
|
||||||
|
|
@ -677,10 +665,12 @@
|
||||||
|
|
||||||
"form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
|
"form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
|
||||||
|
|
||||||
"form-data-encoder": ["form-data-encoder@2.1.4", "", {}, "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="],
|
"form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
|
||||||
|
|
||||||
"format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="],
|
"format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="],
|
||||||
|
|
||||||
|
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
|
||||||
|
|
||||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||||
|
|
||||||
"fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
|
"fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
|
||||||
|
|
@ -779,7 +769,7 @@
|
||||||
|
|
||||||
"hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="],
|
"hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="],
|
||||||
|
|
||||||
"hex-rgb": ["hex-rgb@5.0.0", "", {}, "sha512-NQO+lgVUCtHxZ792FodgW0zflK+ozS9X9dwGp9XvvmPlH7pyxd588cn24TD3rmPm/N0AIRXF10Otah8yKqGw4w=="],
|
"honcho-ai": ["honcho-ai@0.0.11", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-SUl/PnMldTCz8G4S8faP00M2iFd9qWDkI5U8w0FQ7OC6SgKzTf1nJ/j3gyzctzR2IZ6LrOz/2d5OwO4f/PCMww=="],
|
||||||
|
|
||||||
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
|
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
|
||||||
|
|
||||||
|
|
@ -793,9 +783,9 @@
|
||||||
|
|
||||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||||
|
|
||||||
"ico-endec": ["ico-endec@0.1.6", "", {}, "sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ=="],
|
"humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
|
||||||
|
|
||||||
"iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="],
|
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||||
|
|
||||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
|
|
@ -829,7 +819,7 @@
|
||||||
|
|
||||||
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
|
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
|
||||||
|
|
||||||
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
|
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
|
||||||
|
|
||||||
"is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
|
"is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
|
||||||
|
|
||||||
|
|
@ -963,6 +953,8 @@
|
||||||
|
|
||||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||||
|
|
||||||
|
"mdast": ["mdast@3.0.0", "", {}, "sha512-xySmf8g4fPKMeC07jXGz971EkLbWAJ83s4US2Tj9lEdnZ142UP5grN73H1Xd3HzrdbU5o9GYYP/y8F9ZSwLE9g=="],
|
||||||
|
|
||||||
"mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
|
"mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
|
||||||
|
|
||||||
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
|
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
|
||||||
|
|
@ -1099,7 +1091,7 @@
|
||||||
|
|
||||||
"minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
|
"minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
|
||||||
|
|
||||||
"mint": ["mint@4.2.204", "", { "dependencies": { "@mintlify/cli": "4.0.808" }, "bin": { "mint": "index.js", "mintlify": "index.js" } }, "sha512-qOfwgnDKmhzAV+y1b787P1Lv2vYIrurvZs0Q7Kwx7zpJ+uiXjzEhYAzlm0Rj/SITlhwllJrv30axEaSq37sYMA=="],
|
"mint": ["mint@4.2.123", "", { "dependencies": { "@mintlify/cli": "4.0.727" }, "bin": { "mint": "index.js", "mintlify": "index.js" } }, "sha512-md52nrIkMZdtFwWVxpa1vu9msyBMtDePRuMFsTOcdWqYq11JO07L4lyOTVcMe6IPtRbvhjbbKiWihKB2xLZecQ=="],
|
||||||
|
|
||||||
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
|
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
|
||||||
|
|
||||||
|
|
@ -1125,7 +1117,9 @@
|
||||||
|
|
||||||
"nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="],
|
"nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="],
|
||||||
|
|
||||||
"node-fetch": ["node-fetch@2.6.7", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ=="],
|
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||||
|
|
||||||
|
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||||
|
|
||||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||||
|
|
||||||
|
|
@ -1379,13 +1373,11 @@
|
||||||
|
|
||||||
"sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
|
"sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="],
|
||||||
|
|
||||||
"sharp-ico": ["sharp-ico@0.1.5", "", { "dependencies": { "decode-ico": "*", "ico-endec": "*", "sharp": "*" } }, "sha512-a3jODQl82NPp1d5OYb0wY+oFaPk7AvyxipIowCHk7pBsZCWgbe0yAkU2OOXdoH0ENyANhyOQbs9xkAiRHcF02Q=="],
|
|
||||||
|
|
||||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||||
|
|
||||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
|
|
||||||
"shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
|
"shiki": ["shiki@3.8.1", "", { "dependencies": { "@shikijs/core": "3.8.1", "@shikijs/engine-javascript": "3.8.1", "@shikijs/engine-oniguruma": "3.8.1", "@shikijs/langs": "3.8.1", "@shikijs/themes": "3.8.1", "@shikijs/types": "3.8.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-+MYIyjwGPCaegbpBeFN9+oOifI8CKiKG3awI/6h3JeT85c//H2wDW/xCJEGuQ5jPqtbboKNqNy+JyX9PYpGwNg=="],
|
||||||
|
|
||||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||||
|
|
||||||
|
|
@ -1475,8 +1467,6 @@
|
||||||
|
|
||||||
"tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
|
"tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
|
||||||
|
|
||||||
"to-data-view": ["to-data-view@1.1.0", "", {}, "sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ=="],
|
|
||||||
|
|
||||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||||
|
|
||||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||||
|
|
@ -1563,8 +1553,6 @@
|
||||||
|
|
||||||
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
||||||
|
|
||||||
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
|
||||||
|
|
||||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||||
|
|
||||||
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||||
|
|
@ -1577,6 +1565,8 @@
|
||||||
|
|
||||||
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
|
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
|
||||||
|
|
||||||
|
"web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
|
||||||
|
|
||||||
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||||
|
|
||||||
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||||
|
|
@ -1617,7 +1607,7 @@
|
||||||
|
|
||||||
"yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
|
"yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
|
||||||
|
|
||||||
"yoctocolors-cjs": ["yoctocolors-cjs@2.1.3", "", {}, "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw=="],
|
"yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
|
||||||
|
|
||||||
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
||||||
|
|
||||||
|
|
@ -1631,15 +1621,9 @@
|
||||||
|
|
||||||
"@asyncapi/parser/ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="],
|
"@asyncapi/parser/ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="],
|
||||||
|
|
||||||
"@inquirer/checkbox/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
"@asyncapi/parser/node-fetch": ["node-fetch@2.6.7", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ=="],
|
||||||
|
|
||||||
"@inquirer/checkbox/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
"@inquirer/checkbox/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
||||||
|
|
||||||
"@inquirer/confirm/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
|
||||||
|
|
||||||
"@inquirer/confirm/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
|
||||||
|
|
||||||
"@inquirer/core/@inquirer/figures": ["@inquirer/figures@1.0.13", "", {}, "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw=="],
|
|
||||||
|
|
||||||
"@inquirer/core/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
"@inquirer/core/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
||||||
|
|
||||||
|
|
@ -1647,39 +1631,9 @@
|
||||||
|
|
||||||
"@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
"@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
||||||
|
|
||||||
"@inquirer/core/yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
|
"@inquirer/password/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
||||||
|
|
||||||
"@inquirer/editor/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
"@inquirer/select/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
||||||
|
|
||||||
"@inquirer/editor/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
|
||||||
|
|
||||||
"@inquirer/expand/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
|
||||||
|
|
||||||
"@inquirer/expand/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
|
||||||
|
|
||||||
"@inquirer/input/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
|
||||||
|
|
||||||
"@inquirer/input/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
|
||||||
|
|
||||||
"@inquirer/number/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
|
||||||
|
|
||||||
"@inquirer/number/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
|
||||||
|
|
||||||
"@inquirer/password/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
|
||||||
|
|
||||||
"@inquirer/password/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
|
||||||
|
|
||||||
"@inquirer/rawlist/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
|
||||||
|
|
||||||
"@inquirer/rawlist/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
|
||||||
|
|
||||||
"@inquirer/search/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
|
||||||
|
|
||||||
"@inquirer/search/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
|
||||||
|
|
||||||
"@inquirer/select/@inquirer/core": ["@inquirer/core@10.3.2", "", { "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A=="],
|
|
||||||
|
|
||||||
"@inquirer/select/@inquirer/type": ["@inquirer/type@3.0.10", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA=="],
|
|
||||||
|
|
||||||
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||||
|
|
||||||
|
|
@ -1687,17 +1641,37 @@
|
||||||
|
|
||||||
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common": ["@mintlify/common@1.0.537", "", { "dependencies": { "@asyncapi/parser": "^3.4.0", "@mintlify/mdx": "2.0.11", "@mintlify/models": "0.0.229", "@mintlify/openapi-parser": "^0.0.7", "@mintlify/validation": "0.1.471", "@sindresorhus/slugify": "^2.1.1", "acorn": "^8.11.2", "acorn-jsx": "^5.3.2", "estree-util-to-js": "^2.0.0", "estree-walker": "^3.0.3", "gray-matter": "^4.0.3", "hast-util-from-html": "^2.0.3", "hast-util-to-html": "^9.0.4", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "mdast": "^3.0.0", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.0.0", "mdast-util-mdx": "^3.0.0", "mdast-util-mdx-jsx": "^3.1.3", "micromark-extension-gfm": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.1", "micromark-extension-mdxjs": "^3.0.0", "openapi-types": "^12.0.0", "postcss": "^8.5.6", "remark": "^15.0.1", "remark-frontmatter": "^5.0.0", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-mdx": "^3.1.0", "remark-stringify": "^11.0.0", "tailwindcss": "^3.4.4", "unified": "^11.0.5", "unist-builder": "^4.0.0", "unist-util-map": "^4.0.0", "unist-util-remove": "^4.0.0", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-Mqm9OuXhaL0mVxkbPZHTIYNH8cVZdh9lsi5GHSGl8U7Vc+qHfv0CS+fempV1RAg6zRBjdSwD5rh43RMrPJSl/Q=="],
|
||||||
|
|
||||||
|
"@mintlify/common/@mintlify/models": ["@mintlify/models@0.0.213", "", { "dependencies": { "axios": "^1.8.3", "openapi-types": "^12.0.0" } }, "sha512-fiAVlRwUJxeI8ikpuXdcQLapHGoFHdUebIQMrZEt/UB74fMEnmzvLU01edCKLClPfw+DsceVXD7E8inWfpZSnA=="],
|
||||||
|
|
||||||
|
"@mintlify/common/@mintlify/validation": ["@mintlify/validation@0.1.424", "", { "dependencies": { "@mintlify/models": "0.0.213", "lcm": "^0.0.3", "lodash": "^4.17.21", "openapi-types": "^12.0.0", "zod": "^3.20.6", "zod-to-json-schema": "^3.20.3" } }, "sha512-mA9MoYT78KtVf34jXh01j/eqbj5agYplUYt+YYVPzGXnWM/lISlNMvQVwXekZYOPbcxATIy68Rn/Zk9ARgMWcQ=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common": ["@mintlify/common@1.0.537", "", { "dependencies": { "@asyncapi/parser": "^3.4.0", "@mintlify/mdx": "2.0.11", "@mintlify/models": "0.0.229", "@mintlify/openapi-parser": "^0.0.7", "@mintlify/validation": "0.1.471", "@sindresorhus/slugify": "^2.1.1", "acorn": "^8.11.2", "acorn-jsx": "^5.3.2", "estree-util-to-js": "^2.0.0", "estree-walker": "^3.0.3", "gray-matter": "^4.0.3", "hast-util-from-html": "^2.0.3", "hast-util-to-html": "^9.0.4", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "mdast": "^3.0.0", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.0.0", "mdast-util-mdx": "^3.0.0", "mdast-util-mdx-jsx": "^3.1.3", "micromark-extension-gfm": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.1", "micromark-extension-mdxjs": "^3.0.0", "openapi-types": "^12.0.0", "postcss": "^8.5.6", "remark": "^15.0.1", "remark-frontmatter": "^5.0.0", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-mdx": "^3.1.0", "remark-stringify": "^11.0.0", "tailwindcss": "^3.4.4", "unified": "^11.0.5", "unist-builder": "^4.0.0", "unist-util-map": "^4.0.0", "unist-util-remove": "^4.0.0", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-Mqm9OuXhaL0mVxkbPZHTIYNH8cVZdh9lsi5GHSGl8U7Vc+qHfv0CS+fempV1RAg6zRBjdSwD5rh43RMrPJSl/Q=="],
|
||||||
|
|
||||||
"@mintlify/link-rot/unist-util-visit": ["unist-util-visit@4.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.1.1" } }, "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg=="],
|
"@mintlify/link-rot/unist-util-visit": ["unist-util-visit@4.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.1.1" } }, "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg=="],
|
||||||
|
|
||||||
"@mintlify/mdx/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
"@mintlify/mdx/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common": ["@mintlify/common@1.0.537", "", { "dependencies": { "@asyncapi/parser": "^3.4.0", "@mintlify/mdx": "2.0.11", "@mintlify/models": "0.0.229", "@mintlify/openapi-parser": "^0.0.7", "@mintlify/validation": "0.1.471", "@sindresorhus/slugify": "^2.1.1", "acorn": "^8.11.2", "acorn-jsx": "^5.3.2", "estree-util-to-js": "^2.0.0", "estree-walker": "^3.0.3", "gray-matter": "^4.0.3", "hast-util-from-html": "^2.0.3", "hast-util-to-html": "^9.0.4", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "mdast": "^3.0.0", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.0.0", "mdast-util-mdx": "^3.0.0", "mdast-util-mdx-jsx": "^3.1.3", "micromark-extension-gfm": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.1", "micromark-extension-mdxjs": "^3.0.0", "openapi-types": "^12.0.0", "postcss": "^8.5.6", "remark": "^15.0.1", "remark-frontmatter": "^5.0.0", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-mdx": "^3.1.0", "remark-stringify": "^11.0.0", "tailwindcss": "^3.4.4", "unified": "^11.0.5", "unist-builder": "^4.0.0", "unist-util-map": "^4.0.0", "unist-util-remove": "^4.0.0", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-Mqm9OuXhaL0mVxkbPZHTIYNH8cVZdh9lsi5GHSGl8U7Vc+qHfv0CS+fempV1RAg6zRBjdSwD5rh43RMrPJSl/Q=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/scraping": ["@mintlify/scraping@4.0.396", "", { "dependencies": { "@mintlify/common": "1.0.537", "@mintlify/openapi-parser": "^0.0.7", "fs-extra": "^11.1.1", "hast-util-to-mdast": "^10.1.0", "js-yaml": "^4.1.0", "mdast-util-mdx-jsx": "^3.1.3", "neotraverse": "^0.6.18", "puppeteer": "^22.14.0", "rehype-parse": "^9.0.0", "remark-gfm": "^4.0.0", "remark-mdx": "^3.0.1", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "yargs": "^17.6.0", "zod": "^3.20.6" }, "bin": { "mintlify-scrape": "bin/cli.js" } }, "sha512-cPavXt7yrnyGLNb5QEY8C8anPfw9Tj8TL7CVE76Ey/+l7sae9inLd2f5E3vKYfWrqyIouxGmvA+pFuenOt66aw=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||||
|
|
||||||
"@mintlify/prebuild/unist-util-visit": ["unist-util-visit@4.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.1.1" } }, "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg=="],
|
"@mintlify/prebuild/unist-util-visit": ["unist-util-visit@4.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.1.1" } }, "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common": ["@mintlify/common@1.0.537", "", { "dependencies": { "@asyncapi/parser": "^3.4.0", "@mintlify/mdx": "2.0.11", "@mintlify/models": "0.0.229", "@mintlify/openapi-parser": "^0.0.7", "@mintlify/validation": "0.1.471", "@sindresorhus/slugify": "^2.1.1", "acorn": "^8.11.2", "acorn-jsx": "^5.3.2", "estree-util-to-js": "^2.0.0", "estree-walker": "^3.0.3", "gray-matter": "^4.0.3", "hast-util-from-html": "^2.0.3", "hast-util-to-html": "^9.0.4", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "mdast": "^3.0.0", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.0.0", "mdast-util-mdx": "^3.0.0", "mdast-util-mdx-jsx": "^3.1.3", "micromark-extension-gfm": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.1", "micromark-extension-mdxjs": "^3.0.0", "openapi-types": "^12.0.0", "postcss": "^8.5.6", "remark": "^15.0.1", "remark-frontmatter": "^5.0.0", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-mdx": "^3.1.0", "remark-stringify": "^11.0.0", "tailwindcss": "^3.4.4", "unified": "^11.0.5", "unist-builder": "^4.0.0", "unist-util-map": "^4.0.0", "unist-util-remove": "^4.0.0", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-Mqm9OuXhaL0mVxkbPZHTIYNH8cVZdh9lsi5GHSGl8U7Vc+qHfv0CS+fempV1RAg6zRBjdSwD5rh43RMrPJSl/Q=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||||
|
|
||||||
"@mintlify/previewing/unist-util-visit": ["unist-util-visit@4.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.1.1" } }, "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg=="],
|
"@mintlify/previewing/unist-util-visit": ["unist-util-visit@4.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.1.1" } }, "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg=="],
|
||||||
|
|
||||||
"@stoplight/better-ajv-errors/leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
|
"@shikijs/twoslash/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
"@stoplight/json-ref-readers/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
"@shikijs/twoslash/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
|
|
||||||
|
"@stoplight/better-ajv-errors/leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
|
||||||
|
|
||||||
"@stoplight/json-ref-readers/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
|
"@stoplight/json-ref-readers/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
|
||||||
|
|
||||||
|
|
@ -1709,14 +1683,10 @@
|
||||||
|
|
||||||
"@stoplight/spectral-parsers/@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="],
|
"@stoplight/spectral-parsers/@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="],
|
||||||
|
|
||||||
"@stoplight/spectral-runtime/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
|
||||||
|
|
||||||
"@stoplight/yaml/@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="],
|
"@stoplight/yaml/@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="],
|
||||||
|
|
||||||
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
|
||||||
|
|
||||||
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"chromium-bidi/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
|
"chromium-bidi/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
|
||||||
|
|
@ -1735,16 +1705,10 @@
|
||||||
|
|
||||||
"engine.io/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
|
"engine.io/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
|
||||||
|
|
||||||
"error-ex/is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
|
|
||||||
|
|
||||||
"escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
"express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"external-editor/chardet": ["chardet@0.7.0", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="],
|
|
||||||
|
|
||||||
"external-editor/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
|
||||||
|
|
||||||
"extract-zip/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
|
"extract-zip/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
|
||||||
|
|
||||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
@ -1759,11 +1723,13 @@
|
||||||
|
|
||||||
"glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
"glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||||
|
|
||||||
|
"got/form-data-encoder": ["form-data-encoder@2.1.4", "", {}, "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="],
|
||||||
|
|
||||||
"gray-matter/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
|
"gray-matter/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
|
||||||
|
|
||||||
"ink/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
"ink/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts": ["@inquirer/prompts@7.7.1", "", { "dependencies": { "@inquirer/checkbox": "^4.2.0", "@inquirer/confirm": "^5.1.14", "@inquirer/editor": "^4.2.15", "@inquirer/expand": "^4.0.17", "@inquirer/input": "^4.2.1", "@inquirer/number": "^3.0.17", "@inquirer/password": "^4.0.17", "@inquirer/rawlist": "^4.1.5", "@inquirer/search": "^3.0.17", "@inquirer/select": "^4.3.1" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-XDxPrEWeWUBy8scAXzXuFY45r/q49R0g72bUzgQXZ1DY/xEFX+ESDMkTQolcb5jRBzaNJX2W8XQl6krMNDTjaA=="],
|
"ink/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||||
|
|
||||||
"inquirer/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
"inquirer/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
||||||
|
|
||||||
|
|
@ -1785,8 +1751,6 @@
|
||||||
|
|
||||||
"public-ip/got": ["got@12.6.1", "", { "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", "cacheable-lookup": "^7.0.0", "cacheable-request": "^10.2.8", "decompress-response": "^6.0.0", "form-data-encoder": "^2.1.2", "get-stream": "^6.0.1", "http2-wrapper": "^2.1.10", "lowercase-keys": "^3.0.0", "p-cancelable": "^3.0.0", "responselike": "^3.0.0" } }, "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ=="],
|
"public-ip/got": ["got@12.6.1", "", { "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", "cacheable-lookup": "^7.0.0", "cacheable-request": "^10.2.8", "decompress-response": "^6.0.0", "form-data-encoder": "^2.1.2", "get-stream": "^6.0.1", "http2-wrapper": "^2.1.10", "lowercase-keys": "^3.0.0", "p-cancelable": "^3.0.0", "responselike": "^3.0.0" } }, "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ=="],
|
||||||
|
|
||||||
"raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
|
||||||
|
|
||||||
"react-dom/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
"react-dom/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||||
|
|
||||||
"react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
|
"react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
|
||||||
|
|
@ -1795,6 +1759,8 @@
|
||||||
|
|
||||||
"send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
|
"send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
|
||||||
|
|
||||||
|
"simple-swizzle/is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
|
||||||
|
|
||||||
"slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.0.0", "", { "dependencies": { "get-east-asian-width": "^1.0.0" } }, "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA=="],
|
"slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.0.0", "", { "dependencies": { "get-east-asian-width": "^1.0.0" } }, "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA=="],
|
||||||
|
|
||||||
"socket.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
"socket.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||||
|
|
@ -1815,66 +1781,48 @@
|
||||||
|
|
||||||
"wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
"@inquirer/checkbox/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
"@inquirer/checkbox/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
||||||
|
|
||||||
"@inquirer/checkbox/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
||||||
|
|
||||||
"@inquirer/confirm/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
|
||||||
|
|
||||||
"@inquirer/confirm/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
||||||
|
|
||||||
"@inquirer/core/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
"@inquirer/core/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
||||||
|
|
||||||
"@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
"@inquirer/editor/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
"@inquirer/password/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
||||||
|
|
||||||
"@inquirer/editor/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
"@inquirer/select/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
||||||
|
|
||||||
"@inquirer/expand/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
|
||||||
|
|
||||||
"@inquirer/expand/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
||||||
|
|
||||||
"@inquirer/input/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
|
||||||
|
|
||||||
"@inquirer/input/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
||||||
|
|
||||||
"@inquirer/number/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
|
||||||
|
|
||||||
"@inquirer/number/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
||||||
|
|
||||||
"@inquirer/password/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
|
||||||
|
|
||||||
"@inquirer/password/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
||||||
|
|
||||||
"@inquirer/rawlist/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
|
||||||
|
|
||||||
"@inquirer/rawlist/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
||||||
|
|
||||||
"@inquirer/search/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
|
||||||
|
|
||||||
"@inquirer/search/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
||||||
|
|
||||||
"@inquirer/select/@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
|
||||||
|
|
||||||
"@inquirer/select/@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
|
||||||
|
|
||||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||||
|
|
||||||
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx": ["@mintlify/mdx@2.0.11", "", { "dependencies": { "@shikijs/transformers": "^3.11.0", "@shikijs/twoslash": "^3.12.2", "hast-util-to-string": "^3.0.1", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.1.0", "mdast-util-mdx-jsx": "^3.2.0", "mdast-util-to-hast": "^13.2.0", "next-mdx-remote-client": "^1.0.3", "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-smartypants": "^3.0.2", "shiki": "^3.11.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@radix-ui/react-popover": "^1.1.15", "react": "^18.3.1", "react-dom": "^18.3.1" } }, "sha512-yXwuM0BNCxNaJetPrh89c5Q2lhzU2al4QrOM3zLUdrPOdjOpPmv8ewcdiXV/qIhZDpl5Ll9k47dsz33bZjVWTg=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx": ["@mintlify/mdx@2.0.11", "", { "dependencies": { "@shikijs/transformers": "^3.11.0", "@shikijs/twoslash": "^3.12.2", "hast-util-to-string": "^3.0.1", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.1.0", "mdast-util-mdx-jsx": "^3.2.0", "mdast-util-to-hast": "^13.2.0", "next-mdx-remote-client": "^1.0.3", "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-smartypants": "^3.0.2", "shiki": "^3.11.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@radix-ui/react-popover": "^1.1.15", "react": "^18.3.1", "react-dom": "^18.3.1" } }, "sha512-yXwuM0BNCxNaJetPrh89c5Q2lhzU2al4QrOM3zLUdrPOdjOpPmv8ewcdiXV/qIhZDpl5Ll9k47dsz33bZjVWTg=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||||
|
|
||||||
"@mintlify/link-rot/unist-util-visit/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
"@mintlify/link-rot/unist-util-visit/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
||||||
|
|
||||||
"@mintlify/link-rot/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="],
|
"@mintlify/link-rot/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="],
|
||||||
|
|
||||||
"@mintlify/link-rot/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@5.1.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" } }, "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg=="],
|
"@mintlify/link-rot/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@5.1.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" } }, "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx": ["@mintlify/mdx@2.0.11", "", { "dependencies": { "@shikijs/transformers": "^3.11.0", "@shikijs/twoslash": "^3.12.2", "hast-util-to-string": "^3.0.1", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.1.0", "mdast-util-mdx-jsx": "^3.2.0", "mdast-util-to-hast": "^13.2.0", "next-mdx-remote-client": "^1.0.3", "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-smartypants": "^3.0.2", "shiki": "^3.11.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@radix-ui/react-popover": "^1.1.15", "react": "^18.3.1", "react-dom": "^18.3.1" } }, "sha512-yXwuM0BNCxNaJetPrh89c5Q2lhzU2al4QrOM3zLUdrPOdjOpPmv8ewcdiXV/qIhZDpl5Ll9k47dsz33bZjVWTg=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/scraping/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||||
|
|
||||||
"@mintlify/prebuild/unist-util-visit/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
"@mintlify/prebuild/unist-util-visit/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
||||||
|
|
||||||
"@mintlify/prebuild/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="],
|
"@mintlify/prebuild/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="],
|
||||||
|
|
||||||
"@mintlify/prebuild/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@5.1.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" } }, "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg=="],
|
"@mintlify/prebuild/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@5.1.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" } }, "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx": ["@mintlify/mdx@2.0.11", "", { "dependencies": { "@shikijs/transformers": "^3.11.0", "@shikijs/twoslash": "^3.12.2", "hast-util-to-string": "^3.0.1", "mdast-util-from-markdown": "^2.0.2", "mdast-util-gfm": "^3.1.0", "mdast-util-mdx-jsx": "^3.2.0", "mdast-util-to-hast": "^13.2.0", "next-mdx-remote-client": "^1.0.3", "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "remark-smartypants": "^3.0.2", "shiki": "^3.11.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@radix-ui/react-popover": "^1.1.15", "react": "^18.3.1", "react-dom": "^18.3.1" } }, "sha512-yXwuM0BNCxNaJetPrh89c5Q2lhzU2al4QrOM3zLUdrPOdjOpPmv8ewcdiXV/qIhZDpl5Ll9k47dsz33bZjVWTg=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||||
|
|
||||||
"@mintlify/previewing/unist-util-visit/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
"@mintlify/previewing/unist-util-visit/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
||||||
|
|
||||||
"@mintlify/previewing/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="],
|
"@mintlify/previewing/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="],
|
||||||
|
|
@ -1903,28 +1851,12 @@
|
||||||
|
|
||||||
"ink/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
"ink/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/checkbox": ["@inquirer/checkbox@4.2.0", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/confirm": ["@inquirer/confirm@5.1.14", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/editor": ["@inquirer/editor@4.2.15", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "external-editor": "^3.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/expand": ["@inquirer/expand@4.0.17", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/input": ["@inquirer/input@4.2.1", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/number": ["@inquirer/number@3.0.17", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/password": ["@inquirer/password@4.0.17", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/rawlist": ["@inquirer/rawlist@4.1.5", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/search": ["@inquirer/search@3.0.17", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-CuBU4BAGFqRYors4TNCYzy9X3DpKtgIW4Boi0WNkm4Ei1hvY9acxKdBdyqzqBCEe4YxSdaQQsasJlFlUJNgojw=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/select": ["@inquirer/select@4.3.1", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA=="],
|
|
||||||
|
|
||||||
"inquirer/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
"inquirer/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
||||||
|
|
||||||
|
"is-online/got/form-data-encoder": ["form-data-encoder@2.1.4", "", {}, "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="],
|
||||||
|
|
||||||
|
"public-ip/got/form-data-encoder": ["form-data-encoder@2.1.4", "", {}, "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="],
|
||||||
|
|
||||||
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
"widest-line/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
|
"widest-line/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
|
||||||
|
|
@ -1935,46 +1867,98 @@
|
||||||
|
|
||||||
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||||
|
|
||||||
"@inquirer/checkbox/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="],
|
||||||
|
|
||||||
"@inquirer/confirm/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||||
|
|
||||||
"@inquirer/editor/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
|
||||||
|
|
||||||
"@inquirer/expand/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="],
|
||||||
|
|
||||||
"@inquirer/input/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||||
|
|
||||||
"@inquirer/number/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
|
||||||
|
|
||||||
"@inquirer/password/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="],
|
||||||
|
|
||||||
"@inquirer/rawlist/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||||
|
|
||||||
"@inquirer/search/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
|
||||||
|
|
||||||
"@inquirer/select/@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
|
||||||
|
|
||||||
"cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
"cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||||
|
|
||||||
"ink/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
"ink/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/checkbox/@inquirer/figures": ["@inquirer/figures@1.0.13", "", {}, "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/checkbox/yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/expand/yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/rawlist/yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/search/@inquirer/figures": ["@inquirer/figures@1.0.13", "", {}, "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/search/yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/select/@inquirer/figures": ["@inquirer/figures@1.0.13", "", {}, "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw=="],
|
|
||||||
|
|
||||||
"inquirer/@inquirer/prompts/@inquirer/select/yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
|
|
||||||
|
|
||||||
"widest-line/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
"widest-line/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/shiki/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
|
||||||
|
|
||||||
|
"@mintlify/cli/@mintlify/common/@mintlify/mdx/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/shiki/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
|
||||||
|
|
||||||
|
"@mintlify/link-rot/@mintlify/common/@mintlify/mdx/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/shiki/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
|
||||||
|
|
||||||
|
"@mintlify/prebuild/@mintlify/common/@mintlify/mdx/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/shiki/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
|
||||||
|
|
||||||
|
"@mintlify/previewing/@mintlify/common/@mintlify/mdx/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,51 +4,38 @@ description: "Compatibility guide for Honcho's SDKs and API"
|
||||||
icon: "shield-check"
|
icon: "shield-check"
|
||||||
---
|
---
|
||||||
|
|
||||||
This guide helps you match the right SDK version to your Honcho API version. Newer SDK patch versions are always backward-compatible within the same major version — install the latest patch for your range.
|
This guide helps you understand which versions of Honcho's API are compatible with which SDK versions.
|
||||||
|
|
||||||
## Current Versions
|
## Version Compatibility
|
||||||
|
|
||||||
|
### Honcho API v2.4.2 (Current)
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
<CardGroup cols={2}>
|
||||||
<Card title="TypeScript SDK" icon="js">
|
<Card title="TypeScript SDK" icon="js">
|
||||||
**Latest:** v2.4.0
|
**Compatible Version:** v1.5.0
|
||||||
|
|
||||||
|
Install with:
|
||||||
```bash
|
```bash
|
||||||
npm install @honcho-ai/sdk
|
npm install @honcho-ai/sdk@1.5.0
|
||||||
```
|
```
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Python SDK" icon="python">
|
<Card title="Python SDK" icon="python">
|
||||||
**Latest:** v2.4.0
|
**Compatible Version:** v1.5.0
|
||||||
|
|
||||||
|
Install with:
|
||||||
```bash
|
```bash
|
||||||
pip install honcho-ai
|
pip install honcho-ai==1.5.0
|
||||||
```
|
```
|
||||||
</Card>
|
</Card>
|
||||||
</CardGroup>
|
</CardGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Version Compatibility Table
|
## Version Compatibility Table
|
||||||
|
|
||||||
| Honcho API Version | TypeScript SDK | Python SDK |
|
| Honcho API Version | TypeScript SDK | Python SDK |
|
||||||
|-------------------|---------------|------------|
|
|-------------------|---------------|------------|
|
||||||
| v3.1.1 (Current) | v2.4.0 | v2.4.0 |
|
| v2.4.2 (Current) | v1.5.0 | v1.5.0 |
|
||||||
| v3.1.0 | v2.4.0 | v2.4.0 |
|
|
||||||
| v3.0.12 | v2.3.0 | v2.3.0 |
|
|
||||||
| v3.0.11 | v2.1.2 | v2.1.2 |
|
|
||||||
| v3.0.10 | v2.1.2 | v2.1.2 |
|
|
||||||
| v3.0.9 | v2.1.2 | v2.1.2 |
|
|
||||||
| v3.0.8 | v2.1.2 | v2.1.2 |
|
|
||||||
| v3.0.7 | v2.1.2 | v2.1.2 |
|
|
||||||
| v3.0.6 | v2.1.1 | v2.1.1 |
|
|
||||||
| v3.0.5 | v2.1.0 | v2.1.0 |
|
|
||||||
| v3.0.4 | v2.1.0 | v2.1.0 |
|
|
||||||
| v3.0.3 | v2.1.0 | v2.1.0 |
|
|
||||||
| v3.0.2 | v2.0.0+ | v2.0.0+ |
|
|
||||||
| v3.0.1 | v2.0.0+ | v2.0.0+ |
|
|
||||||
| v3.0.0 | v2.0.0+ | v2.0.0+ |
|
|
||||||
| v2.5.1 | v1.6.0 | v1.6.0 |
|
|
||||||
| v2.5.0 | v1.6.0 | v1.6.0 |
|
|
||||||
| v2.4.3 | v1.5.0 | v1.5.0 |
|
|
||||||
| v2.4.2 | v1.5.0 | v1.5.0 |
|
|
||||||
| v2.4.1 | v1.5.0 | v1.5.0 |
|
| v2.4.1 | v1.5.0 | v1.5.0 |
|
||||||
| v2.4.0 | v1.5.0 | v1.5.0 |
|
| v2.4.0 | v1.5.0 | v1.5.0 |
|
||||||
| v2.3.3 | v1.4.1 | v1.4.1 |
|
| v2.3.3 | v1.4.1 | v1.4.1 |
|
||||||
|
|
|
||||||
|
|
@ -27,442 +27,7 @@ Welcome to the Honcho changelog! This section documents all notable changes to t
|
||||||
### Honcho API and SDK Changelogs
|
### Honcho API and SDK Changelogs
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Honcho API">
|
<Tab title="Honcho API">
|
||||||
<Update label="v3.1.1 (Current)">
|
<Update label="v2.4.2 (Current)">
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Server `requires-python` is `>=3.13`, matching the production image. Self-hosters on 3.10–3.12 need to upgrade; SDK and CLI floors are unchanged (#1090)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Concurrent `create_documents` writers to the same collection deadlocked on `times_derived` reinforcement UPDATEs issued in batch order; the error was swallowed per-document, the batch was lost, and the queue item was marked processed. Writers now lock target rows with `SELECT ... ORDER BY id FOR UPDATE` before applying, abort the batch on `SQLAlchemyError` instead of continuing through a dead session, and retry transient errors (deadlock, serialization failure, lock/statement timeout, lost connection) up to `MAX_RETRYABLE_ATTEMPTS` instead of burning the item (#1033)
|
|
||||||
- Scope backfill no longer embeds, writes, and syncs every planned copy at once. A 14k-document session is ~580MB of vectors; several concurrent backfills OOM-killed the deriver at its 1000Mi limit and crash-looped because the work units never completed. Phases 2–4 now run per chunk of 500 specs, reload source embeddings per chunk, and drop them once synced. Membership is locked across chunk writes so a concurrent leave cannot commit between the check and the inserts (#1104)
|
|
||||||
- Model-generated observations with NUL bytes (`\u0000`) no longer fail the exact-content dedup pre-fetch with a Postgres `DataError` that dropped the whole observer batch. Ingress already stripped NUL from user content; the deriver now strips it so stored text matches embedded text. All-NUL content is dropped rather than stored empty (#1095)
|
|
||||||
- `search_messages` no longer forwards `top_k=0` to Turbopuffer (which requires 1..10000). Zero/negative limits short-circuit to empty results; tool limits are floored at 1. The documents path was already guarded (#970); this closes the message path (#1084)
|
|
||||||
- OpenAI-compatible tool-call turns with `content=null` keep null through history replay instead of being coerced to `""`. Providers that bind reasoning state to the exact assistant message shape were breaking on the empty string. Tool-less null still becomes `""` (#1064)
|
|
||||||
- The production image now ships `pyproject.toml` in the runtime stage, so the service reports its real version instead of `unknown` in OpenAPI and telemetry (#1074)
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.1.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Scopes: a named grouping of sessions that acts as a visibility boundary on recall, implemented as a facade over an observer peer (`scope.{name}` with `{"kind": "scope"}`). Developers manage them exclusively through `/v3/workspaces/{workspace_id}/scopes` (create-or-get, list, get, add/list/remove session membership) and an optional `scopes` field on session create — never through the observer/observed mechanics. Scope peers cannot author messages, cannot be a chat or representation `target`, are excluded from `peers.list` by default (`PeerGet.kind` = `"scope"` / `"all"` switches the view), and are rejected on the generic session-peer routes. Workspace-level key required; peer- and session-scoped keys get 401. Legacy peers occupying a reserved `scope.` name without the kind flag are refused with 409, never adopted (#884)
|
|
||||||
- `scope` read option on chat, representation, session context, and workspace search. A single scope swaps the observer to the backing scope peer so conclusion recall, peer cards, and message tools stay inside that scope's membership. A list of scopes takes the union of member sessions (capped at `MAX_SESSION_ALLOWLIST_ENTRIES`) and executes via the session-allowlist path. Empty scopes fail closed. `scope` is mutually exclusive with `filters` and `session_id`. Workspace- or admin-level key required (403 otherwise). Scope peers are also rejected as `peer_target` / `peer_perspective` on session context and as the path peer or `target` on `GET /peers/{id}/context` (#897)
|
|
||||||
- Scope backfill-by-copy and removal reconciliation. Adding a session that already has messages copies its explicit-level documents into the scope's collections (no LLM re-derivation; idempotent via `copied_from`). Removing a session soft-deletes those copies and fail-closed cascades to derived documents whose `source_ids` intersect anything removed, then enqueues a `card_refresh` dream with `rebuild=True` plus an omni dream. `GET /v3/workspaces/{workspace_id}/scopes/{scope_id}/status` reports per-session backfill state (`pending` / `completed` / `failed`, plus `docs_copied`) (#904)
|
|
||||||
- Workspace-level chat at `POST /v3/workspaces/{workspace_id}/chat`: agentic dialectic over the whole workspace instead of a single (observer, observed) pair. Prefetches workspace stats and the top active peers' self cards, then searches pair-scoped memory with `[observer->observed]` attribution. Supports `session_id`, `scope`, `reasoning_level`, `response_format`, and SSE streaming (#931)
|
|
||||||
- MCP workspace discovery: tools accept `workspace_id`, the worker honors an optional `X-Honcho-Workspace-ID` connection header, and `list_workspace` / `create_workspace` tools let clients pick or create a workspace instead of relying on the SDK default (#1020)
|
|
||||||
- MCP `search` also queries conclusions in parallel with messages when `peer_id` is given, returning `{messages, conclusions}`. The conclusions leg degrades to `[]` on error so search never gets worse than before (#974)
|
|
||||||
- Prometheus metrics for physical DB connections, visible even under `DB_POOL_CLASS=null`: `db_connections_open` (gauge) and `db_connections_established` (counter), hooked to SQLAlchemy connection-lifecycle events and registered on both the API and the deriver (#1055)
|
|
||||||
- Bounded-label Prometheus series are zero-initialized at process start so an absent series means a broken scrape rather than "nothing happened" (#927)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Workspace and pair chat system prompts now describe Honcho, peers, and the harness on their own terms, and render only the tools the request actually offers. The pair prompt no longer advertises a write tool that is not in the loadout (#1066)
|
|
||||||
- Deriver idle polling backoff is longer and no longer reset by periodic reconciler work, so downstream connection pools can cull idle DB connections (#1015)
|
|
||||||
- LLM provider SDKs are lazy-loaded so idle API and deriver processes no longer pay for every provider at import time (#1011)
|
|
||||||
- Production image is a multi-stage build: LanceDB/PyArrow move behind an optional `lancedb` extra (`INSTALL_LANCEDB=true` to restore them), FastAPI's unused cloud CLI is dropped, and the venv is copied into the runtime image with final ownership so Docker does not double the layer. Default unpacked image is about 663 MB (was 1.7 GB) (#1014)
|
|
||||||
- Redis Cluster cache keys hash-tag the namespace so one deployment's keys land on a single shard instead of opening a connection to every node. No behaviour change on a non-cluster backend; existing keys age out by TTL (#1058)
|
|
||||||
- Deriver extraction prompt no longer leaks its own few-shot examples into extracted conclusions (#1028)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Observer-scoped `get_observation_context` no longer materializes every session the observer has ever joined into a `session_name IN (...)` list (twice in one statement). Past ~32k sessions that hit psycopg's bind-parameter ceiling and 500'd. The observer half is now a correlated `EXISTS` over `session_peers`, two bind parameters regardless of membership size (#1065)
|
|
||||||
- Re-adding an already-active session peer no longer advances `joined_at`, so `peer_perspective` search keeps messages from the original join. Genuine leave-and-rejoin still starts a new window (#1059)
|
|
||||||
- Transient embedding-provider errors (for example an OpenAI-compatible 200 with empty `data: []`) were relabeled as token-limit errors. Only genuine oversize input raises `EmbeddingTokenLimitError`; other provider errors propagate unchanged (#791)
|
|
||||||
- The filter DSL now fails closed with a 422 instead of a 500 on bad shapes, coerces operands by column type (so `{"session_id": {"ne": "abc"}}` is a string inequality rather than "invalid numeric"), and treats `NOT` / `ne` as null-safe (`IS NOT TRUE` / `IS DISTINCT FROM`) so negation no longer drops rows whose field is unset. Closed-set columns like `level` reject unknown values. Session-allowlist entries must be well-formed ids (`*` is 422, not a silent widen) (#947)
|
|
||||||
- `ne` on JSONB metadata keys is null-safe: a missing key is not equal to the compared value, so `{"metadata": {"foo": {"ne": "bar"}}}` includes rows where `foo` is unset (#1036)
|
|
||||||
- Oversized texts in `simple_batch_embed` are truncated to the embedding token cap instead of failing the whole batch. Representation processing reports failed observer saves in `RepresentationCompletedEvent` and raises when every observer save fails (#1019)
|
|
||||||
- Assistant `reasoning_content` (DeepSeek / some OpenRouter models) is preserved across tool-loop turns. Previously the tool loop dropped thinking content before building the next assistant history message, so continuation requests failed. `reasoning_details` still takes precedence when both are present (#1034)
|
|
||||||
- `create_observations` now honors `DERIVER_DEDUPLICATE` instead of hardcoding `deduplicate=True`, matching the representation write path (#1018)
|
|
||||||
- `provider_params.timeout` is forwarded to the OpenAI-compatible embedding client, not just the LLM client (#1024)
|
|
||||||
- Conclusions semantic-search validation errors name the field and the constraint instead of returning a generic 422 (#960)
|
|
||||||
- OpenAI-compatible embedding calls request `encoding_format=float` so providers that default to base64 do not break pgvector inserts (#938)
|
|
||||||
- Gemini batch embedding works for `gemini-embedding-2*` models, which rejected the previous request shape (#745)
|
|
||||||
- MCP OAuth with no advertised scopes no longer defaults to read-only (which 403'd chat and search POSTs). Protected-resource metadata advertises read and write (#1004)
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.12">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Session allowlist on the Dialectic and representation via a constrained `filters` body on `POST /peers/{peer_id}/chat` and `/representation`, supporting only the `session_id` key (a session id, a bare list, or `{"in": [...]}`). Unsupported keys and shapes are rejected with 422 rather than silently ignored, it composes with `session_id` (which must be included in the allowlist when both are given), and it is capped at 1,000 sessions per request. Enforcement is uniform and fail-closed at every recall chokepoint: scoped conclusion recall is restricted to `level == "explicit"` (dream-derived conclusions carry a single `session_name` but are synthesized across all sessions, so that stamp can't be scoped on), `get_reasoning_chain` is unavailable under an allowlist, and an empty allowlist short-circuits to empty results everywhere. Workspace keys pass the allowlist as-given; peer-scoped JWTs must be an active member of every allowlisted session (401 otherwise) (#882)
|
|
||||||
- Bare-list membership sugar in the filter DSL: `{"session_id": ["s1", "s2"]}` is now shorthand for `{"session_id": {"in": [...]}}` on regular columns generically. JSONB metadata columns are excluded and keep containment semantics. Strictly additive, since a bare list on a regular column previously compiled to a type-mismatched equality that matched nothing (#881)
|
|
||||||
- Optional structured outputs on the Dialectic: `response_format` (a JSON Schema with root type `object`) on peer chat makes `content` a JSON string conforming to that schema. Only a conservative subset of JSON Schema is supported, with DoS guards and non-recursive `$ref` support (#896)
|
|
||||||
- Combined tool calling and structured output in the LLM transport layer, with per-backend request shaping: OpenAI routes tool-carrying structured requests through `create()` with an explicit `json_schema` response format (`parse()` 500s on non-strict function tools), Anthropic skips the `{` JSON prefill when tools are present so `tool_use` blocks stay reachable, and Gemini injects a schema instruction into the final turn instead of using native `response_schema` (rejected alongside function calling before Gemini 3). All backends skip structured-output parsing on tool-call turns, which carry no consumable content (#907)
|
|
||||||
- `card_refresh` dream type: a lightweight dream that runs only the peer-card update, for event-driven refreshes such as membership changes and cold starts. Handled by a new `CardRefreshSpecialist` restricted to `get_recent_observations`, `search_memory`, and `update_peer_card` (no observation-mutating tools) with a tool-iteration cap of `min(6, DREAM.MAX_TOOL_ITERATIONS)`. `POST /v3/workspaces/{workspace_id}/schedule_dream` accepts `dream_type=card_refresh` plus a `rebuild` flag, which omits the existing card from the prompt so the specialist rebuilds it solely from observations present in the collection. Card refreshes never advance the omni dream guard pair (`last_dream_at` / `last_dream_document_count`) (#883)
|
|
||||||
- Full-fidelity LLM trace stream, with Langfuse as one projection over it: each call is captured once (`CapturedLLMCall`) and fanned out to a CloudEvents trace stream (`llm.call.traced` / `trace.content`) and a Langfuse exporter, both reconstructing trace → run → step → generation from the same source of truth. Adds `TELEMETRY_TRACE_PAYLOADS_ENABLED` (default `false`), `TELEMETRY_TRACE_MAX_BYTES` (default 262144, per-message cap with oversized content clipped), `TELEMETRY_TRACE_PURPOSES` (JSON list of `CallPurpose` values; empty means all), and `LANGFUSE_EXPORTER_MODE` (`exporter` by default; `inline` is kept for one release for side-by-side validation). Embedding calls are traced, dreamer branches nest under one dream trace, tool calls become spans under their step, and high-volume events are sampled deterministically. `TRACE_ENDPOINT` is dropped (#845)
|
|
||||||
- Redis Cluster support via `CACHE_CLUSTER` (for example GCP Memorystore for Redis Cluster), alongside a new `CACHE_LOCK_WAIT_CHECK_INTERVAL_SECONDS` (#905)
|
|
||||||
- `EMBEDDING_MODEL_CONFIG__MAX_BATCH_SIZE` caps texts per embedding request for OpenAI-compatible providers with smaller limits than OpenAI's, such as DashScope `text-embedding-v4` (10) and Alibaba Bailian `qwen3.7-text-embedding` (20). When unset, native provider defaults are preserved (OpenAI 2048, Gemini 100) (#983)
|
|
||||||
- Per-request provider timeouts via `provider_params.timeout` on any model config, validated at config load so a bad value fails at startup with the exact config path instead of surfacing per-request as a retried 500. Good values normalize to float seconds; Gemini's is converted to milliseconds (#832)
|
|
||||||
- `RepresentationCompletedEvent` now reports deduplication counts: `exact_dup_in_batch_count`, `exact_dup_existing_count`, `semantic_dup_rejected_count`, and `semantic_dup_replaced_count` (#910)
|
|
||||||
- OAuth discovery for MCP clients: the MCP worker serves `/.well-known/oauth-protected-resource` (RFC 9728) without auth so clients can discover the authorization server, and a 401 now carries `WWW-Authenticate: Bearer resource_metadata="..."` (exposed cross-origin) to start the flow (#923)
|
|
||||||
- Prometheus metrics for the immediate-embed fast path: tasks shed because `EMBEDDING_MAX_PENDING_EMBED_TASKS` was reached, and the current in-flight task count (#892)
|
|
||||||
- Docs: a detailed system architecture diagram, a Codex integration guide (#879), a structured-outputs page (#896), a section on filtering conclusions by reasoning level (#851), a health-check endpoint reference, and SDK updates (#867)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- **Breaking config change:** `DERIVER_REPRESENTATION_BATCH_MAX_TOKENS` is split into two settings that were previously conflated — `DERIVER_REPRESENTATION_BATCH_WORK_UNIT_TARGET_TOKENS` (default 512), the producer-side minimum a work unit accumulates before the deriver claims it, where `0` disables the gate; and `DERIVER_REPRESENTATION_BATCH_TARGET_INPUT_TOKENS` (default 1024), the consumer-side maximum context-window tokens per deriver LLM call. Deployments setting the old name must migrate (#889)
|
|
||||||
- The immediate-embed fast path now applies backpressure: `EMBEDDING_MAX_PENDING_EMBED_TASKS` (default 50) caps in-flight embed tasks, and once saturated, message creation skips the fast path entirely and the reconciler embeds on its next cycle. `0` disables the fast path (#892)
|
|
||||||
- Explicit-level documents are now kept session-pure, so memory can be built by copying explicit documents between collections. Enforcement refuses rather than rewrites: `create_documents` rejects explicit documents with a null `session_name`, exact dedup keys on (content, level, session-for-explicit), semantic dedup scopes candidate search to the same level and — for explicit documents — the same session, and the generic `create_observations` tool rejects `level='explicit'` outside message-ingestion (deriver) context. Derived levels keep cross-session consolidation (#883)
|
|
||||||
- Sentry's `before_send` filter is centralized as `default_before_send` in `src/telemetry/sentry.py` instead of living only in the API's `main.py`, so the deriver gets the same non-actionable-exception filtering. All Sentry events also carry a `namespace` tag for correlation (#934, #870)
|
|
||||||
- The minimal deriver's extraction examples no longer teach inferences its own output schema forbids. The `EXAMPLES` block demonstrated deriving a specific birthday from "I just had my 25th birthday last Saturday", deriving residence from a single visit ("I took my dog for a walk in NYC" → "alice lives in NYC"), and a "+ general knowledge" deductive output the deriver has no channel for. The replacements stay inside the schema's contract and teach the boundary: the dog/NYC message is kept and shown extracting correctly, and a separate example shows "lives in NYC" is valid when actually stated (#985)
|
|
||||||
- Dreamer specialists are instructed not to output summaries (#894)
|
|
||||||
- `session_name` is deprecated for scoping in favor of the session allowlist. It is not removed and not aliased: it also pins the query to one session, bypasses observer scoping, and drives session-history injection into the dialectic prompt, so it has no drop-in replacement (#882)
|
|
||||||
- The MCP worker no longer requires the `X-Honcho-User-Name` or `X-Honcho-Assistant-Name` headers (#923)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Session scoping was applied to only one of the three working-representation query paths: `session_name` reached the recent-documents query, but the semantic and most-derived paths ignored it, so `limit_to_session` leaked cross-session conclusions into perspectives. The allowlist is now threaded uniformly through all three paths and pushed down to pgvector and external vector stores (#881)
|
|
||||||
- Empty membership lists failed open in the vector-store filter builders, silently widening scope: LanceDB dropped empty `IN` clauses and Turbopuffer emitted a bare `In []` with undocumented semantics. Both now emit an explicit always-false predicate, and `_build_filter_conditions` checks `is not None` rather than truthiness so an empty list is no longer treated like `None` (#881, #882)
|
|
||||||
- Session-scoped CRUD helpers ignored the session allowlist entirely, so a caller could read a session the allowlist forbids. The API routes guarded this with a 422, but the dialectic tools call these CRUD functions directly and bypassed it. `_semantic_search_messages` (covering `search_messages` and `search_messages_temporal`), `grep_messages`, `get_messages_by_date_range`, `get_recent_history`, and `get_observation_context` now return `[]` when `session_name` is set and outside the allowlist (#882)
|
|
||||||
- The cache client logged the full Redis URL — including the password — at INFO and WARNING on every connection attempt and failure, exposing the live credential in container logs and downstream aggregation. Credentials are now redacted across userinfo, the `?password=` (redis-py) and `?secret=` (cashews) query params, scheme-less URLs whose password is invisible to `.port`/`.password` parsing, and malformed URLs, whose fallback previously echoed the raw input verbatim (#869)
|
|
||||||
- A `top_k` of `0` reached the vector store, where Turbopuffer rejects it with a 400 (`top_k must be between 1 and 10000`). A non-positive `top_k` now returns `[]` before the embedding call, and the semantic budget floors at 1 so an explicitly requested search isn't silently allocated zero (#970)
|
|
||||||
- Gemini clients had no HTTP timeout, so a stalled socket wedged the deriver worker's uvloop event loop, which the in-process reconciler shares. A 10-minute timeout is now set on both the Gemini LLM client and the Gemini embedding client (#903)
|
|
||||||
- Dreamer conclusions were dated to ingestion time rather than their latest source observation, and their timestamps are now normalized (#890)
|
|
||||||
- Langfuse I/O annotation was gated on `LANGFUSE_PUBLIC_KEY` instead of `langfuse_inline_enabled`, so in the default `exporter` mode it called `update_current_generation()` with no active span — logging "No active span in current context" roughly 14 times per dialectic run and building throwaway `model_dump` payloads on every LLM call. Separately, `AgentToolSummaryCreatedEvent` hardcoded `run_id="deriver"` / `iteration=0`, polluting `run_id` grouping in the CloudEvents stream with a phantom run; both fields are now optional and the resource id is keyed on `message_id:summary_type` (schema_version 2 → 3) (#845)
|
|
||||||
- Assistant tool calls were dropped from the captured trace stream for OpenAI and Gemini: `build_captured_messages` read only `{role, content, tool_call_id}`, but those providers keep tool calls outside `content`, so replayed tool-call turns landed as empty content and Gemini lost its text and tool results entirely. Tool calls are now normalized per provider into a unified `tool_calls` field and folded into the content hash. Gemini's `thought_signature` is bytes, so `model_dump(mode="json")` raised `UnicodeDecodeError` inside `emit_trace`, silently dropping whole tool-calling iterations from the trace stream (billing and Langfuse were unaffected); it is now base64-encoded on the telemetry path while replay keeps the raw bytes (#845)
|
|
||||||
- `EmbeddingClient.encoding` forced full client construction, raising "OpenAI API key is required" even though tiktoken needs no credentials. The document dedup tie-break only needs `.encoding` for token counting, so any test hitting that path failed in environments without embedding keys — notably CI for pull requests from forks. The encoding is now resolved from the configured model directly, falling back to `cl100k_base`, and the underlying client's encoding is reused only when it has already been constructed (#955)
|
|
||||||
- The Docker build failed under Podman because the uv build inputs weren't copied (#878)
|
|
||||||
- LanceDB was installed on macOS Intel, where it doesn't work. A PEP 508 marker excludes `darwin/x86_64` and the LanceDB vector-store import is wrapped so a misconfiguration surfaces as a clear config error (#496)
|
|
||||||
- Prompt checks requiring the literal token "json" for `json_object` mode are now satisfied in lowercase (#887)
|
|
||||||
- Reverted an unintended `RepresentationCompletedEvent` schema-version increment
|
|
||||||
- Documented preinstalling pgvector as a privileged role for deployments where the `DB_CONNECTION_URI` role deliberately cannot create extensions (managed Postgres, Kubernetes operators, NixOS). `CREATE EXTENSION IF NOT EXISTS vector` does not help there, because Postgres checks the privilege before checking whether the extension exists. Docker Compose is unaffected, since the bundled stack connects as the `postgres` superuser (#984)
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.11">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `api_request_duration_seconds` Prometheus histogram tracking per-route request latency, labeled by method and endpoint (#837)
|
|
||||||
- LLM `provider_params` passthroughs (`extra_body` / `extra_headers` / `extra_query`) are now forwarded to the underlying provider transport across all backends, with shape validation that rejects non-mapping values (#821)
|
|
||||||
- `structured_output_mode` model-config option to use `json_object` mode for OpenAI-compatible providers that lack native Structured Outputs support (used by the deriver) (#820)
|
|
||||||
- OpenRouter app-attribution headers (`HTTP-Referer` / `X-Openrouter-Title`) are now sent on OpenAI-compatible clients when the configured base URL is OpenRouter, so requests are attributed to "Honcho" in OpenRouter's dashboard (#805)
|
|
||||||
- Langfuse traces are now tagged with user and session IDs for easier trace filtering (#814)
|
|
||||||
- `DERIVER_REPRESENTATION_BATCH_MAX_AGE_SECONDS` (default 1800s) lets sub-threshold representation work units flush once their oldest unprocessed queue item ages out. Set it to `0` to keep the legacy behavior where sub-threshold tails wait indefinitely unless `DERIVER_FLUSH_ENABLED=true` (#826)
|
|
||||||
- Conclusion responses now include a `level` field (`explicit`, `deductive`, `inductive`, `contradiction`); list/query endpoints support filtering by `level` via `filters`, with reserved filter keys protected from being overridden by user-supplied filters (#851)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Peer-scoped JWTs now get read-only access to the sessions their peer is an active member of (session context, summaries, peers, their own per-session config, search, and message reads). Session-scoped JWTs remain confined to their session and cannot reach peer routes (#679)
|
|
||||||
- Compacted Honcho's log output, with guarded ms/s metric formatting that falls back to a plain string for non-numeric values (#836)
|
|
||||||
- Sentry now drops noisy infra/scrape transactions: the reconciler opens a transaction only once a batch has rows (idle cycles emit none), and a `traces_sampler` returns `0.0` for `/metrics`, `/health`, `/openapi.json`, `/docs`, `/redoc`, and the deriver metrics server. `SENTRY.TRACES_SAMPLE_RATE` still governs real traffic (#834)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Peer- and session-scoped JWTs were effectively workspace-scoped: authorization walked the route's declared scope and fell through to a workspace match, so a `{w, p: alice}` token could act on any peer in the workspace. JWTs are now authorized by their narrowest claim and never widen to workspace access (#679)
|
|
||||||
- The keys API now rejects creating a peer- or session-scoped key without a workspace. Such keys were minted successfully but failed verification on every request (#679)
|
|
||||||
- Agent-supplied observation IDs carrying the display-format `id:` prefix are now normalized (prefix and trailing whitespace stripped) before `source_ids` are stored and on `get_reasoning_chain` lookups, fixing corrupted provenance links and broken reasoning-chain traversal (#795)
|
|
||||||
- Fixed a `create_tree` keyword-argument mismatch in the Dreamer's surprisal tree construction (#749)
|
|
||||||
- Providers that omit output-token counts (observed with Gemini on tool-loop completions) returned `output_tokens=None`, which raised a Pydantic validation error that aborted the call and crashed the Dreamer's induction phase before inductive conclusions were persisted. `None` is now coerced to `0` so token accounting degrades gracefully (#809)
|
|
||||||
- Document creation now performs exact (case-insensitive, whitespace-trimmed) content deduplication before the existing semantic dedup step: exact duplicates within a batch collapse to a single insert, and an exact match against a live document reinforces it (atomic `times_derived` increment) instead of creating a new row (#861)
|
|
||||||
- The OpenAI backend passed `tool_choice` through raw while the Anthropic and Gemini backends translate Honcho's canonical vocabulary to their native form, so on a mixed-provider fallback chain (for example Gemini primary → OpenAI backup) a canonical `"any"` reached OpenAI unchanged and was rejected as an invalid param. The OpenAI backend now converts it, mirroring the others: `any`/`required` → `required`, `auto`/`none` pass through, and a tool-name string or `{"name": ...}` dict becomes a function selection (#850)
|
|
||||||
- Langfuse `@observe` auto-capture serialized every argument of `honcho_llm_call_inner` into the generation span input, including `client_override` (a live `AsyncOpenAI`/`genai` client) and `selected_config` (which carries `api_key`). Auto-capture deep-copied the client into a half-constructed object whose teardown raised (`AsyncHttpxClientWrapper ... no attribute '_state'` on OpenAI, flooding stderr; `BaseApiClient ... no attribute '_http_options'` on Gemini), and it leaked `ModelConfig.api_key` into traces. Capture is now an explicit allowlist: `capture_input`/`capture_output` are disabled and curated, serializable input and output are stamped instead, with tuning knobs surfaced as `model_parameters` via a secret-bearing denylist and per-call token usage mirrored as `usage_details` (#849)
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.10">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Messages are now embedded via a background task rather than blocking API request
|
|
||||||
- Read-only DB session mode (`get_read_db` / `tracked_db(..., read_only=True)`) so reads don't hold a transaction open across the work
|
|
||||||
- `CORS_ORIGINS` env var to configure CORS allowed origins without editing source; defaults match the prior hardcoded list, so self-hosted deployments behind custom domains can whitelist their frontend (#697)
|
|
||||||
- `scripts/generate_jwt.py` — utility for minting scoped or admin Honcho JWTs (`--admin`, `--workspace`/`--peer`/`--session`, `--expires` with human-friendly durations, `--print-only`) without calling the keys API (#757)
|
|
||||||
- `STALE_WORK_UNIT_CLEANUP_INTERVAL_SECONDS` (default 60s) — minimum jittered spacing between deriver stale-work-unit cleanup runs, so cleanup no longer runs on every seconds-scale poll (`0.0` keeps the legacy every-poll behavior) (#773)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Optimized the deriver and dreamer prompt cache prefixes to improve prompt-cache hit rates (#806)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `times_derived` is now properly reinforced when a duplicate conclusion is detected. It had been pinned at 1 for nearly every conclusion (the reject-new branch dropped the increment and the new-wins branch reset the count to 1), so `ORDER BY times_derived DESC` fell back to arbitrary heap order and froze stale conclusions to the front of injected context. Reinforcement is now an atomic increment and both most-derived queries gained a `created_at DESC` recency tiebreaker (#768)
|
|
||||||
- Webhook creation now correctly rejects private/internal IP addresses (#793)
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.9">
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Connection acquisition is now a single attempt with no server-side retry, on a vanilla `AsyncSession`. A new `DB_CONNECT_TIMEOUT_SECONDS` (default 2s) bounds the attempt so a saturated or unreachable pooler fails fast instead of holding a client connection open to re-knock. A saturated DB now surfaces to the caller — the API returns an error and the deriver backs off and retries on a later poll — which lets the pooler drain rather than amplifying saturation.
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Deriver poll jitter so instances that start together don't poll in lockstep: `DERIVER_POLLING_STARTUP_JITTER_SECONDS` (random delay before the first poll, default 30s) and `DERIVER_POLLING_JITTER_RATIO` (±fraction applied to every poll sleep, default 0.5). Both disable at `0.0`; the underlying backoff schedule is unchanged.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Reverted the connection-checkout retry and `HonchoAsyncSession` custom session introduced in 3.0.8. Removed the `DB_CONNECTION_RETRY_ENABLED` / `DB_CONNECTION_RETRY_MAX_DELAY_SECONDS` / `DB_CONNECTION_RETRY_BACKOFF_INITIAL_SECONDS` / `DB_CONNECTION_RETRY_BACKOFF_MAX_SECONDS` settings, the `db_connection_acquisitions{outcome=...}` Prometheus counter, and the `db.pool.acquire` Sentry span. Alerting built on `db_connection_acquisitions` should migrate to `db_pool_connections` / `db_queries_in_flight`.
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.8">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Connection-checkout retry with bounded exponential backoff (tenacity) on `get_db`/`tracked_db`: transient transaction-pooler (Supavisor) rejections — SQLAlchemy `TimeoutError` and `OperationalError` — now retry with backoff instead of surfacing as 500s under client-connection saturation. Gated by
|
|
||||||
`DB_CONNECTION_RETRY_ENABLED` with configurable delay/backoff knobs; ~10s default budget (#758)
|
|
||||||
- `HonchoAsyncSession` — a lazy `AsyncSession` that checks out its pooled connection (with retry) on the first DB-touching call rather than at construction. Request handlers doing non-DB work (embedding, file, LLM) before their first query no longer pin a pooler connection across it. Only the checkout is retried;
|
|
||||||
the statement still runs exactly once, so writes are never duplicated (#758)
|
|
||||||
- Adaptive deriver queue polling: the poll interval backs off when the queue is idle or erroring (base → max, doubling each cycle) and snaps back to base the moment work is claimed, cutting steady-state query load against the DB. Gated by `DERIVER_POLLING_BACKOFF_ENABLED` with configurable max/multiplier (#758)
|
|
||||||
- New Prometheus `db_pool_connections` gauge (checked_out / checked_in / size / overflow), labeled `api`|`deriver`, registered in both the API lifespan and the deriver metrics server (#758)
|
|
||||||
- New Prometheus `db_connection_acquisitions{outcome=ok|retried|exhausted}` counter — the alertable early-warning signal that connection checkouts are retrying through pooler rejection, before requests start failing (#758)
|
|
||||||
- New Prometheus `db_queries_in_flight` gauge — statements actually executing on the wire (via SQLAlchemy cursor-execute events). Paired with `checked_out`, the gap reveals connections held but parked (the "idle in transaction during an external call" antipattern). Gated on `METRICS.ENABLED` for zero overhead when
|
|
||||||
off (#758)
|
|
||||||
- Explicit `SqlalchemyIntegration` in both the API and deriver Sentry inits; connection acquisition wrapped in a `db.pool.acquire` span with live pool stats captured on retry exhaustion (#758)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Default `POOL_TIMEOUT` lowered to 5s, with validation that it stays under the connection-retry budget when a pooled (non-null) `POOL_CLASS` is configured; `config.toml.example` and the v2/v3 configuration docs updated to match (#758)
|
|
||||||
- `HonchoAsyncSession` wraps every DB-touching session method (execute / scalar / scalars / flush / merge / refresh / commit / get / get_one / stream / stream_scalars / delete) so the lazy-checkout-with-retry guarantee has no holes; the acquired flag resets on `close()`/`reset()` so a reused session re-acquires on
|
|
||||||
next use (#758)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Roll the session back on a retryable checkout failure before retrying — a failed autobegin could otherwise leave it pending-rollback, making the next connection attempt raise instead of cleanly re-checking-out (#758)
|
|
||||||
- Guard `DBPoolCollector.collect()` so a pool-read/import hiccup can't raise and abort the entire `/metrics` scrape (Prometheus drops all metrics if any collector raises) (#758)
|
|
||||||
- Clamp the pool overflow gauge to ≥ 0 (it could report negative before the pool fills) (#758)
|
|
||||||
- Removed a double-sleep in the deriver idle poll so the backoff cap is a true cap rather than 2× (#758)
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.7">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- New `src/llm/` package as the single owner of provider runtime: clients, backends, history adapters, tool loop, request builder, credentials, and caching policy (#459)
|
|
||||||
- New cloudevent `LLMCallCompletedEvent` (`llm.call.completed`) fires once per provider hit with full cost-attribution context: transport/provider_label, model, token counts with cache breakdown, finish_reason, outcome, retry/fallback state, duration, tool-call shape, streaming flag, and agent correlation (`run_id` + iteration) (#637)
|
|
||||||
- `RepresentationCompletedEvent` now carries `total_input_tokens` for full-trace cost attribution; per-emitter `honcho_version` injection; deterministic per-`run_id` high-volume sampler via `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE` (#637)
|
|
||||||
- Deriver custom instructions: per-workspace/peer guidance threaded into the deriver prompt with a `MAX_CUSTOM_INSTRUCTIONS_TOKENS` budget (default 2000); deriver `MAX_INPUT_TOKENS` raised 23000 → 25000 (#609)
|
|
||||||
- Configurable embedding dimensions: `EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE` (`auto`/`always`/`never`) controls whether OpenAI `dimensions=` is forwarded (#678)
|
|
||||||
- New `honcho-cli` package — Python CLI for inspecting and managing peers, sessions, and configuration against a Honcho deployment (#424)
|
|
||||||
- `HONCHO_API_URL` env var support in the MCP Worker for self-hosted deployments (#575)
|
|
||||||
- API ID `max_length` increased from 100 to 512 across `WorkspaceCreate`, `PeerCreate`, and `SessionCreate` to align with the DB schema (#684)
|
|
||||||
- `AttemptPlan` dataclass pins per-retry provider selection across stream-final retries so streaming doesn't bounce back to primary after the tool loop has settled on fallback (#459)
|
|
||||||
- Gemini JSON-schema sanitizer for `function_declarations` — strips keywords Gemini's validator rejects while preserving semantics for other backends (#459)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- All LLM orchestration moved out of `src/utils/clients.py` into `src/llm/` with modules split by responsibility (#459)
|
|
||||||
- Default `ModelConfig` factories (deriver, summary, dreamer specialists, dialectic levels) normalized with no extra parameters set by default; operators add transport/thinking overrides explicitly (#459)
|
|
||||||
- OpenAI reasoning-model routing widened to cover `gpt-5.x` and `o1/o3/o4` — these models receive `max_completion_tokens` instead of `max_tokens` (#459)
|
|
||||||
- Peer card prompts reframed as stable identity markers; induction specialist now opts out of peer card writes so only deduction touches the card (#686)
|
|
||||||
- Vector store queries no longer fetch embedding vectors — only document metadata is returned, reducing payload size and DB load (pgvector, lancedb, turbopuffer) (#682)
|
|
||||||
- Langfuse trace metadata now includes `namespace`, `model`, and `provider` so traces can be filtered by deployment slice (#565)
|
|
||||||
- Deriver: model-aware tokenizer (replaces the previously hardcoded encoding) and explicit guard on empty message content (#647)
|
|
||||||
- Dialectic level defaults now merge correctly with per-level overrides (#656)
|
|
||||||
- Default dialectic tool choice switched to `auto` (#630)
|
|
||||||
- Vector sync given a substantial retry budget to tolerate transient embedding provider outages (#604)
|
|
||||||
- `AgentToolConclusionsDeletedEvent` payload now carries `levels` (#612)
|
|
||||||
- Turbopuffer: `InternalServerError` caught and surfaced as a warning rather than a hard failure; vector store sync errors downgraded to warnings (#561)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `reverse` query parameter is now honored on the v3 workspace list, peer list, workspace-scoped session list, and peer-scoped session list. Honcho SDKs at 2.1.0+ were already sending `reverse=true` for these routes but the server silently ignored it. Ties on `created_at` now fall back to the internal nanoid `id` for stable ordering across pages (#685)
|
|
||||||
- LLM client factories now receive `base_url` from `LLMSettings` for default providers — operators pointing at OpenAI-compatible proxies via `LLM__OPENAI_BASE_URL` were previously ignored on the default path (#643, fixes #641)
|
|
||||||
- Internal N+1 query in dialectic agent tool execution — collapsed per-iteration DB lookups into a single fetch (#652)
|
|
||||||
- Dreamer threshold and time-guard semantics: count filter now includes only `documents.level == 'explicit'` (was inflating threshold via dreamer-created levels and creating a feedback loop); `last_dream_at` write relocated from enqueue to process so duplicate enqueues or failed runs no longer reset the 8-hour time guard (#573)
|
|
||||||
- Deriver: blank observations are filtered out before embedding (previously triggered noisy embedding calls and persisted empty rows) (#615)
|
|
||||||
- Surprisal module: filter format corrected from `{"level": levels}` to `{"level": {"in": levels}}` — the prior call silently returned 0 results and made the entire Surprisal phase of the Dream cycle a no-op (#581, fixes #559)
|
|
||||||
- Removed hardcoded `stop_sequences` override from Deriver `ModelConfig` (was clobbering operator-configured stop sequences) (#587)
|
|
||||||
- Embedding client: `embed()` now wraps single-string input in an array, restoring compatibility with OpenAI-compatible third-party providers that reject scalar input (#586)
|
|
||||||
- Docker Compose: deriver service startup gated on the API service healthcheck — prevents races where the deriver starts before the API has run migrations (#689)
|
|
||||||
- Docker image: `HEALTHCHECK` directive removed from the shared base image; service-level health checks now belong in each service's own configuration (#530)
|
|
||||||
- Removed strict parameter validation for thinking params on Anthropic and OpenAI transports — was rejecting valid per-transport configs (#686)
|
|
||||||
- Stream-final retries pin to the `AttemptPlan` that succeeded rather than re-running provider selection through the outer `current_attempt` ContextVar (#459)
|
|
||||||
- Gemini `cached_content` reuse keys now include `system_instruction` and `tool_config` so cache hits don't cross configurations (#459)
|
|
||||||
- CrewAI example updated for the latest CrewAI protocol (#631)
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- `src/utils/clients.py` deleted; its responsibilities are split across `src/llm/registry.py`, `src/llm/credentials.py`, and the backend-specific modules (#459)
|
|
||||||
- `HEALTHCHECK` directive from the shared Docker image (#530)
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.6">
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Tightened transaction scopes across search, agent tools, queue manager, and webhook delivery to minimize DB connection hold time during external operations (#525)
|
|
||||||
- Search operations refactored to two-phase pattern — external work (embeddings, LLM calls) completes before opening a transaction (#525)
|
|
||||||
- Agent tool executor performs external operations before acquiring DB sessions (#525)
|
|
||||||
- Queue manager transaction scope reduced to only the critical section (#525)
|
|
||||||
- Webhook delivery no longer holds a DB session parameter (#525)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Session leakage in non-session-scoped dialectic chat calls (#526)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Health check endpoint (`/health`) for container orchestration and load balancer probes (#510)
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.5">
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- explicit rollback on all transactions to force connection closed
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.4">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- JSONB metadata validation enforces 100 key limit and max depth of 5 (#419)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Schemas refactored from single `schemas.py` into `schemas/api.py`, `schemas/configuration.py`, and `schemas/internal.py` with backwards-compatible re-exports (#419)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Missing `deleted_at` filter on `RepresentationManager._query_documents_recent()` and `._query_documents_most_derived()` allowed soft-deleted documents to leak into the deriver's working representation (#456)
|
|
||||||
- `CleanupStaleItemsCompletedEvent` emitted spuriously when no queue item was actually deleted (#454)
|
|
||||||
- Empty JSON file uploads caused unhandled errors; now returns normalized error responses (#434)
|
|
||||||
- Memory leak: `_observation_locks` switched to `WeakValueDictionary` to prevent unbounded growth (#419)
|
|
||||||
- SQL injection in `dependencies.py`: parameterized `set_config` calls to prevent injection via request context (#419)
|
|
||||||
- NUL byte crashes: string inputs (message content, queries, peer cards) now stripped at schema level (#419)
|
|
||||||
- Filter recursion depth capped at 5 to prevent stack overflow (#419)
|
|
||||||
- Dedup-skipped observations now correctly reflected in created counts (#477)
|
|
||||||
- External vector store support for message search — routes queries through configured external vector store with oversampling and
|
|
||||||
deduplication to handle chunked embeddings (#479)
|
|
||||||
- Dialectic agent no longer holds a DB connection during LLM calls — embeddings are pre-computed before tool execution, DB sessions isolated in `extract_preferences`, `query_documents` no longer accepts a DB session parameter (#477)
|
|
||||||
</Update>
|
|
||||||
<Update label="v3.0.3">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Consolidated session context into a single DB session with 40/60 token budget allocation between summary and messages
|
|
||||||
- Observation validation via `ObservationInput` Pydantic schema with partial-success support and batch embedding with per-observation fallback
|
|
||||||
- Peer card hard cap of 40 facts with case-insensitive deduplication and whitespace normalization
|
|
||||||
- Safe integer coercion (`_safe_int`) for all LLM tool inputs to handle non-integer values like `"Infinity"`
|
|
||||||
- Embedding pre-computation and reuse across multiple search calls in dialectic and representation flows
|
|
||||||
- Peer existence validation in dialectic chat endpoints — raises ResourceNotFoundException instead of silently failing
|
|
||||||
- Logging filter to suppress noisy `GET /metrics` access logs
|
|
||||||
- Oolong long-context aggregation benchmark (synth and real variants, 1K–4M token context windows)
|
|
||||||
- MolecularBench fact quality evaluation (ambiguity, decontextuality, minimality scoring)
|
|
||||||
- CoverageBench information recall evaluation (gold fact extraction, coverage matching, QA verification)
|
|
||||||
- LoCoMo summary-as-context baseline evaluation
|
|
||||||
- Webhook delivery tests, dependency lifecycle tests, queue cleanup tests, summarizer fallback tests
|
|
||||||
- Parallel test execution via pytest-xdist with worker-specific databases
|
|
||||||
- `test_reasoning_levels.py` script for LOCOM dataset testing across reasoning levels
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Workspace deletion is now async — returns 202 Accepted, validates no active sessions (409 Conflict), cascade-deletes in background
|
|
||||||
- Redis caching layer now stores plain-dict instead of ORM objects, with v2-prefixed keys, storage, resilient `safe_cache_set`/`safe_cache_delete` helpers, and deferred post-commit cache invalidation
|
|
||||||
- All `get_or_create_*` CRUD operations now use savepoints (`db.begin_nested()`) instead of commit/rollback for race condition prevention
|
|
||||||
- Reconciler vector sync uses direct ORM mutation instead of batch parameterized UPDATE statements
|
|
||||||
- Summarizer enforces hard word limit in prompt and creates fallback text for empty summaries with `summary_tokens = 0`
|
|
||||||
- Blocked Gemini responses (SAFETY, RECITATION, PROHIBITED_CONTENT, BLOCKLIST) now raise `LLMError` to trigger retry/backup-provider logic
|
|
||||||
- Gemini client explicitly sets `max_output_tokens` from `max_tokens` parameter
|
|
||||||
- All deriver and metrics collector logging replaced with structured `logging.getLogger(__name__)` calls
|
|
||||||
- Dreamer specialist prompts updated to enforce durable-facts-only peer cards with max 40 entries and deduplication
|
|
||||||
- `GetOrCreateResult` changed from `NamedTuple` to `dataclass` with `async post_commit()` method
|
|
||||||
- FastAPI upgraded from 0.111.0 to 0.131.0; added pyarrow dependency
|
|
||||||
- Queue status filtering to only show user-facing tasks (representation, summary, dream); excludes internal infrastructure tasks
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- JWT timestamp bug — `JWTParams.t` was evaluated once at class definition time instead of per-instance
|
|
||||||
- Session cache invalidation on deletion was missing
|
|
||||||
- `get_peer_card()` now properly propagates `ResourceNotFoundException` instead of swallowing it
|
|
||||||
- `set_peer_card()` ensures peer exists via `get_or_create_peers()` before updating
|
|
||||||
- Backup provider failover with proper tool input type safety
|
|
||||||
- Removed `setup_admin_jwt()` from server startup
|
|
||||||
- Sentry coroutine detection switched from `asyncio.iscoroutinefunction` to `inspect.iscoroutinefunction`
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- `explicit.py` and `obex.py` benchmarks replaced by coverage.py and molecular.py
|
|
||||||
- Claude Code review automation workflow (`.github/workflows/claude.yml`)
|
|
||||||
- Coverage reporting from default pytest configuration
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.2">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Documentation for reasoning_level and Claude Code plugin
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Gave dreaming sub-agents better prompting around peer card creation, tweaked overall prompts
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Added message-search fallback for memory search tool, necessary in fresh sessions
|
|
||||||
- Made FLUSH_ENABLED a config value
|
|
||||||
- Removed N+1 query in search_messages
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.1">
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Token counting in Explicit Agent Loop
|
|
||||||
- Backwards compatibility of queue items
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v3.0.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Agentic Dreamer for intelligent memory consolidation using LLM agents
|
|
||||||
- Agentic Dialectic for query answering using LLM agents with tool use
|
|
||||||
- Reasoning levels configuration for dialectic (`minimal`, `low`, `medium`, `high`, `max`)
|
|
||||||
- Prometheus token tracking for deriver and dialectic operations
|
|
||||||
- n8n integration
|
|
||||||
- Cloud Events for auditable telemetry
|
|
||||||
- External Vector Store support for turbopuffer and lancedb with reconciliation flow
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- API route renaming for consistency
|
|
||||||
- Dreamer and dialectic now respect peer card configuration settings
|
|
||||||
- Observations renamed to Conclusions across API and SDKs
|
|
||||||
- Deriver to buffer representation tasks to normalize workloads
|
|
||||||
- Local Representation tasks to create singular QueueItems
|
|
||||||
- getContext endpoint to use `search_query` rather than force `last_user_message`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Dream scheduling bugs
|
|
||||||
- Summary creation when start_message_id > end_message_id
|
|
||||||
- Cashews upgrade to prevent NoScriptError
|
|
||||||
- Memory leak in `accumulate_metric` call
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Peer card configuration from message configuration; peer cards no longer created/updated in deriver process
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v2.5.1">
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Backwards compatibility for `message_ids` field in documents to handle legacy tuple format
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v2.5.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Message level configurations
|
|
||||||
- CRUD operations for observations
|
|
||||||
- Comprehensive test cases for harness
|
|
||||||
- Peer level get_context
|
|
||||||
- Set Peer Card Method
|
|
||||||
- Manual dreaming trigger endpoint
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Configurations to support more flags for fine-grained control of the deriver, peer cards, summaries, etc.
|
|
||||||
- Working Representations to support more fine-grained parameters
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- File uploads to match `MessageCreate` structure
|
|
||||||
- Cache invalidation strategy
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v2.4.3">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Redis caching to improve DB IO
|
|
||||||
- Backup LLM provider to avoid failures when a provider is down
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- QueueItems to use standardized columns
|
|
||||||
- Improved Deduplication logic for Representation Tasks
|
|
||||||
- More finegrained metrics for representation, summary, and peer card tasks
|
|
||||||
- DB constraint to follow standard naming conventions
|
|
||||||
</Update>
|
|
||||||
|
|
||||||
<Update label="v2.4.2">
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Langfuse tracing to have readable waterfalls
|
- Langfuse tracing to have readable waterfalls
|
||||||
|
|
@ -536,7 +101,7 @@ Welcome to the Honcho changelog! This section documents all notable changes to t
|
||||||
<Update label="v2.3.2">
|
<Update label="v2.3.2">
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Get peer cards endpoint (`GET /v2/peers/{peer_id}/card`) for retrieving targeted peer context information
|
- Get peer cards endpoint (`GET /v2/peers/{peer_id}/peer-card`) for retrieving targeted peer context information
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
@ -766,7 +331,7 @@ Welcome to the Honcho changelog! This section documents all notable changes to t
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- `/list` endpoints to not require a request body
|
- `/list` endpoints to not require a request body
|
||||||
- `metamessage_type` to `label` with backwards compatibility
|
- `metamessage_type` to `label` with backwards compatability
|
||||||
- Database Provisioning to rely on alembic
|
- Database Provisioning to rely on alembic
|
||||||
- Database Session Manager to explicitly rollback transactions before closing
|
- Database Session Manager to explicitly rollback transactions before closing
|
||||||
the connection
|
the connection
|
||||||
|
|
@ -800,111 +365,6 @@ Welcome to the Honcho changelog! This section documents all notable changes to t
|
||||||
|
|
||||||
<Tab title="Python SDK">
|
<Tab title="Python SDK">
|
||||||
[Python SDK](https://pypi.org/project/honcho-ai/)
|
[Python SDK](https://pypi.org/project/honcho-ai/)
|
||||||
<Update label="v2.4.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Scopes: `Honcho.scope()` / `HonchoAio.scope()` get-or-create a named visibility boundary, `Honcho.scopes()` lists them, and a `Scope` object adds/removes sessions, lists membership, and reads backfill `status()`. `Honcho.session(..., scopes=[...])` joins a new session to scopes at creation. Requires a Honcho server with the matching API support (Honcho v3.1.0+).
|
|
||||||
- `scope` option on `Peer.chat()` / `chat_stream()`, representation, session context, and workspace search. A single scope answers from that scope's collection and card; a list of scopes restricts recall to the union of their member sessions (explicit-only). Mutually exclusive with `session` / `sessions` / `filters`.
|
|
||||||
- Workspace-level chat: `Honcho.chat()` / `HonchoAio.chat()` and `chat_stream()` ask a question across every peer in the workspace, with the same `session`, `scope`, `reasoning_level`, and `response_format` options as `Peer.chat()`. Requires a Honcho server with the matching API support (Honcho v3.1.0+).
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- `ConclusionScope` is renamed to `ConclusionsView`. The old name remains as a deprecated alias for one more minor version. "Scope" now means a named set of sessions (`Scope`); these objects are views over one observer/observed pair.
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.3.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `response_format` on `Peer.chat()` / `PeerAio.chat()` and `Peer.chat_stream()` / `PeerAio.chat_stream()`, for constraining a dialectic answer to a schema. Pass a Pydantic model class to get a validated instance back (parsed via `model_validate_json`), or a raw JSON Schema dict to get the JSON string as-is. Overloads type the return precisely, so a model class narrows to that model and a dict narrows to `str`. On the streaming variants, chunks stay raw text that accumulates to a JSON string — parse it after the stream completes. Requires a Honcho server with the matching API support (Honcho v3.0.12+).
|
|
||||||
- `response_format` field on `DialecticParams`.
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.2.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `ConclusionLevel` type (`explicit`, `deductive`, `inductive`, `contradiction`) and a `level` field on `Conclusion`, exposing the reasoning level the server already tracked but previously stripped from responses.
|
|
||||||
- `filters` parameter on `ConclusionScope.list()` and `ConclusionScope.query()` (sync and async), passed through to the same dynamic server-side filter logic as `peers()`/`sessions()`/`messages()`. Filter explicit-only conclusions with `filters={"level": "explicit"}`, or by any other supported field/operator. Requires a Honcho server with the matching API support (Honcho v3.0.11+).
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Scope-managed filter keys (`observer`, `observed`, `session`) are now rejected with a clear `ValueError` if passed in `filters`, instead of silently overriding the scope and returning conclusions from a different peer pair. Use `peer.conclusions` / `conclusions_of(target)` and the `session=` parameter instead. `session_id` remains a valid filter on `query()`.
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.1.2">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `page`, `size`, and `reverse` pagination parameters on `Honcho.workspaces()` and `HonchoAio.workspaces()`, closing the gap from 2.1.0 which added these to other list methods but not to `workspaces()`. Honoring `reverse` on the workspace/peer/session list routes also requires a Honcho server with the matching API fix; older servers silently ignore the parameter.
|
|
||||||
- `peers` parameter on `Honcho.session()` and `HonchoAio.session()` — attach peers to a session at creation time instead of needing a follow-up `session.add_peers()` call. Accepts the same shapes as `Session.add_peers` (peer ID string, `Peer` object, list of either, or tuples with `SessionPeerConfig`).
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- `WorkspaceCreateParams`, `PeerCreateParams`, and `SessionCreateParams` now accept IDs up to 512 characters (was 100), matching the server-side schema change in Honcho v3.0.7.
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.1.1">
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Broadened HTTP retry logic to cover `httpx.NetworkError` and `httpx.RemoteProtocolError` in addition to `httpx.TimeoutException` and `httpx.ConnectError`, improving resilience against transient network failures
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.1.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `created_at` property on `Peer` and `Session` objects
|
|
||||||
- `is_active` property on `Session` objects
|
|
||||||
- `get_message(message_id)` method on `Session` (sync and async) to fetch a single message by ID
|
|
||||||
- `page`, `size`, and `reverse` pagination parameters on all list methods
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- **Breaking**: `peer()` and `session()` now always make a get-or-create API call — no more lazy initialization
|
|
||||||
- Response configuration models now tolerate unknown fields from newer servers for forward compatibility
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Sync and async `Session.get_metadata()`, `get_configuration()`, and `refresh()` now refresh cached `created_at` and `is_active` values along with metadata and configuration
|
|
||||||
- `honcho.__version__` now derives from package metadata, with a source-checkout fallback, so it stays aligned with released package versions
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.0.2">
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- All input models now reject unknown fields via strict Pydantic validation (`extra="forbid"`). Previously, misspelled or extraneous fields were silently ignored. Now a `ValidationError` is raised with the unrecognized field name.
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.0.1">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `set_peer_card` method
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- `card` is now `get_card` with `card` kept for backwards compatibility and marked as deprecated
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.0.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `ConclusionScope` object for CRUD operations on conclusions (renamed from observations)
|
|
||||||
- Representation configuration support
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Observations renamed to Conclusions across the SDK
|
|
||||||
- Major SDK refactoring and cleanup
|
|
||||||
- Simplified method signatures throughout
|
|
||||||
- Representation endpoints now return `string` instead of old Representation object
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Standalone types module (now uses honcho-core types)
|
|
||||||
- Representation object
|
|
||||||
</Update>
|
|
||||||
<Update label="v1.6.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- metadata and configuration fields to Workspace, Peer, Session, and Message objects
|
|
||||||
- Session Clone methods
|
|
||||||
- Peer level get_context method
|
|
||||||
- `ObservationScope` object to perform CRUD operations on observations
|
|
||||||
- Representation object for WorkingRepresentations
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- methods that take IDs, can all optionally take an object of the same type
|
|
||||||
</Update>
|
|
||||||
<Update label="v1.5.0">
|
<Update label="v1.5.0">
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
|
@ -979,141 +439,6 @@ Welcome to the Honcho changelog! This section documents all notable changes to t
|
||||||
|
|
||||||
<Tab title="TypeScript SDK">
|
<Tab title="TypeScript SDK">
|
||||||
[TypeScript SDK](https://www.npmjs.com/package/@honcho-ai/sdk)
|
[TypeScript SDK](https://www.npmjs.com/package/@honcho-ai/sdk)
|
||||||
<Update label="v2.4.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Scopes: `honcho.scope()` get-or-creates a named visibility boundary, `honcho.scopes()` lists them, and a `Scope` object adds/removes sessions, lists membership, and reads backfill `status()`. `honcho.session({ scopes: [...] })` joins a new session to scopes at creation. Requires a Honcho server with the matching API support (Honcho v3.1.0+).
|
|
||||||
- `scope` option on `peer.chat()` / `chatStream()`, representation, session context, and workspace search. A single scope answers from that scope's collection and card; a list of scopes restricts recall to the union of their member sessions (explicit-only). Mutually exclusive with `session` / `sessions` / `filters`.
|
|
||||||
- Workspace-level chat: `honcho.chat()` / `honcho.chatStream()` ask a question across every peer in the workspace, with the same `session`, `scope`, `reasoningLevel`, and `responseFormat` options as `peer.chat()`. Requires a Honcho server with the matching API support (Honcho v3.1.0+).
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- `ConclusionScope` is renamed to `ConclusionsView`. The old name remains as a deprecated alias for one more minor version. "Scope" now means a named set of sessions (`Scope`); these objects are views over one observer/observed pair.
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.3.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `responseFormat` option on `peer.chat()` and `peer.chatStream()`, for constraining a dialectic answer to a schema. Pass a Zod schema to get a parsed, validated result back, or a raw JSON Schema object to get the JSON string as-is. Overloads type the return precisely, so a Zod schema narrows to its inferred type and a plain object narrows to `string`. On `chatStream()`, chunks stay raw text that accumulates to a JSON string — parse it after the stream completes. Requires a Honcho server with the matching API support (Honcho v3.0.12+).
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.2.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `ConclusionLevel` type (`explicit`, `deductive`, `inductive`, `contradiction`) and a `level` field on `Conclusion`, exposing the reasoning level the server already tracked but previously stripped from responses.
|
|
||||||
- `filters` option on `conclusions.list()` and `conclusions.query()`, passed through to the same dynamic server-side filter logic as the other list endpoints. Filter explicit-only conclusions with `{ filters: { level: 'explicit' } }`, or by any other supported field/operator. Requires a Honcho server with the matching API support (Honcho v3.0.11+).
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Scope-managed filter keys (`observer`, `observed`, `session`) are now rejected with a clear error if passed in `filters`, instead of silently overriding the scope and returning conclusions from a different peer pair. Use `peer.conclusions` / `peer.conclusionsOf(target)` and the dedicated `session` option instead. `session_id` remains a valid filter on `query()`.
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.1.2">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `peers` option on `Honcho.session()` — attach peers to a session at creation time instead of needing a follow-up `session.addPeers()` call. Accepts the same `PeerAddition` shape as `session.addPeers()` (peer ID strings, `Peer` objects, arrays of either, or a record with per-peer `observe_me`/`observe_others` config).
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- ID validation in `validation.ts` now accepts workspace, peer, and session IDs up to 512 characters (was 100), matching the server-side schema change in Honcho v3.0.7.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `Honcho.workspaces()` now actually forwards the `reverse` option to the server. The 2.1.0 changelog listed `workspaces()` among the list methods that gained `reverse`, but `client.ts` was missing the field on the params type and request builder, so the option was silently dropped. Honoring `reverse` on the workspace/peer/session list routes also requires a Honcho server with the matching API fix; older servers silently ignore the parameter.
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.1.1">
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Broadened fetch error retry logic to catch all `TypeError` network failures (connection resets, DNS errors, etc.) instead of only those with `'fetch'` in the message, improving resilience across runtimes (Node, Bun, browsers)
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.1.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `createdAt` property on `Peer` and `Session` wrapper objects
|
|
||||||
- `isActive` property on `Session` wrapper objects
|
|
||||||
- `getMessage(messageId)` method on `Session` to fetch a single message by ID
|
|
||||||
- `Peer.representation()`, `Session.representation()`, and `Session.context()` now accept `Message` objects for `searchQuery`
|
|
||||||
- `page`, `size`, and `reverse` pagination controls on all list methods
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- **Breaking**: `searchQuery` removed from top-level `context()` options — use `representationOptions.searchQuery` instead:
|
|
||||||
```typescript
|
|
||||||
// Before (v2.0.x)
|
|
||||||
await session.context({ searchQuery: "..." });
|
|
||||||
// After (v2.1.0)
|
|
||||||
await session.context({ representationOptions: { searchQuery: "..." } });
|
|
||||||
```
|
|
||||||
- List methods (`peers()`, `sessions()`, `messages()`, `workspaces()`) support both the new options object and the legacy raw-filter form
|
|
||||||
- Representation search options now accept strings and content-like objects, including `Message` instances, while rejecting whitespace-only or invalid runtime inputs
|
|
||||||
- **Breaking**: `peer()` and `session()` now always make a get-or-create API call — no more lazy initialization. If you relied on constructing SDK objects without triggering a network request, note that every `peer()` and `session()` call now hits the API:
|
|
||||||
```typescript
|
|
||||||
// Before (v2.0.x) — no API call
|
|
||||||
const session = honcho.session("my-session");
|
|
||||||
// After (v2.1.0) — makes a get-or-create API call
|
|
||||||
const session = await honcho.session("my-session");
|
|
||||||
```
|
|
||||||
- Response configuration models now tolerate unknown fields from newer servers for forward compatibility
|
|
||||||
- Moved `@types/node` from `dependencies` to `devDependencies`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `uploadFile()` now rejects unsupported top-level binary/object inputs and only validates inputs the serializer can actually upload
|
|
||||||
- `uploadFile()` now serializes message configuration using API field names, matching `addMessages()`
|
|
||||||
- Session fetch methods now refresh cached `createdAt` and `isActive` values alongside metadata and configuration
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.0.2">
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Client constructor now rejects unknown options via `.strict()` Zod validation. Previously, misspelled options (e.g., `baseUrl` instead of `baseURL`) were silently ignored, causing the SDK to fall back to defaults. Now a `ZodError` is thrown with the unrecognized key name.
|
|
||||||
- All input schemas now use `.strict()` validation to reject unknown fields.
|
|
||||||
- `FileUploadSchema.configuration` now uses `MessageConfigurationSchema` instead of open record type.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- README example used `baseUrl` instead of `baseURL`.
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.0.1">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `setPeerCard` method
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- `card` is now `getCard` with `card` kept for backwards compatibility and marked as deprecated
|
|
||||||
</Update>
|
|
||||||
<Update label="v2.0.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `ConclusionScope` object for CRUD operations on conclusions (renamed from observations)
|
|
||||||
- Representation configuration support
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Observations renamed to Conclusions across the SDK
|
|
||||||
- Major SDK refactoring and cleanup
|
|
||||||
- Simplified method signatures throughout
|
|
||||||
- Representation endpoints now return `string` instead of old Representation object
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Pagination `this` binding issue
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Representation object
|
|
||||||
- Stainless "core" SDK -- this SDK is now standalone
|
|
||||||
</Update>
|
|
||||||
<Update label="v1.6.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- metadata and configuration fields to Workspace, Peer, Session, and Message objects
|
|
||||||
- Session Clone methods
|
|
||||||
- Peer level get_context method
|
|
||||||
- `ObservationScope` object to perform CRUD operations on observations
|
|
||||||
- Representation object for WorkingRepresentations
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- methods that take IDs, can all optionally take an object of the same type
|
|
||||||
</Update>
|
|
||||||
<Update label="v1.5.0">
|
<Update label="v1.5.0">
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
|
@ -1183,54 +508,6 @@ Welcome to the Honcho changelog! This section documents all notable changes to t
|
||||||
- Simplified Honcho client import path
|
- Simplified Honcho client import path
|
||||||
</Update>
|
</Update>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Honcho CLI">
|
|
||||||
[Honcho CLI](https://pypi.org/project/honcho-cli/)
|
|
||||||
<Update label="v0.1.4">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- A TTY notice when a newer `honcho-cli` is on PyPI (`uv tool upgrade honcho-cli`). Skipped in JSON mode; disable with `HONCHO_NO_UPDATE_CHECK`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `--setup` for openai-compatible writes `EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL` into the profile `.env` alongside `LLM_OPENAI_BASE_URL` (#1068)
|
|
||||||
- `--setup` API key prompts echo `*` per character so a paste is visibly received instead of a blank getpass field
|
|
||||||
</Update>
|
|
||||||
<Update label="v0.1.3">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `honcho start`, `honcho stop`, and `honcho status` — run a personal Honcho stack in Docker (API, deriver, Postgres, Redis). Profiles live under `~/.honcho/profiles/`. First start pins `ghcr.io/plastic-labs/honcho:latest` by digest and copies the image `config.toml`. Optional `--setup basic` / `--setup advanced` wizard writes LLM overrides to `.env` (#1029)
|
|
||||||
- `honcho session view` — session transcript table (`--last N`, `--page N --size M`, `--all`, `--reverse`, `--ids`, peer filter via `-p`). Content is shown verbatim, timestamps are normalized to UTC, and the command is read-only: unlike the other session commands it never get-or-creates the session (#1006)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- `honcho message list --last N` no longer stops at the first page of 50 — it walks pages to fill the requested window (#1006)
|
|
||||||
</Update>
|
|
||||||
<Update label="v0.1.2">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Device-code OAuth login for managed Honcho servers. `honcho init` now offers browser-based login (RFC 8628 device authorization grant) when the host advertises the device grant in its OAuth authorization-server metadata; tokens are persisted to `~/.honcho/config.json` and auto-refreshed (#891)
|
|
||||||
- `HONCHO_CONFIG_DIR` environment variable for pointing the CLI at an alternate config directory (#891)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- An OAuth grant now records the host it was minted against and is ignored — neither used nor refreshed — when `base_url` points elsewhere, so a staging grant is never sent to production. A live OAuth token takes precedence over a stored `apiKey`, and a dead grant degrades to the saved key with a warning instead of aborting. Device login no longer deletes the shared `apiKey`, which sibling tools read from the same config file (#891)
|
|
||||||
</Update>
|
|
||||||
<Update label="v0.1.1">
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Declare `click` as an explicit dependency. The CLI imported `click` directly but relied on it being pulled in transitively, so installs without it on the path could fail at runtime (#787)
|
|
||||||
</Update>
|
|
||||||
<Update label="v0.1.0">
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Initial release of `honcho-cli` — a terminal for inspecting and managing a Honcho deployment (#424)
|
|
||||||
- `workspace`, `peer`, `session`, `message`, `conclusion`, and `config` command groups for managing resources against any Honcho server
|
|
||||||
- `init` onboarding flow that prompts for and persists connection settings, with flag/env-var pre-seeding for non-interactive use
|
|
||||||
- Per-command flags, environment variables, and a config file for pointing the CLI at different servers (local, self-hosted, or hosted)
|
|
||||||
- Rich terminal output and an agent-usage mode for scripting against the CLI
|
|
||||||
- Documentation and an agent skill for the CLI (#589)
|
|
||||||
</Update>
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
@ -1238,4 +515,4 @@ Welcome to the Honcho changelog! This section documents all notable changes to t
|
||||||
If you encounter issues using the Honcho API or its SDKs:
|
If you encounter issues using the Honcho API or its SDKs:
|
||||||
|
|
||||||
1. Open an issue on [GitHub](https://github.com/plastic-labs/honcho/issues)
|
1. Open an issue on [GitHub](https://github.com/plastic-labs/honcho/issues)
|
||||||
2. Join our [Discord community](http://discord.gg/honcho) for support
|
2. Join our [Discord community](http://discord.gg/plasticlabs) for support
|
||||||
|
|
|
||||||
435
docs/docs.json
|
|
@ -2,284 +2,29 @@
|
||||||
"$schema": "https://mintlify.com/docs.json",
|
"$schema": "https://mintlify.com/docs.json",
|
||||||
"theme": "mint",
|
"theme": "mint",
|
||||||
"name": "Honcho",
|
"name": "Honcho",
|
||||||
"redirects": [
|
|
||||||
{
|
|
||||||
"source": "/",
|
|
||||||
"destination": "/v3/documentation/introduction/overview"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "/v3/guides/integrations/claudecode",
|
|
||||||
"destination": "/v3/guides/integrations/claude-code"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "/v3/documentation/features/advanced/representation-scopes",
|
|
||||||
"destination": "/v3/documentation/features/advanced/directional-representations"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"colors": {
|
"colors": {
|
||||||
"primary": "#66AAFF",
|
"primary": "#86BCF2",
|
||||||
"dark": "#151E27",
|
"dark": "#151E27",
|
||||||
"light": "#86BCF2"
|
"light": "#B5D9FD"
|
||||||
},
|
},
|
||||||
"favicon": "/favicon.svg",
|
"favicon": "/favicon.svg",
|
||||||
"contextual": {
|
"contextual": {
|
||||||
"options": ["copy", "view", "chatgpt", "claude"]
|
"options": [
|
||||||
|
"copy",
|
||||||
|
"view",
|
||||||
|
"chatgpt",
|
||||||
|
"claude"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"versions": [
|
"versions": [
|
||||||
{
|
{
|
||||||
"version": "v3.1.1",
|
"version": "v2.4.2",
|
||||||
"api": {
|
"api": {
|
||||||
"openapi": ["v3/openapi.json"]
|
"openapi": [
|
||||||
},
|
"openapi.documented.yml"
|
||||||
"tabs": [
|
|
||||||
{
|
|
||||||
"tab": "Documentation",
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"group": "Introduction",
|
|
||||||
"pages": [
|
|
||||||
"v3/documentation/introduction/overview",
|
|
||||||
"v3/documentation/introduction/quickstart",
|
|
||||||
"v3/documentation/introduction/vibecoding"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"group": "Core Concepts",
|
|
||||||
"pages": [
|
|
||||||
"v3/documentation/core-concepts/architecture",
|
|
||||||
"v3/documentation/core-concepts/reasoning",
|
|
||||||
"v3/documentation/core-concepts/representation",
|
|
||||||
"v3/documentation/core-concepts/design-patterns"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Features",
|
|
||||||
"pages": [
|
|
||||||
"v3/documentation/features/storing-data",
|
|
||||||
"v3/documentation/features/get-context",
|
|
||||||
"v3/documentation/features/chat",
|
|
||||||
{
|
|
||||||
"group": "Advanced",
|
|
||||||
"pages": [
|
|
||||||
"v3/documentation/features/advanced/overview",
|
|
||||||
"v3/documentation/features/advanced/reasoning-configuration",
|
|
||||||
"v3/documentation/features/advanced/summarizer",
|
|
||||||
"v3/documentation/features/advanced/peer-card",
|
|
||||||
"v3/documentation/features/advanced/directional-representations",
|
|
||||||
"v3/documentation/features/advanced/scopes",
|
|
||||||
"v3/documentation/features/advanced/dreaming",
|
|
||||||
"v3/documentation/features/advanced/queue-status",
|
|
||||||
"v3/documentation/features/advanced/webhooks",
|
|
||||||
"v3/documentation/features/advanced/search",
|
|
||||||
"v3/documentation/features/advanced/using-filters",
|
|
||||||
"v3/documentation/features/advanced/structured-outputs",
|
|
||||||
"v3/documentation/features/advanced/streaming-response",
|
|
||||||
"v3/documentation/features/advanced/file-uploads",
|
|
||||||
"v3/documentation/features/advanced/deleting-data"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Reference",
|
|
||||||
"pages": [
|
|
||||||
"v3/documentation/reference/platform",
|
|
||||||
"v3/documentation/reference/sdk",
|
|
||||||
"v3/documentation/reference/cli"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tab": "Guides",
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"group": "Overview",
|
|
||||||
"pages": ["v3/guides/overview"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Integrations",
|
|
||||||
"pages": [
|
|
||||||
"v3/guides/integrations/claude-code",
|
|
||||||
"v3/guides/integrations/opencode",
|
|
||||||
"v3/guides/integrations/codex",
|
|
||||||
"v3/guides/integrations/deepseek-harness",
|
|
||||||
"v3/guides/integrations/vercel-ai-sdk",
|
|
||||||
"v3/guides/integrations/crewai",
|
|
||||||
"v3/guides/integrations/langgraph",
|
|
||||||
"v3/guides/integrations/mcp",
|
|
||||||
"v3/guides/integrations/n8n",
|
|
||||||
"v3/guides/integrations/openclaw",
|
|
||||||
"v3/guides/integrations/hermes",
|
|
||||||
"v3/guides/integrations/zo-computer",
|
|
||||||
"v3/guides/integrations/paperclip",
|
|
||||||
"v3/guides/integrations/sillytavern"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Tutorials",
|
|
||||||
"pages": [
|
|
||||||
"v3/guides/recipes/unified-memory-setup",
|
|
||||||
"v3/guides/discord",
|
|
||||||
"v3/guides/granola",
|
|
||||||
"v3/guides/telegram",
|
|
||||||
"v3/guides/integrations/reachy-mini",
|
|
||||||
"v3/guides/gmail"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Community Integrations",
|
|
||||||
"pages": [
|
|
||||||
"v3/guides/community/agent0",
|
|
||||||
"v3/guides/community/pi-honcho-memory"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Migrations",
|
|
||||||
"pages": ["v3/guides/migrations/mem0"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tab": "Open Source",
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"group": "Self-Hosting",
|
|
||||||
"pages": [
|
|
||||||
"v3/contributing/self-hosting",
|
|
||||||
"v3/contributing/configuration",
|
|
||||||
"v3/contributing/changing-embeddings",
|
|
||||||
"v3/contributing/troubleshooting"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Contributing",
|
|
||||||
"pages": [
|
|
||||||
"v3/contributing/guidelines",
|
|
||||||
"v3/contributing/license"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tab": "API Reference",
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"group": "API Documentation",
|
|
||||||
"pages": ["v3/api-reference/introduction"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "workspaces",
|
|
||||||
"pages": [
|
|
||||||
"v3/api-reference/endpoint/workspaces/get-or-create-workspace",
|
|
||||||
"v3/api-reference/endpoint/workspaces/get-all-workspaces",
|
|
||||||
"v3/api-reference/endpoint/workspaces/update-workspace",
|
|
||||||
"v3/api-reference/endpoint/workspaces/delete-workspace",
|
|
||||||
"v3/api-reference/endpoint/workspaces/search-workspace",
|
|
||||||
"v3/api-reference/endpoint/workspaces/get-queue-status",
|
|
||||||
"v3/api-reference/endpoint/workspaces/schedule-dream"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "peers",
|
|
||||||
"pages": [
|
|
||||||
"v3/api-reference/endpoint/peers/get-peers",
|
|
||||||
"v3/api-reference/endpoint/peers/get-or-create-peer",
|
|
||||||
"v3/api-reference/endpoint/peers/update-peer",
|
|
||||||
"v3/api-reference/endpoint/peers/get-sessions-for-peer",
|
|
||||||
"v3/api-reference/endpoint/peers/chat",
|
|
||||||
"v3/api-reference/endpoint/peers/get-representation",
|
|
||||||
"v3/api-reference/endpoint/peers/get-peer-card",
|
|
||||||
"v3/api-reference/endpoint/peers/set-peer-card",
|
|
||||||
"v3/api-reference/endpoint/peers/get-peer-context",
|
|
||||||
"v3/api-reference/endpoint/peers/search-peer"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "sessions",
|
|
||||||
"pages": [
|
|
||||||
"v3/api-reference/endpoint/sessions/get-or-create-session",
|
|
||||||
"v3/api-reference/endpoint/sessions/get-sessions",
|
|
||||||
"v3/api-reference/endpoint/sessions/update-session",
|
|
||||||
"v3/api-reference/endpoint/sessions/delete-session",
|
|
||||||
"v3/api-reference/endpoint/sessions/clone-session",
|
|
||||||
"v3/api-reference/endpoint/sessions/get-session-peers",
|
|
||||||
"v3/api-reference/endpoint/sessions/set-session-peers",
|
|
||||||
"v3/api-reference/endpoint/sessions/add-peers-to-session",
|
|
||||||
"v3/api-reference/endpoint/sessions/remove-peers-from-session",
|
|
||||||
"v3/api-reference/endpoint/sessions/get-peer-config",
|
|
||||||
"v3/api-reference/endpoint/sessions/set-peer-config",
|
|
||||||
"v3/api-reference/endpoint/sessions/get-session-context",
|
|
||||||
"v3/api-reference/endpoint/sessions/get-session-summaries",
|
|
||||||
"v3/api-reference/endpoint/sessions/search-session"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "scopes",
|
|
||||||
"pages": [
|
|
||||||
"v3/api-reference/endpoint/scopes/get-or-create-scope",
|
|
||||||
"v3/api-reference/endpoint/scopes/get-scopes",
|
|
||||||
"v3/api-reference/endpoint/scopes/get-scope",
|
|
||||||
"v3/api-reference/endpoint/scopes/add-sessions-to-scope",
|
|
||||||
"v3/api-reference/endpoint/scopes/get-scope-sessions",
|
|
||||||
"v3/api-reference/endpoint/scopes/remove-session-from-scope",
|
|
||||||
"v3/api-reference/endpoint/scopes/get-scope-status"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "messages",
|
|
||||||
"pages": [
|
|
||||||
"v3/api-reference/endpoint/messages/create-messages-for-session",
|
|
||||||
"v3/api-reference/endpoint/messages/get-messages",
|
|
||||||
"v3/api-reference/endpoint/messages/get-message",
|
|
||||||
"v3/api-reference/endpoint/messages/update-message",
|
|
||||||
"v3/api-reference/endpoint/messages/create-messages-with-file"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "conclusions",
|
|
||||||
"pages": [
|
|
||||||
"v3/api-reference/endpoint/conclusions/create-conclusions",
|
|
||||||
"v3/api-reference/endpoint/conclusions/list-conclusions",
|
|
||||||
"v3/api-reference/endpoint/conclusions/query-conclusions",
|
|
||||||
"v3/api-reference/endpoint/conclusions/delete-conclusion"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "webhooks",
|
|
||||||
"pages": [
|
|
||||||
"v3/api-reference/endpoint/webhooks/list-webhook-endpoints",
|
|
||||||
"v3/api-reference/endpoint/webhooks/get-or-create-webhook-endpoint",
|
|
||||||
"v3/api-reference/endpoint/webhooks/delete-webhook-endpoint",
|
|
||||||
"v3/api-reference/endpoint/webhooks/test-emit"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "miscellaneous",
|
|
||||||
"pages": ["v3/api-reference/endpoint/keys/create-key"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tab": "Changelog",
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"group": "Overview",
|
|
||||||
"pages": [
|
|
||||||
"changelog/introduction",
|
|
||||||
"changelog/compatibility-guide"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"version": "v2.5.1",
|
|
||||||
"api": {
|
|
||||||
"openapi": ["v2/openapi.json"]
|
|
||||||
},
|
|
||||||
"tabs": [
|
"tabs": [
|
||||||
{
|
{
|
||||||
"tab": "Documentation",
|
"tab": "Documentation",
|
||||||
|
|
@ -296,19 +41,10 @@
|
||||||
"group": "Core Concepts",
|
"group": "Core Concepts",
|
||||||
"pages": [
|
"pages": [
|
||||||
"v2/documentation/core-concepts/architecture",
|
"v2/documentation/core-concepts/architecture",
|
||||||
"v2/documentation/core-concepts/features/storing-data",
|
"v2/documentation/core-concepts/glossary",
|
||||||
"v2/documentation/core-concepts/features/dialectic-endpoint",
|
"v2/documentation/core-concepts/features",
|
||||||
"v2/documentation/core-concepts/features/get-context",
|
|
||||||
"v2/documentation/core-concepts/features/search",
|
|
||||||
"v2/documentation/core-concepts/features/working-rep",
|
|
||||||
"v2/documentation/core-concepts/features/streaming-response",
|
|
||||||
"v2/documentation/core-concepts/features/using-filters",
|
|
||||||
"v2/documentation/core-concepts/features/file-uploads",
|
|
||||||
"v2/documentation/core-concepts/features/queue-status",
|
|
||||||
"v2/documentation/core-concepts/features/local-vs-global",
|
|
||||||
"v2/documentation/core-concepts/configuration",
|
"v2/documentation/core-concepts/configuration",
|
||||||
"v2/documentation/core-concepts/summarizer",
|
"v2/documentation/core-concepts/summarizer"
|
||||||
"v2/documentation/core-concepts/glossary"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -325,27 +61,43 @@
|
||||||
"groups": [
|
"groups": [
|
||||||
{
|
{
|
||||||
"group": "Getting Started",
|
"group": "Getting Started",
|
||||||
"pages": ["v2/guides/overview"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Migrations",
|
|
||||||
"pages": ["v2/migrations/from-mem0"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "Integrations",
|
|
||||||
"pages": [
|
"pages": [
|
||||||
"v2/integrations/crewai",
|
"v2/guides/overview",
|
||||||
"v2/integrations/langgraph",
|
"v2/guides/mcp"
|
||||||
"v2/integrations/mcp"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "Application Interfaces",
|
"group": "Application Interfaces",
|
||||||
"pages": [
|
"pages": [
|
||||||
"v2/guides/discord",
|
"v2/guides/discord",
|
||||||
"v2/guides/n8n",
|
|
||||||
"v2/guides/telegram"
|
"v2/guides/telegram"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "Design Patterns",
|
||||||
|
"pages": [
|
||||||
|
"v2/guides/dialectic-endpoint",
|
||||||
|
"v2/guides/get-context",
|
||||||
|
"v2/guides/search",
|
||||||
|
"v2/guides/working-rep",
|
||||||
|
"v2/guides/streaming-response",
|
||||||
|
"v2/guides/using-filters",
|
||||||
|
"v2/guides/file-uploads"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tab": "Contributing",
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"group": "Contributing",
|
||||||
|
"pages": [
|
||||||
|
"v2/contributing/guidelines",
|
||||||
|
"v2/contributing/self-hosting",
|
||||||
|
"v2/contributing/configuration",
|
||||||
|
"v2/contributing/license"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -354,7 +106,9 @@
|
||||||
"groups": [
|
"groups": [
|
||||||
{
|
{
|
||||||
"group": "API Documentation",
|
"group": "API Documentation",
|
||||||
"pages": ["v2/api-reference/introduction"]
|
"pages": [
|
||||||
|
"v2/api-reference/introduction"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "workspaces",
|
"group": "workspaces",
|
||||||
|
|
@ -364,8 +118,7 @@
|
||||||
"v2/api-reference/endpoint/workspaces/update-workspace",
|
"v2/api-reference/endpoint/workspaces/update-workspace",
|
||||||
"v2/api-reference/endpoint/workspaces/delete-workspace",
|
"v2/api-reference/endpoint/workspaces/delete-workspace",
|
||||||
"v2/api-reference/endpoint/workspaces/search-workspace",
|
"v2/api-reference/endpoint/workspaces/search-workspace",
|
||||||
"v2/api-reference/endpoint/workspaces/get-deriver-status",
|
"v2/api-reference/endpoint/workspaces/get-deriver-status"
|
||||||
"v2/api-reference/endpoint/workspaces/trigger-dream"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -377,10 +130,8 @@
|
||||||
"v2/api-reference/endpoint/peers/get-sessions-for-peer",
|
"v2/api-reference/endpoint/peers/get-sessions-for-peer",
|
||||||
"v2/api-reference/endpoint/peers/chat",
|
"v2/api-reference/endpoint/peers/chat",
|
||||||
"v2/api-reference/endpoint/peers/get-working-representation",
|
"v2/api-reference/endpoint/peers/get-working-representation",
|
||||||
"v2/api-reference/endpoint/peers/get-peer-card",
|
"v2/api-reference/endpoint/peers/search-peer",
|
||||||
"v2/api-reference/endpoint/peers/set-peer-card",
|
"v2/api-reference/endpoint/peers/get-peer-card"
|
||||||
"v2/api-reference/endpoint/peers/get-peer-context",
|
|
||||||
"v2/api-reference/endpoint/peers/search-peer"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -412,15 +163,6 @@
|
||||||
"v2/api-reference/endpoint/messages/create-messages-with-file"
|
"v2/api-reference/endpoint/messages/create-messages-with-file"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"group": "observations",
|
|
||||||
"pages": [
|
|
||||||
"v2/api-reference/endpoint/observations/create-observations",
|
|
||||||
"v2/api-reference/endpoint/observations/list-observations",
|
|
||||||
"v2/api-reference/endpoint/observations/query-observations",
|
|
||||||
"v2/api-reference/endpoint/observations/delete-observation"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"group": "webhooks",
|
"group": "webhooks",
|
||||||
"pages": [
|
"pages": [
|
||||||
|
|
@ -440,15 +182,13 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tab": "Contributing",
|
"tab": "Changelog",
|
||||||
"groups": [
|
"groups": [
|
||||||
{
|
{
|
||||||
"group": "Contributing",
|
"group": "Overview",
|
||||||
"pages": [
|
"pages": [
|
||||||
"v2/contributing/guidelines",
|
"changelog/introduction",
|
||||||
"v2/contributing/self-hosting",
|
"changelog/compatibility-guide"
|
||||||
"v2/contributing/configuration",
|
|
||||||
"v2/contributing/license"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -458,7 +198,9 @@
|
||||||
{
|
{
|
||||||
"version": "v1.1.0",
|
"version": "v1.1.0",
|
||||||
"api": {
|
"api": {
|
||||||
"openapi": ["openapi.json"]
|
"openapi": [
|
||||||
|
"openapi.json"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"tabs": [
|
"tabs": [
|
||||||
{
|
{
|
||||||
|
|
@ -488,15 +230,23 @@
|
||||||
"groups": [
|
"groups": [
|
||||||
{
|
{
|
||||||
"group": "Getting Started",
|
"group": "Getting Started",
|
||||||
"pages": ["v1/guides/overview", "v1/guides/streaming-response"]
|
"pages": [
|
||||||
|
"v1/guides/overview",
|
||||||
|
"v1/guides/streaming-response"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "Application Interfaces",
|
"group": "Application Interfaces",
|
||||||
"pages": ["v1/guides/discord", "v1/guides/honcho-mcp"]
|
"pages": [
|
||||||
|
"v1/guides/discord",
|
||||||
|
"v1/guides/honcho-mcp"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "Personal Memory",
|
"group": "Personal Memory",
|
||||||
"pages": ["v1/guides/dialectic-endpoint"]
|
"pages": [
|
||||||
|
"v1/guides/dialectic-endpoint"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -505,7 +255,9 @@
|
||||||
"groups": [
|
"groups": [
|
||||||
{
|
{
|
||||||
"group": "API Documentation",
|
"group": "API Documentation",
|
||||||
"pages": ["v1/api-reference/introduction"]
|
"pages": [
|
||||||
|
"v1/api-reference/introduction"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "apps",
|
"group": "apps",
|
||||||
|
|
@ -553,7 +305,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "keys",
|
"group": "keys",
|
||||||
"pages": ["v1/api-reference/endpoint/keys/create-key"]
|
"pages": [
|
||||||
|
"v1/api-reference/endpoint/keys/create-key"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "metamessages",
|
"group": "metamessages",
|
||||||
|
|
@ -587,10 +341,41 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"tab": "Changelog",
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"group": "Overview",
|
||||||
|
"pages": [
|
||||||
|
"changelog/introduction",
|
||||||
|
"changelog/compatibility-guide"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"global": {
|
||||||
|
"anchors": [
|
||||||
|
{
|
||||||
|
"anchor": "Managed Platform",
|
||||||
|
"href": "https://app.honcho.dev",
|
||||||
|
"icon": "book-open-cover"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anchor": "Community",
|
||||||
|
"href": "https://discord.gg/honcho",
|
||||||
|
"icon": "discord"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anchor": "Blog",
|
||||||
|
"href": "https://blog.plasticlabs.ai",
|
||||||
|
"icon": "newspaper"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"logo": {
|
"logo": {
|
||||||
"light": "/logo/honcho-dark.svg",
|
"light": "/logo/honcho-dark.svg",
|
||||||
|
|
@ -604,16 +389,14 @@
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"socials": {
|
"socials": {
|
||||||
"twitter": "https://x.com/honchodotdev",
|
"twitter": "https://twitter.com/plastic_labs",
|
||||||
"github": "https://github.com/plastic-labs/honcho",
|
"github": "https://github.com/plastic-labs",
|
||||||
"discord": "https://discord.gg/honcho",
|
"linkedin": "https://www.linkedin.com/company/plasticlabs"
|
||||||
"linkedin": "https://www.linkedin.com/company/plasticlabs",
|
|
||||||
"youtube": "https://www.youtube.com/@plasticlabs"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"integrations": {
|
"integrations": {
|
||||||
"gtm": {
|
"posthog": {
|
||||||
"tagId": "GTM-NSPT9PJF"
|
"apiKey": "phc_1yrzzcgywqXGcerkkI4g7C0YfyPMcAKNOOvGcjTCiUk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 265 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 172 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 244 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 243 KiB |
|
Before Width: | Height: | Size: 235 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 414 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 418 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 323 KiB |
|
|
@ -5,15 +5,16 @@
|
||||||
"main": ".pnp.js",
|
"main": ".pnp.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "mint dev",
|
"dev": "mint dev",
|
||||||
"openapi": "npx @mintlify/scraping openapi-file v3/openapi.json -o v3/api-reference/endpoint",
|
"openapi": "npx @mintlify/scraping openapi-file openapi.documented.yml -o api-reference/endpoint",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mintlify/scraping": "^4.0.467"
|
"@mintlify/scraping": "^4.0.284",
|
||||||
|
"honcho-ai": "^0.0.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mint": "^4.2.204"
|
"mint": "^4.2.123"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
// Loads PostHog only when the CookieConsent cookie grants Statistics; the
|
|
||||||
// cookie is host-scoped, so a landing-page answer covers the docs.
|
|
||||||
;(function () {
|
|
||||||
var KEY = 'phc_1yrzzcgywqXGcerkkI4g7C0YfyPMcAKNOOvGcjTCiUk'
|
|
||||||
var loaded = false
|
|
||||||
|
|
||||||
function granted() {
|
|
||||||
var m = document.cookie.match(/(?:^|;\s*)CookieConsent=([^;]*)/)
|
|
||||||
if (!m) return false
|
|
||||||
var v = decodeURIComponent(m[1])
|
|
||||||
// "-1" is Cookiebot's consent-not-required marker.
|
|
||||||
return v === '-1' || /statistics\s*:\s*true/.test(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadPosthog() {
|
|
||||||
if (loaded) return
|
|
||||||
loaded = true
|
|
||||||
var s = document.createElement('script')
|
|
||||||
s.src = 'https://us-assets.i.posthog.com/static/array.js'
|
|
||||||
s.async = true
|
|
||||||
s.onerror = function () {
|
|
||||||
loaded = false
|
|
||||||
}
|
|
||||||
s.onload = function () {
|
|
||||||
// Consent withdrawn while array.js was downloading: skip init, allow a retry on re-grant.
|
|
||||||
if (!granted()) {
|
|
||||||
loaded = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
window.posthog.init(KEY, {
|
|
||||||
api_host: 'https://us.i.posthog.com',
|
|
||||||
ui_host: 'https://us.posthog.com',
|
|
||||||
cross_subdomain_cookie: true,
|
|
||||||
person_profiles: 'identified_only',
|
|
||||||
capture_pageview: 'history_change',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
document.head.appendChild(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
function sync() {
|
|
||||||
if (granted()) {
|
|
||||||
if (!loaded) {
|
|
||||||
loadPosthog()
|
|
||||||
} else if (
|
|
||||||
window.posthog &&
|
|
||||||
window.posthog.has_opted_out_capturing &&
|
|
||||||
window.posthog.has_opted_out_capturing()
|
|
||||||
) {
|
|
||||||
window.posthog.opt_in_capturing()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Withdrawal mid-session: an already running instance must stop.
|
|
||||||
if (loaded && window.posthog && window.posthog.opt_out_capturing) {
|
|
||||||
window.posthog.opt_out_capturing()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sync()
|
|
||||||
var events = [
|
|
||||||
'CookiebotOnConsentReady',
|
|
||||||
'CookiebotOnAccept',
|
|
||||||
'CookiebotOnDecline',
|
|
||||||
]
|
|
||||||
for (var i = 0; i < events.length; i++) {
|
|
||||||
window.addEventListener(events[i], sync)
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
@ -1,652 +0,0 @@
|
||||||
{/*
|
|
||||||
GENERATED by honcho-cli/scripts/generate_cli_docs.py — do not edit.
|
|
||||||
Re-generate with: uv run --package honcho-cli python honcho-cli/scripts/generate_cli_docs.py
|
|
||||||
Source of truth: honcho-cli/src/honcho_cli/commands/
|
|
||||||
*/}
|
|
||||||
|
|
||||||
## honcho conclusion
|
|
||||||
|
|
||||||
List, search, create, and delete peer conclusions (Honcho's memory atoms).
|
|
||||||
|
|
||||||
<AccordionGroup>
|
|
||||||
<Accordion title="create">
|
|
||||||
Create a conclusion.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho conclusion create <content>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="content" type="string" required />
|
|
||||||
<ParamField path="--observer" type="string">
|
|
||||||
Observer peer ID.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--observed" type="string">
|
|
||||||
Observed peer ID.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--session" type="string">
|
|
||||||
Session context. Short alias: `-s`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="delete">
|
|
||||||
Delete a conclusion.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho conclusion delete <conclusion_id>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="conclusion_id" type="string" required />
|
|
||||||
<ParamField path="--observer" type="string">
|
|
||||||
Observer peer ID.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--observed" type="string">
|
|
||||||
Observed peer ID.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--yes" type="boolean">
|
|
||||||
Skip confirmation. Short alias: `-y`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="list">
|
|
||||||
List conclusions.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho conclusion list
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="--observer" type="string">
|
|
||||||
Observer peer ID.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--observed" type="string">
|
|
||||||
Observed peer ID.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--limit" type="number" default="10">
|
|
||||||
Max results.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="search">
|
|
||||||
Semantic search over conclusions.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho conclusion search <query>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="query" type="string" required />
|
|
||||||
<ParamField path="--observer" type="string">
|
|
||||||
Observer peer ID.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--observed" type="string">
|
|
||||||
Observed peer ID.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--top-k" type="number" default="10">
|
|
||||||
Max results.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
</AccordionGroup>
|
|
||||||
|
|
||||||
## honcho config
|
|
||||||
|
|
||||||
Inspect CLI configuration.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho config
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## honcho doctor
|
|
||||||
|
|
||||||
Verify config and connectivity. Scope with -w / -p to check workspace, peer, and queue health.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho doctor
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## honcho help
|
|
||||||
|
|
||||||
Show help message.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho help
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## honcho init
|
|
||||||
|
|
||||||
Set API key and server URL in ~/.honcho/config.json.
|
|
||||||
|
|
||||||
Press Enter to keep the current value or type a replacement.
|
|
||||||
Workspace / peer / session scoping is per-command via -w / -p / -s
|
|
||||||
or HONCHO_* env vars — never persisted.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho init
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="--api-key" type="string">
|
|
||||||
API key (admin JWT).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--base-url" type="string">
|
|
||||||
Honcho API URL (e.g. https://api.honcho.dev, http://localhost:8000).
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
## honcho message
|
|
||||||
|
|
||||||
List, create, and get messages within a session.
|
|
||||||
|
|
||||||
<AccordionGroup>
|
|
||||||
<Accordion title="create">
|
|
||||||
Create a message in a session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho message create <content>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="content" type="string" required />
|
|
||||||
<ParamField path="--peer" type="string" required>
|
|
||||||
Peer ID of the message sender. Short alias: `-p`.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--metadata" type="string">
|
|
||||||
JSON metadata to associate with the message.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--session" type="string">
|
|
||||||
Session ID. Short alias: `-s`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="get">
|
|
||||||
Get a single message by ID.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho message get <message_id>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="message_id" type="string" required />
|
|
||||||
<ParamField path="--session" type="string">
|
|
||||||
Session ID. Short alias: `-s`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="list">
|
|
||||||
List messages in a session. Scoped to a peer with -p.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho message list [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
<ParamField path="--last" type="number" default="20">
|
|
||||||
Number of recent messages.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--reverse" type="boolean">
|
|
||||||
Show oldest first (default is newest first).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--brief" type="boolean">
|
|
||||||
Show only IDs, peer, token count, and created_at (no content).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--peer" type="string">
|
|
||||||
Filter by peer ID. Short alias: `-p`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
</AccordionGroup>
|
|
||||||
|
|
||||||
## honcho peer
|
|
||||||
|
|
||||||
List, create, chat with, search, and manage peers and their representations.
|
|
||||||
|
|
||||||
<AccordionGroup>
|
|
||||||
<Accordion title="card">
|
|
||||||
Get raw peer card content.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho peer card [<peer_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="peer_id" type="string" />
|
|
||||||
<ParamField path="--target" type="string">
|
|
||||||
Target peer for relationship card.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="chat">
|
|
||||||
Query the dialectic about a peer.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho peer chat <query>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="query" type="string" required />
|
|
||||||
<ParamField path="--target" type="string">
|
|
||||||
Target peer for perspective.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--reasoning" type="string">
|
|
||||||
Reasoning level: minimal, low, medium, high, max. Short alias: `-r`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="create">
|
|
||||||
Create or get a peer.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho peer create <peer_id>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="peer_id" type="string" required />
|
|
||||||
<ParamField path="--observe-me" type="boolean">
|
|
||||||
Whether Honcho will form a representation of this peer. Negate with `--no-observe-me`.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--metadata" type="string">
|
|
||||||
JSON metadata to associate with the peer.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="get-metadata">
|
|
||||||
Get metadata for a peer.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho peer get-metadata [<peer_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="peer_id" type="string" />
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="inspect">
|
|
||||||
Inspect a peer: card, session count, recent conclusions.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho peer inspect [<peer_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="peer_id" type="string" />
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="list">
|
|
||||||
List all peers in the workspace.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho peer list
|
|
||||||
```
|
|
||||||
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="representation">
|
|
||||||
Get the formatted representation for a peer.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho peer representation [<peer_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="peer_id" type="string" />
|
|
||||||
<ParamField path="--target" type="string">
|
|
||||||
Target peer to get representation about.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--search-query" type="string">
|
|
||||||
Semantic search query to filter conclusions.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--max-conclusions" type="number">
|
|
||||||
Maximum number of conclusions to include.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="search">
|
|
||||||
Search a peer's messages.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho peer search <query>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="query" type="string" required />
|
|
||||||
<ParamField path="--limit" type="number" default="10">
|
|
||||||
Max results.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="set-metadata">
|
|
||||||
Set metadata for a peer.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho peer set-metadata <metadata>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="metadata" type="string" required />
|
|
||||||
<ParamField path="--peer" type="string">
|
|
||||||
Peer ID (uses default if omitted). Short alias: `-p`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
</AccordionGroup>
|
|
||||||
|
|
||||||
## honcho session
|
|
||||||
|
|
||||||
List, inspect, view, create, delete, and manage conversation sessions and their peers.
|
|
||||||
|
|
||||||
<AccordionGroup>
|
|
||||||
<Accordion title="add-peers">
|
|
||||||
Add peers to a session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session add-peers <session_id> <peer_ids>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" required />
|
|
||||||
<ParamField path="peer_ids" type="string" required />
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="context">
|
|
||||||
Get session context (what an agent would see).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session context [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
<ParamField path="--tokens" type="number">
|
|
||||||
Token budget.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--summary" type="boolean" default="true">
|
|
||||||
Include summary. Negate with `--no-summary`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="create">
|
|
||||||
Create or get a session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session create <session_id>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" required />
|
|
||||||
<ParamField path="--peers" type="string">
|
|
||||||
Comma-separated peer IDs to add to the session.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--metadata" type="string">
|
|
||||||
JSON metadata to associate with the session.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="delete">
|
|
||||||
Delete a session and all its data. Destructive — requires --yes or interactive confirm.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session delete [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
<ParamField path="--yes" type="boolean">
|
|
||||||
Skip confirmation. Short alias: `-y`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="get-metadata">
|
|
||||||
Get metadata for a session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session get-metadata [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="inspect">
|
|
||||||
Inspect a session: peers, message count, summaries, config.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session inspect [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="list">
|
|
||||||
List sessions in the workspace.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session list
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="--peer" type="string">
|
|
||||||
Filter by peer. Short alias: `-p`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="peers">
|
|
||||||
List peers in a session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session peers [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="remove-peers">
|
|
||||||
Remove peers from a session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session remove-peers <session_id> <peer_ids>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" required />
|
|
||||||
<ParamField path="peer_ids" type="string" required />
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="representation">
|
|
||||||
Get the representation of a peer within a session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session representation <peer_id> [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="peer_id" type="string" required />
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
<ParamField path="--target" type="string">
|
|
||||||
Target peer (what peer_id knows about target).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--search-query" type="string">
|
|
||||||
Semantic search query to filter conclusions.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--max-conclusions" type="number">
|
|
||||||
Maximum number of conclusions to include.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="search">
|
|
||||||
Search messages in a session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session search <query> [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="query" type="string" required />
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
<ParamField path="--limit" type="number" default="10">
|
|
||||||
Max results.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="set-metadata">
|
|
||||||
Set metadata for a session.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session set-metadata [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
<ParamField path="--data" type="string" required>
|
|
||||||
JSON metadata to set (e.g. '\{"key": "value"\}'). Short alias: `-d`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="summaries">
|
|
||||||
Get session summaries (short + long).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session summaries [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="view">
|
|
||||||
View a session transcript as a chat log.
|
|
||||||
|
|
||||||
Modes (pick one):
|
|
||||||
|
|
||||||
- default / --last N: tail of the conversation (most recent N)
|
|
||||||
- --page N [--size M]: page through the full transcript
|
|
||||||
- --all: every message
|
|
||||||
|
|
||||||
Paging follows the requested order: --page 1 starts at the oldest message,
|
|
||||||
or the newest with --reverse.
|
|
||||||
|
|
||||||
Human mode prints a row-delimited table. JSON mode emits the message list
|
|
||||||
(same shape as message list).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho session view [<session_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="session_id" type="string" />
|
|
||||||
<ParamField path="--last" type="number">
|
|
||||||
Show only the N most recent messages (default when no --page/--all: 50).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--page" type="number">
|
|
||||||
1-indexed page of the full transcript. Use for page 2+.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--size" type="number">
|
|
||||||
Messages per page; requires --page (1-100, default: 50).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--all" type="boolean">
|
|
||||||
Show the full transcript (every page).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--reverse" type="boolean">
|
|
||||||
Newest first (default is chronological: oldest at top).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--ids" type="boolean">
|
|
||||||
Include message IDs in the transcript.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--peer" type="string">
|
|
||||||
Filter by peer ID. Short alias: `-p`.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
</AccordionGroup>
|
|
||||||
|
|
||||||
## honcho start
|
|
||||||
|
|
||||||
Start a local Honcho stack (API, deriver, Postgres, Redis).
|
|
||||||
|
|
||||||
Requires Docker. Uses cloud LLM providers. Does not change the CLI's
|
|
||||||
configured server URL — pass HONCHO_BASE_URL to talk to this stack.
|
|
||||||
``--setup basic`` or ``--setup advanced`` runs an interactive config wizard.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho start
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="--profile" type="string" default="local">
|
|
||||||
Local stack profile name.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--api-port" type="string">
|
|
||||||
Host port for the API.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--db-port" type="string">
|
|
||||||
Host port for Postgres.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--redis-port" type="string">
|
|
||||||
Host port for Redis.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--setup" type="string">
|
|
||||||
Interactive config wizard: basic (provider/model) or advanced (embeddings, deriver, dialectic, dreams, flush).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--image" type="string">
|
|
||||||
Honcho image to pull and pin by digest (default: ghcr.io/plastic-labs/honcho:latest).
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--timeout" type="string" default="180">
|
|
||||||
Seconds to wait for /health after compose up.
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
## honcho status
|
|
||||||
|
|
||||||
Show local stack endpoints and container health.
|
|
||||||
|
|
||||||
With no ``--profile``, lists every stack under ``~/.honcho/profiles/``.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho status
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="--profile" type="string">
|
|
||||||
Limit to this profile. Omit to show every local stack.
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
## honcho stop
|
|
||||||
|
|
||||||
Stop the local stack started by `honcho start`. Keeps data unless --wipe.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho stop
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="--profile" type="string" default="local">
|
|
||||||
Local stack profile name.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--wipe" type="boolean">
|
|
||||||
Also delete volumes (Postgres data).
|
|
||||||
</ParamField>
|
|
||||||
|
|
||||||
## honcho workspace
|
|
||||||
|
|
||||||
List, create, inspect, delete, and search workspaces.
|
|
||||||
|
|
||||||
<AccordionGroup>
|
|
||||||
<Accordion title="create">
|
|
||||||
Create or get a workspace.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho workspace create <workspace_id>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="workspace_id" type="string" required />
|
|
||||||
<ParamField path="--metadata" type="string">
|
|
||||||
JSON metadata to associate with the workspace.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="delete">
|
|
||||||
Delete a workspace. Use --dry-run first to see what will be deleted.
|
|
||||||
|
|
||||||
Requires --yes to skip confirmation, or will prompt interactively.
|
|
||||||
If sessions exist, requires --cascade to delete them first.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho workspace delete <workspace_id>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="workspace_id" type="string" required />
|
|
||||||
<ParamField path="--yes" type="boolean">
|
|
||||||
Skip confirmation prompt (for scripted/agent use). Short alias: `-y`.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--cascade" type="boolean">
|
|
||||||
Delete all sessions before deleting the workspace.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--dry-run" type="boolean">
|
|
||||||
Show what would be deleted without deleting.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="inspect">
|
|
||||||
Inspect a workspace: peers, sessions, config.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho workspace inspect [<workspace_id>]
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="workspace_id" type="string" />
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="list">
|
|
||||||
List all accessible workspaces.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho workspace list
|
|
||||||
```
|
|
||||||
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="queue-status">
|
|
||||||
Get queue processing status.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho workspace queue-status
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="--observer" type="string">
|
|
||||||
Filter by observer peer.
|
|
||||||
</ParamField>
|
|
||||||
<ParamField path="--sender" type="string">
|
|
||||||
Filter by sender peer.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="search">
|
|
||||||
Search messages across workspace.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
honcho workspace search <query>
|
|
||||||
```
|
|
||||||
|
|
||||||
<ParamField path="query" type="string" required />
|
|
||||||
<ParamField path="--limit" type="number" default="10">
|
|
||||||
Max results.
|
|
||||||
</ParamField>
|
|
||||||
</Accordion>
|
|
||||||
</AccordionGroup>
|
|
||||||
|
|
@ -11,7 +11,7 @@ indicate a feature or bug fix you are working on.
|
||||||
|
|
||||||
Once you have finished your contribution make a PR , and it will be reviewed by
|
Once you have finished your contribution make a PR , and it will be reviewed by
|
||||||
a project manager. Feel free to join us in our
|
a project manager. Feel free to join us in our
|
||||||
[discord](http://discord.gg/honcho) to discuss your changes or get help.
|
[discord](http://discord.gg/plasticlabs) to discuss your changes or get help.
|
||||||
|
|
||||||
Your changes will undergo a period of testing and discussion before finally
|
Your changes will undergo a period of testing and discussion before finally
|
||||||
being entered into the `main` branch and being staged for release. For more
|
being entered into the `main` branch and being staged for release. For more
|
||||||
|
|
|
||||||
|
|
@ -59,4 +59,4 @@ Finally, Claude needs instructions on how to use Honcho. The Desktop app doesn't
|
||||||
|
|
||||||
<Note>Be sure to update the \<app_name\> and \<user_name\> variables in the instructions.txt file.</Note>
|
<Note>Be sure to update the \<app_name\> and \<user_name\> variables in the instructions.txt file.</Note>
|
||||||
|
|
||||||
Claude should then query for insights before responding and write your messages to storage! If you come up with more creative ways to get Claude to manage its own memory with Honcho, feel free to [let us know](https://discord.gg/honcho) or make a PR on this [repo](https://github.com/plastic-labs/honcho-mcp/tree/main)!
|
Claude should then query for insights before responding and write your messages to storage! If you come up with more creative ways to get Claude to manage its own memory with Honcho, feel free to [let us know](https://discord.gg/plasticlabs) or make a PR on this [repo](https://github.com/plastic-labs/honcho-mcp/tree/main)!
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
openapi: post /v2/workspaces/{workspace_id}/observations
|
|
||||||
---
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
openapi: delete /v2/workspaces/{workspace_id}/observations/{observation_id}
|
|
||||||
---
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
openapi: post /v2/workspaces/{workspace_id}/observations/list
|
|
||||||
---
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
openapi: post /v2/workspaces/{workspace_id}/observations/query
|
|
||||||
---
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
openapi: get /v2/workspaces/{workspace_id}/peers/{peer_id}/context
|
|
||||||
---
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
openapi: put /v2/workspaces/{workspace_id}/peers/{peer_id}/card
|
|
||||||
---
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
openapi: post /v2/workspaces/{workspace_id}/trigger_dream
|
|
||||||
---
|
|
||||||
|
|
@ -46,19 +46,14 @@ cp config.toml.example config.toml
|
||||||
|
|
||||||
Then modify the values as needed. The TOML file is organized into sections:
|
Then modify the values as needed. The TOML file is organized into sections:
|
||||||
|
|
||||||
- `[app]` - Application-level settings (log level, session limits, embedding settings, Langfuse integration, local metrics collection)
|
- `[app]` - Application-level settings (log level, host, port, embedding settings)
|
||||||
- `[db]` - Database connection and pool settings (connection URI, pool size, timeouts, connection recycling)
|
- `[db]` - Database connection and pool settings
|
||||||
- `[auth]` - Authentication configuration (enable/disable auth, JWT secret)
|
- `[auth]` - Authentication configuration
|
||||||
- `[cache]` - Redis cache configuration (enable/disable caching, Redis URL, TTL settings, lock configuration for cache stampede prevention)
|
- `[llm]` - LLM provider API keys and general settings
|
||||||
- `[llm]` - LLM provider API keys (Anthropic, OpenAI, Gemini, Groq, OpenAI-compatible endpoints) and general LLM settings
|
- `[dialectic]` - Dialectic API configuration (provider, model, search settings)
|
||||||
- `[dialectic]` - Dialectic API configuration (provider, model, query generation settings, semantic search parameters, context window size)
|
- `[deriver]` - Background worker settings and theory of mind configuration
|
||||||
- `[deriver]` - Background worker settings (worker count, polling intervals, queue management) and theory of mind configuration (model, tokens, observation limits)
|
- `[summary]` - Session summarization settings
|
||||||
- `[peer_card]` - Peer card generation settings (provider, model, token limits)
|
- `[sentry]` - Error tracking and monitoring settings
|
||||||
- `[summary]` - Session summarization settings (frequency thresholds, provider, model, token limits for short and long summaries)
|
|
||||||
- `[dream]` - Dream processing configuration (enable/disable, thresholds, idle timeouts, dream types, LLM settings)
|
|
||||||
- `[webhook]` - Webhook configuration (webhook secret, workspace limits)
|
|
||||||
- `[metrics]` - Metrics collection settings (enable/disable metrics, namespace)
|
|
||||||
- `[sentry]` - Error tracking and monitoring settings (enable/disable, DSN, environment, sample rates)
|
|
||||||
|
|
||||||
### Using Environment Variables
|
### Using Environment Variables
|
||||||
|
|
||||||
|
|
@ -96,14 +91,14 @@ If you have this in `config.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[db]
|
[db]
|
||||||
CONNECTION_URI = "postgresql+psycopg://localhost/honcho_dev"
|
CONNECTION_URI = "postgresql://localhost/honcho_dev"
|
||||||
POOL_SIZE = 10
|
POOL_SIZE = 10
|
||||||
```
|
```
|
||||||
|
|
||||||
You can override just the connection URI in production:
|
You can override just the connection URI in production:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export DB_CONNECTION_URI="postgresql+psycopg://prod-server/honcho_prod"
|
export DB_CONNECTION_URI="postgresql://prod-server/honcho_prod"
|
||||||
```
|
```
|
||||||
|
|
||||||
The application will use the production connection URI while keeping the pool size from config.toml.
|
The application will use the production connection URI while keeping the pool size from config.toml.
|
||||||
|
|
@ -112,33 +107,28 @@ The application will use the production connection URI while keeping the pool si
|
||||||
|
|
||||||
### Application Settings
|
### Application Settings
|
||||||
|
|
||||||
Application-level settings control core behavior of the Honcho server including logging, session limits, message handling, and optional integrations.
|
|
||||||
|
|
||||||
**Basic Application Configuration:**
|
**Basic Application Configuration:**
|
||||||
```bash
|
```bash
|
||||||
# Logging and server settings
|
# Logging and server settings
|
||||||
LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR
|
||||||
|
SESSION_PEERS_LIMIT=10
|
||||||
|
GET_CONTEXT_MAX_TOKENS=100000
|
||||||
|
|
||||||
# Session and context limits
|
# Embedding settings (optional)
|
||||||
SESSION_OBSERVERS_LIMIT=10 # Maximum number of observers per session
|
EMBED_MESSAGES=false
|
||||||
GET_CONTEXT_MAX_TOKENS=100000 # Maximum tokens for context retrieval
|
MAX_EMBEDDING_TOKENS=8192
|
||||||
MAX_MESSAGE_SIZE=25000 # Maximum message size in characters
|
MAX_EMBEDDING_TOKENS_PER_REQUEST=300000
|
||||||
|
|
||||||
# Embedding settings
|
|
||||||
EMBED_MESSAGES=true # Enable vector embeddings for messages
|
|
||||||
MAX_EMBEDDING_TOKENS=8192 # Maximum tokens per embedding
|
|
||||||
MAX_EMBEDDING_TOKENS_PER_REQUEST=300000 # Batch embedding limit
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Optional Integrations:**
|
**Environment-specific settings:**
|
||||||
```bash
|
```bash
|
||||||
# Langfuse integration for LLM observability
|
# Development
|
||||||
LANGFUSE_HOST=https://cloud.langfuse.com
|
LOG_LEVEL=DEBUG
|
||||||
LANGFUSE_PUBLIC_KEY=your-langfuse-public-key
|
FASTAPI_HOST=127.0.0.1
|
||||||
|
|
||||||
# Local metrics collection
|
# Production
|
||||||
COLLECT_METRICS_LOCAL=false
|
LOG_LEVEL=WARNING
|
||||||
LOCAL_METRICS_FILE=metrics.jsonl
|
FASTAPI_HOST=0.0.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
### Database Configuration
|
### Database Configuration
|
||||||
|
|
@ -149,7 +139,7 @@ LOCAL_METRICS_FILE=metrics.jsonl
|
||||||
DB_CONNECTION_URI=postgresql+psycopg://username:password@host:port/database
|
DB_CONNECTION_URI=postgresql+psycopg://username:password@host:port/database
|
||||||
|
|
||||||
# Example for local development
|
# Example for local development
|
||||||
DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/postgres
|
DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/honcho
|
||||||
|
|
||||||
# Example for production
|
# Example for production
|
||||||
DB_CONNECTION_URI=postgresql+psycopg://honcho_user:secure_password@db.example.com:5432/honcho_prod
|
DB_CONNECTION_URI=postgresql+psycopg://honcho_user:secure_password@db.example.com:5432/honcho_prod
|
||||||
|
|
@ -161,7 +151,7 @@ DB_CONNECTION_URI=postgresql+psycopg://honcho_user:secure_password@db.example.co
|
||||||
DB_SCHEMA=public
|
DB_SCHEMA=public
|
||||||
DB_POOL_SIZE=10
|
DB_POOL_SIZE=10
|
||||||
DB_MAX_OVERFLOW=20
|
DB_MAX_OVERFLOW=20
|
||||||
DB_POOL_TIMEOUT=5
|
DB_POOL_TIMEOUT=30
|
||||||
DB_POOL_RECYCLE=300
|
DB_POOL_RECYCLE=300
|
||||||
DB_POOL_PRE_PING=true
|
DB_POOL_PRE_PING=true
|
||||||
DB_SQL_DEBUG=false
|
DB_SQL_DEBUG=false
|
||||||
|
|
@ -206,36 +196,6 @@ AUTH_JWT_SECRET=your-super-secret-jwt-key
|
||||||
python scripts/generate_jwt_secret.py
|
python scripts/generate_jwt_secret.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cache Configuration
|
|
||||||
|
|
||||||
Honcho supports Redis caching to improve performance by caching frequently accessed data like peers, sessions, and working representations. Caching also includes lock mechanisms to prevent cache stampede scenarios.
|
|
||||||
|
|
||||||
**Redis Cache Settings:**
|
|
||||||
```bash
|
|
||||||
# Enable/disable Redis caching
|
|
||||||
CACHE_ENABLED=false # Set to true to enable caching
|
|
||||||
|
|
||||||
# Redis connection
|
|
||||||
CACHE_URL=redis://localhost:6379/0?suppress=true
|
|
||||||
|
|
||||||
# Cache namespace and TTL
|
|
||||||
CACHE_NAMESPACE=honcho # Prefix for all cache keys
|
|
||||||
CACHE_DEFAULT_TTL_SECONDS=300 # How long items stay in cache (5 minutes)
|
|
||||||
|
|
||||||
# Lock settings for preventing cache stampede
|
|
||||||
CACHE_DEFAULT_LOCK_TTL_SECONDS=5 # Lock duration when fetching from DB on cache miss
|
|
||||||
```
|
|
||||||
|
|
||||||
**When to Enable Caching:**
|
|
||||||
- High-traffic production environments
|
|
||||||
- Applications with many repeated reads of the same data
|
|
||||||
- When you need to reduce database load
|
|
||||||
|
|
||||||
**Note:** Caching requires a Redis instance. You can run Redis locally with Docker:
|
|
||||||
```bash
|
|
||||||
docker run -d -p 6379:6379 redis:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
## LLM Provider Configuration
|
## LLM Provider Configuration
|
||||||
|
|
||||||
Honcho supports multiple LLM providers for different tasks. API keys are configured in the `[llm]` section, while specific features use their own configuration sections.
|
Honcho supports multiple LLM providers for different tasks. API keys are configured in the `[llm]` section, while specific features use their own configuration sections.
|
||||||
|
|
@ -261,9 +221,6 @@ LLM_OPENAI_COMPATIBLE_BASE_URL=https://your-openai-compatible-endpoint.com
|
||||||
```bash
|
```bash
|
||||||
# Default settings for all LLM calls
|
# Default settings for all LLM calls
|
||||||
LLM_DEFAULT_MAX_TOKENS=2500
|
LLM_DEFAULT_MAX_TOKENS=2500
|
||||||
|
|
||||||
# Embedding provider (used when EMBED_MESSAGES=true)
|
|
||||||
LLM_EMBEDDING_PROVIDER=openai # Options: openai, gemini
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Feature-Specific Model Configuration
|
### Feature-Specific Model Configuration
|
||||||
|
|
@ -271,147 +228,67 @@ LLM_EMBEDDING_PROVIDER=openai # Options: openai, gemini
|
||||||
Different features can use different providers and models:
|
Different features can use different providers and models:
|
||||||
|
|
||||||
**Dialectic API:**
|
**Dialectic API:**
|
||||||
|
|
||||||
The Dialectic API provides theory-of-mind informed responses by integrating long-term facts with current context.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Main dialectic model (default: Anthropic)
|
# Main dialectic model (default: Anthropic)
|
||||||
DIALECTIC_PROVIDER=anthropic
|
DIALECTIC_PROVIDER=anthropic
|
||||||
DIALECTIC_MODEL=claude-sonnet-4-20250514
|
DIALECTIC_MODEL=claude-sonnet-4-20250514
|
||||||
DIALECTIC_MAX_OUTPUT_TOKENS=2500
|
DIALECTIC_MAX_OUTPUT_TOKENS=2500
|
||||||
DIALECTIC_THINKING_BUDGET_TOKENS=1024 # Only used with Anthropic provider
|
DIALECTIC_THINKING_BUDGET_TOKENS=1024
|
||||||
DIALECTIC_CONTEXT_WINDOW_SIZE=100000 # Maximum context window tokens
|
|
||||||
|
|
||||||
# Query generation for dialectic searches
|
# Query generation for dialectic (default: Groq)
|
||||||
DIALECTIC_PERFORM_QUERY_GENERATION=false # Enable query generation for semantic search
|
|
||||||
DIALECTIC_QUERY_GENERATION_PROVIDER=groq
|
DIALECTIC_QUERY_GENERATION_PROVIDER=groq
|
||||||
DIALECTIC_QUERY_GENERATION_MODEL=llama-3.1-8b-instant
|
DIALECTIC_QUERY_GENERATION_MODEL=llama-3.1-8b-instant
|
||||||
|
|
||||||
# Semantic search settings
|
# Semantic search settings
|
||||||
DIALECTIC_SEMANTIC_SEARCH_TOP_K=10 # Number of results to retrieve
|
DIALECTIC_SEMANTIC_SEARCH_TOP_K=10
|
||||||
DIALECTIC_SEMANTIC_SEARCH_MAX_DISTANCE=0.85 # Maximum distance for relevance
|
DIALECTIC_SEMANTIC_SEARCH_MAX_DISTANCE=0.85
|
||||||
```
|
```
|
||||||
|
|
||||||
**Deriver (Theory of Mind):**
|
**Deriver:**
|
||||||
|
|
||||||
The Deriver is a background processing system that extracts facts from messages and builds theory-of-mind representations of peers.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# LLM settings for deriver
|
# Deriver model (default: Google)
|
||||||
DERIVER_PROVIDER=google
|
DERIVER_PROVIDER=google
|
||||||
DERIVER_MODEL=gemini-2.5-flash-lite
|
DERIVER_MODEL=gemini-2.0-flash-lite
|
||||||
DERIVER_MAX_OUTPUT_TOKENS=10000
|
|
||||||
DERIVER_THINKING_BUDGET_TOKENS=1024 # Only used with Anthropic provider
|
|
||||||
DERIVER_MAX_INPUT_TOKENS=23000 # Maximum input tokens for deriver
|
|
||||||
|
|
||||||
# Worker settings
|
# Worker settings
|
||||||
DERIVER_WORKERS=1 # Number of background worker processes
|
DERIVER_WORKERS=1
|
||||||
DERIVER_POLLING_SLEEP_INTERVAL_SECONDS=1.0 # Time between queue checks
|
DERIVER_STALE_SESSION_TIMEOUT_MINUTES=5
|
||||||
DERIVER_STALE_SESSION_TIMEOUT_MINUTES=5 # Timeout for stale sessions
|
DERIVER_POLLING_SLEEP_INTERVAL_SECONDS=1.0
|
||||||
|
|
||||||
# Queue management
|
# Peer card settings
|
||||||
DERIVER_QUEUE_ERROR_RETENTION_SECONDS=2592000 # Keep errored items for 30 days
|
DERIVER_PEER_CARD_PROVIDER=openai
|
||||||
|
DERIVER_PEER_CARD_MODEL=gpt-5-nano-2025-08-07
|
||||||
|
DERIVER_PEER_CARD_MAX_OUTPUT_TOKENS=2000
|
||||||
|
|
||||||
# Working representation settings
|
# Maximum number of observations to store in working representation
|
||||||
DERIVER_WORKING_REPRESENTATION_MAX_OBSERVATIONS=50 # Max observations stored
|
# This is applied to both explicit and deductive observations
|
||||||
DERIVER_REPRESENTATION_BATCH_MAX_TOKENS=4096 # Max tokens per batch
|
DERIVER_WORKING_REPRESENTATION_MAX_OBSERVATIONS=100
|
||||||
```
|
|
||||||
|
|
||||||
**Peer Card:**
|
|
||||||
|
|
||||||
Peer cards are short, structured summaries of peer identity and characteristics.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Enable/disable peer card generation
|
|
||||||
PEER_CARD_ENABLED=true
|
|
||||||
|
|
||||||
# LLM settings for peer card generation
|
|
||||||
PEER_CARD_PROVIDER=openai
|
|
||||||
PEER_CARD_MODEL=gpt-5-nano-2025-08-07
|
|
||||||
PEER_CARD_MAX_OUTPUT_TOKENS=4000 # Includes thinking tokens for GPT-5 models
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Summary Generation:**
|
**Summary Generation:**
|
||||||
|
|
||||||
Session summaries provide compressed context for long conversations. Honcho creates two types: short summaries (frequent) and long summaries (comprehensive).
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Enable/disable summarization
|
# Summary model (default: Google)
|
||||||
SUMMARY_ENABLED=true
|
SUMMARY_PROVIDER=google
|
||||||
|
SUMMARY_MODEL=gemini-1.5-flash-latest
|
||||||
|
SUMMARY_MAX_TOKENS_SHORT=1000
|
||||||
|
SUMMARY_MAX_TOKENS_LONG=2000
|
||||||
|
SUMMARY_THINKING_BUDGET_TOKENS=512
|
||||||
|
|
||||||
# LLM settings for summary generation
|
# Summary frequency
|
||||||
SUMMARY_PROVIDER=openai
|
SUMMARY_MESSAGES_PER_SHORT_SUMMARY=20
|
||||||
SUMMARY_MODEL=gpt-4o-mini-2024-07-18
|
SUMMARY_MESSAGES_PER_LONG_SUMMARY=60
|
||||||
SUMMARY_MAX_TOKENS_SHORT=1000 # Max tokens for short summaries
|
|
||||||
SUMMARY_MAX_TOKENS_LONG=4000 # Max tokens for long summaries
|
|
||||||
SUMMARY_THINKING_BUDGET_TOKENS=512 # Only used with Anthropic provider
|
|
||||||
|
|
||||||
# Summary frequency thresholds
|
|
||||||
SUMMARY_MESSAGES_PER_SHORT_SUMMARY=20 # Create short summary every N messages
|
|
||||||
SUMMARY_MESSAGES_PER_LONG_SUMMARY=60 # Create long summary every N messages
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Default Provider Usage
|
### Default Provider Usage
|
||||||
|
|
||||||
By default, Honcho uses:
|
By default, Honcho uses:
|
||||||
- **Anthropic** (Claude) for dialectic API responses
|
- **Anthropic** for dialectic API responses
|
||||||
- **Groq** for query generation (fast, cost-effective)
|
- **Groq** for query generation
|
||||||
- **Google** (Gemini) for theory of mind derivation
|
- **Google** for deriving theory of mind and summarization
|
||||||
- **OpenAI** (GPT) for peer cards and summarization
|
|
||||||
- **OpenAI** for embeddings (if `EMBED_MESSAGES=true`)
|
- **OpenAI** for embeddings (if `EMBED_MESSAGES=true`)
|
||||||
|
|
||||||
You only need to set the API keys for the providers you plan to use. All providers are configurable per feature.
|
You only need to set the API keys for the providers you plan to use.
|
||||||
|
|
||||||
## Additional Features Configuration
|
|
||||||
|
|
||||||
### Dream Processing
|
|
||||||
|
|
||||||
Dream processing consolidates and refines peer representations during idle periods, similar to how human memory consolidation works during sleep.
|
|
||||||
|
|
||||||
**Dream Settings:**
|
|
||||||
```bash
|
|
||||||
# Enable/disable dream processing
|
|
||||||
DREAM_ENABLED=true
|
|
||||||
|
|
||||||
# Trigger thresholds
|
|
||||||
DREAM_DOCUMENT_THRESHOLD=50 # Minimum documents to trigger a dream
|
|
||||||
DREAM_IDLE_TIMEOUT_MINUTES=60 # Minutes of inactivity before dream can start
|
|
||||||
DREAM_MIN_HOURS_BETWEEN_DREAMS=8 # Minimum hours between dreams for a peer
|
|
||||||
|
|
||||||
# Dream types to enable
|
|
||||||
DREAM_ENABLED_TYPES=["consolidate"] # Currently supported: consolidate
|
|
||||||
|
|
||||||
# LLM settings for dream processing
|
|
||||||
DREAM_PROVIDER=openai
|
|
||||||
DREAM_MODEL=gpt-4o-mini-2024-07-18
|
|
||||||
DREAM_MAX_OUTPUT_TOKENS=2000
|
|
||||||
```
|
|
||||||
|
|
||||||
### Webhook Configuration
|
|
||||||
|
|
||||||
Webhooks allow you to receive real-time notifications when events occur in Honcho (e.g., new messages, session updates).
|
|
||||||
|
|
||||||
**Webhook Settings:**
|
|
||||||
```bash
|
|
||||||
# Webhook secret for signing payloads (optional but recommended)
|
|
||||||
WEBHOOK_SECRET=your-webhook-signing-secret
|
|
||||||
|
|
||||||
# Limit on webhooks per workspace
|
|
||||||
WEBHOOK_MAX_WORKSPACE_LIMIT=10
|
|
||||||
```
|
|
||||||
|
|
||||||
### Metrics Collection
|
|
||||||
|
|
||||||
Enable metrics collection for monitoring Honcho performance and usage.
|
|
||||||
|
|
||||||
**Metrics Settings:**
|
|
||||||
```bash
|
|
||||||
# Enable/disable metrics collection
|
|
||||||
METRICS_ENABLED=false
|
|
||||||
|
|
||||||
# Namespace for metrics (used in metric names)
|
|
||||||
METRICS_NAMESPACE=honcho
|
|
||||||
```
|
|
||||||
|
|
||||||
## Monitoring Configuration
|
## Monitoring Configuration
|
||||||
|
|
||||||
|
|
@ -419,17 +296,13 @@ METRICS_NAMESPACE=honcho
|
||||||
|
|
||||||
**Sentry Settings:**
|
**Sentry Settings:**
|
||||||
```bash
|
```bash
|
||||||
# Enable/disable Sentry error tracking
|
# Enable/disable Sentry
|
||||||
SENTRY_ENABLED=false
|
SENTRY_ENABLED=false
|
||||||
|
|
||||||
# Sentry configuration
|
# Sentry configuration
|
||||||
SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
|
SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
|
||||||
SENTRY_RELEASE=2.4.0 # Optional: track which version errors come from
|
SENTRY_TRACES_SAMPLE_RATE=0.1
|
||||||
SENTRY_ENVIRONMENT=production # Environment name (development, staging, production)
|
SENTRY_PROFILES_SAMPLE_RATE=0.1
|
||||||
|
|
||||||
# Sampling rates (0.0 to 1.0)
|
|
||||||
SENTRY_TRACES_SAMPLE_RATE=0.1 # 10% of transactions tracked
|
|
||||||
SENTRY_PROFILES_SAMPLE_RATE=0.1 # 10% of transactions profiled
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment-Specific Examples
|
## Environment-Specific Examples
|
||||||
|
|
@ -440,7 +313,7 @@ SENTRY_PROFILES_SAMPLE_RATE=0.1 # 10% of transactions profiled
|
||||||
```toml
|
```toml
|
||||||
[app]
|
[app]
|
||||||
LOG_LEVEL = "DEBUG"
|
LOG_LEVEL = "DEBUG"
|
||||||
SESSION_OBSERVERS_LIMIT = 10
|
SESSION_PEERS_LIMIT = 10
|
||||||
EMBED_MESSAGES = false
|
EMBED_MESSAGES = false
|
||||||
|
|
||||||
[db]
|
[db]
|
||||||
|
|
@ -450,40 +323,21 @@ POOL_SIZE = 5
|
||||||
[auth]
|
[auth]
|
||||||
USE_AUTH = false
|
USE_AUTH = false
|
||||||
|
|
||||||
[cache]
|
|
||||||
ENABLED = false
|
|
||||||
|
|
||||||
[dialectic]
|
[dialectic]
|
||||||
PROVIDER = "anthropic"
|
PROVIDER = "anthropic"
|
||||||
MODEL = "claude-sonnet-4-20250514"
|
MODEL = "claude-sonnet-4-20250514"
|
||||||
PERFORM_QUERY_GENERATION = false
|
QUERY_GENERATION_PROVIDER = "groq"
|
||||||
|
QUERY_GENERATION_MODEL = "llama-3.1-8b-instant"
|
||||||
MAX_OUTPUT_TOKENS = 2500
|
MAX_OUTPUT_TOKENS = 2500
|
||||||
|
|
||||||
|
[summary]
|
||||||
|
PROVIDER = "google"
|
||||||
|
MODEL = "gemini-1.5-flash-latest"
|
||||||
|
MAX_TOKENS_SHORT = 1000
|
||||||
|
MAX_TOKENS_LONG = 2000
|
||||||
|
|
||||||
[deriver]
|
[deriver]
|
||||||
WORKERS = 1
|
WORKERS = 1
|
||||||
PROVIDER = "google"
|
|
||||||
MODEL = "gemini-2.5-flash-lite"
|
|
||||||
|
|
||||||
[peer_card]
|
|
||||||
ENABLED = true
|
|
||||||
PROVIDER = "openai"
|
|
||||||
MODEL = "gpt-5-nano-2025-08-07"
|
|
||||||
|
|
||||||
[summary]
|
|
||||||
ENABLED = true
|
|
||||||
PROVIDER = "openai"
|
|
||||||
MODEL = "gpt-4o-mini-2024-07-18"
|
|
||||||
MAX_TOKENS_SHORT = 1000
|
|
||||||
MAX_TOKENS_LONG = 4000
|
|
||||||
|
|
||||||
[dream]
|
|
||||||
ENABLED = true
|
|
||||||
|
|
||||||
[webhook]
|
|
||||||
MAX_WORKSPACE_LIMIT = 10
|
|
||||||
|
|
||||||
[metrics]
|
|
||||||
ENABLED = false
|
|
||||||
|
|
||||||
[sentry]
|
[sentry]
|
||||||
ENABLED = false
|
ENABLED = false
|
||||||
|
|
@ -495,12 +349,7 @@ ENABLED = false
|
||||||
LOG_LEVEL=DEBUG
|
LOG_LEVEL=DEBUG
|
||||||
DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/honcho_dev
|
DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/honcho_dev
|
||||||
AUTH_USE_AUTH=false
|
AUTH_USE_AUTH=false
|
||||||
CACHE_ENABLED=false
|
ANTHROPIC_API_KEY=your-dev-anthropic-key
|
||||||
|
|
||||||
# LLM Provider API Keys
|
|
||||||
LLM_ANTHROPIC_API_KEY=your-dev-anthropic-key
|
|
||||||
LLM_OPENAI_API_KEY=your-dev-openai-key
|
|
||||||
LLM_GEMINI_API_KEY=your-dev-gemini-key
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Production Configuration
|
### Production Configuration
|
||||||
|
|
@ -509,7 +358,7 @@ LLM_GEMINI_API_KEY=your-dev-gemini-key
|
||||||
```toml
|
```toml
|
||||||
[app]
|
[app]
|
||||||
LOG_LEVEL = "WARNING"
|
LOG_LEVEL = "WARNING"
|
||||||
SESSION_OBSERVERS_LIMIT = 10
|
SESSION_PEERS_LIMIT = 10
|
||||||
EMBED_MESSAGES = true
|
EMBED_MESSAGES = true
|
||||||
|
|
||||||
[db]
|
[db]
|
||||||
|
|
@ -520,50 +369,27 @@ MAX_OVERFLOW = 40
|
||||||
[auth]
|
[auth]
|
||||||
USE_AUTH = true
|
USE_AUTH = true
|
||||||
|
|
||||||
[cache]
|
|
||||||
ENABLED = true
|
|
||||||
URL = "redis://redis:6379/0"
|
|
||||||
DEFAULT_TTL_SECONDS = 300
|
|
||||||
|
|
||||||
[dialectic]
|
[dialectic]
|
||||||
PROVIDER = "anthropic"
|
PROVIDER = "anthropic"
|
||||||
MODEL = "claude-sonnet-4-20250514"
|
MODEL = "claude-sonnet-4-20250514"
|
||||||
PERFORM_QUERY_GENERATION = false
|
QUERY_GENERATION_PROVIDER = "groq"
|
||||||
|
QUERY_GENERATION_MODEL = "llama-3.1-8b-instant"
|
||||||
MAX_OUTPUT_TOKENS = 2500
|
MAX_OUTPUT_TOKENS = 2500
|
||||||
|
|
||||||
|
[summary]
|
||||||
|
PROVIDER = "google"
|
||||||
|
MODEL = "gemini-1.5-flash-latest"
|
||||||
|
MAX_TOKENS_SHORT = 1000
|
||||||
|
MAX_TOKENS_LONG = 2000
|
||||||
|
|
||||||
[deriver]
|
[deriver]
|
||||||
WORKERS = 4
|
WORKERS = 4
|
||||||
PROVIDER = "google"
|
PROVIDER = "google"
|
||||||
MODEL = "gemini-2.5-flash-lite"
|
MODEL = "gemini-2.0-flash-lite"
|
||||||
|
|
||||||
[peer_card]
|
|
||||||
ENABLED = true
|
|
||||||
PROVIDER = "openai"
|
|
||||||
MODEL = "gpt-5-nano-2025-08-07"
|
|
||||||
|
|
||||||
[summary]
|
|
||||||
ENABLED = true
|
|
||||||
PROVIDER = "openai"
|
|
||||||
MODEL = "gpt-4o-mini-2024-07-18"
|
|
||||||
MAX_TOKENS_SHORT = 1000
|
|
||||||
MAX_TOKENS_LONG = 4000
|
|
||||||
|
|
||||||
[dream]
|
|
||||||
ENABLED = true
|
|
||||||
PROVIDER = "openai"
|
|
||||||
MODEL = "gpt-4o-mini-2024-07-18"
|
|
||||||
|
|
||||||
[webhook]
|
|
||||||
MAX_WORKSPACE_LIMIT = 10
|
|
||||||
|
|
||||||
[metrics]
|
|
||||||
ENABLED = true
|
|
||||||
|
|
||||||
[sentry]
|
[sentry]
|
||||||
ENABLED = true
|
ENABLED = true
|
||||||
ENVIRONMENT = "production"
|
|
||||||
TRACES_SAMPLE_RATE = 0.1
|
TRACES_SAMPLE_RATE = 0.1
|
||||||
PROFILES_SAMPLE_RATE = 0.1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Environment variables for production:**
|
**Environment variables for production:**
|
||||||
|
|
@ -571,27 +397,11 @@ PROFILES_SAMPLE_RATE = 0.1
|
||||||
# .env.production
|
# .env.production
|
||||||
LOG_LEVEL=WARNING
|
LOG_LEVEL=WARNING
|
||||||
DB_CONNECTION_URI=postgresql+psycopg://honcho_user:secure_password@prod-db:5432/honcho_prod
|
DB_CONNECTION_URI=postgresql+psycopg://honcho_user:secure_password@prod-db:5432/honcho_prod
|
||||||
|
|
||||||
# Authentication
|
|
||||||
AUTH_USE_AUTH=true
|
AUTH_USE_AUTH=true
|
||||||
AUTH_JWT_SECRET=your-super-secret-jwt-key
|
AUTH_JWT_SECRET=your-super-secret-jwt-key
|
||||||
|
ANTHROPIC_API_KEY=your-prod-anthropic-key
|
||||||
# Cache
|
GEMINI_API_KEY=your-prod-gemini-key
|
||||||
CACHE_ENABLED=true
|
|
||||||
CACHE_URL=redis://redis:6379/0
|
|
||||||
|
|
||||||
# LLM Provider API Keys
|
|
||||||
LLM_ANTHROPIC_API_KEY=your-prod-anthropic-key
|
|
||||||
LLM_OPENAI_API_KEY=your-prod-openai-key
|
|
||||||
LLM_GEMINI_API_KEY=your-prod-gemini-key
|
|
||||||
LLM_GROQ_API_KEY=your-prod-groq-key
|
|
||||||
|
|
||||||
# Webhooks
|
|
||||||
WEBHOOK_SECRET=your-webhook-signing-secret
|
|
||||||
|
|
||||||
# Monitoring
|
|
||||||
SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
|
SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
|
||||||
SENTRY_ENVIRONMENT=production
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Migration Management
|
## Migration Management
|
||||||
|
|
|
||||||
|
|
@ -5,31 +5,13 @@ icon: 'handshake'
|
||||||
|
|
||||||
Thank you for your interest in contributing to Honcho! This guide outlines the process for contributing to the project and our development conventions.
|
Thank you for your interest in contributing to Honcho! This guide outlines the process for contributing to the project and our development conventions.
|
||||||
|
|
||||||
## Before you write code
|
|
||||||
|
|
||||||
**Every pull request needs an issue, and that issue needs the `maintainer-approved` label.**
|
|
||||||
|
|
||||||
A pull request that is not linked to an approved issue gets labelled `needs-approved-issue`, with a comment explaining why. You then have 72 hours to link one before it is closed automatically. Reopening costs nothing once the link is in place. This is automated. We do this because an unreviewable backlog helps nobody: a PR against an unapproved issue is work you did that we may not be able to merge, no matter how good it is.
|
|
||||||
|
|
||||||
So, in order:
|
|
||||||
|
|
||||||
1. **Find approved work.** Browse [issues labelled `maintainer-approved`](https://github.com/plastic-labs/honcho/issues?q=is%3Aissue+is%3Aopen+label%3Amaintainer-approved). That label is the queue of things we have agreed should be built. Anything in it is fair game — comment on the issue to claim it.
|
|
||||||
|
|
||||||
2. **Or open an issue and get it approved.** Use the [issue templates](https://github.com/plastic-labs/honcho/issues/new/choose). Maintainers triage and apply the label.
|
|
||||||
|
|
||||||
3. **If you feel strongly about an issue, come to [Discord](https://discord.gg/honcho).** This is the fastest path by a wide margin. Maintainers are more active there than in the issue tracker, and a five-minute conversation about what you want to build usually resolves whether it fits before either side spends real time on it.
|
|
||||||
|
|
||||||
4. **Then open the PR** and link the issue — either `Fixes #123` in the description, or **Development → link an issue** in the sidebar. Both work.
|
|
||||||
|
|
||||||
Small exceptions we will not be pedantic about: fixing a typo, a broken link, or an obviously wrong code sample. Open the PR, explain it in one line, and we will sort out the issue linkage.
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
Before you start contributing, please:
|
Before you start contributing, please:
|
||||||
|
|
||||||
1. **Set up your development environment** - Follow the [Local Development guide](https://github.com/plastic-labs/honcho/blob/main/CONTRIBUTING.md#local-development) in the Honcho repository to get Honcho running locally.
|
1. **Set up your development environment** - Follow the [Local Development guide](https://github.com/plastic-labs/honcho/blob/main/CONTRIBUTING.md#local-development) in the Honcho repository to get Honcho running locally.
|
||||||
|
|
||||||
2. **Join our community** - Feel free to join us in our [Discord](https://discord.gg/honcho) to discuss your changes, get help, or ask questions.
|
2. **Join our community** - Feel free to join us in our [Discord](http://discord.gg/plasticlabs) to discuss your changes, get help, or ask questions.
|
||||||
|
|
||||||
3. **Review existing issues** - Check the [issues tab](https://github.com/plastic-labs/honcho/issues) to see what's already being worked on or to find something to contribute to.
|
3. **Review existing issues** - Check the [issues tab](https://github.com/plastic-labs/honcho/issues) to see what's already being worked on or to find something to contribute to.
|
||||||
|
|
||||||
|
|
@ -112,7 +94,7 @@ git commit -m "docs(readme): update installation instructions"
|
||||||
3. Fill out the pull request template with:
|
3. Fill out the pull request template with:
|
||||||
- A clear description of what changes you've made
|
- A clear description of what changes you've made
|
||||||
- The motivation for the changes
|
- The motivation for the changes
|
||||||
- A link to the approved issue — `Fixes #123` in the description, or **Development → link an issue** in the sidebar. This is required; see [Before you write code](#before-you-write-code).
|
- Any relevant issue numbers (use "Closes #123" to auto-close issues)
|
||||||
- Screenshots or examples if applicable
|
- Screenshots or examples if applicable
|
||||||
|
|
||||||
## Coding Standards
|
## Coding Standards
|
||||||
|
|
@ -146,7 +128,7 @@ git commit -m "docs(readme): update installation instructions"
|
||||||
|
|
||||||
## Review Process
|
## Review Process
|
||||||
|
|
||||||
1. **Automated checks** - Your PR will run through automated checks including tests, linting, and the issue gate
|
1. **Automated checks** - Your PR will run through automated checks including tests and linting
|
||||||
2. **Project maintainer review** - A project maintainer will review your code for:
|
2. **Project maintainer review** - A project maintainer will review your code for:
|
||||||
- Code quality and adherence to standards
|
- Code quality and adherence to standards
|
||||||
- Functionality and correctness
|
- Functionality and correctness
|
||||||
|
|
@ -171,21 +153,17 @@ We welcome various types of contributions:
|
||||||
When reporting bugs or requesting features:
|
When reporting bugs or requesting features:
|
||||||
|
|
||||||
1. Check if the issue already exists
|
1. Check if the issue already exists
|
||||||
2. Use the appropriate [issue template](https://github.com/plastic-labs/honcho/issues/new/choose) (bug, memory/recall quality, feature, integration, or documentation)
|
2. Use the appropriate issue template
|
||||||
3. Provide clear reproduction steps for bugs
|
3. Provide clear reproduction steps for bugs
|
||||||
4. Include relevant environment information (managed vs self-hosted, server version, SDK)
|
4. Include relevant environment information
|
||||||
5. Be specific about expected vs actual behavior
|
5. Be specific about expected vs actual behavior
|
||||||
6. Redact secrets, JWTs, and production user content
|
|
||||||
|
|
||||||
## Questions and Support
|
## Questions and Support
|
||||||
|
|
||||||
- **General questions** - Join our [Discord](https://discord.gg/honcho)
|
- **General questions** - Join our [Discord](http://discord.gg/plasticlabs)
|
||||||
- **Bug reports** - GitHub issues → Bug report template
|
- **Bug reports** - Use GitHub issues
|
||||||
- **Memory / recall quality** - GitHub issues → Memory / recall quality template
|
- **Feature requests** - Use GitHub issues with the feature request template
|
||||||
- **Feature requests** - GitHub issues → Feature request template
|
- **Security issues** - Please email us privately rather than opening a public issue
|
||||||
- **Integrations / plugins / app-store listings** - GitHub issues → Integration request template
|
|
||||||
- **Documentation issues** - GitHub issues → Documentation issue template
|
|
||||||
- **Security issues** - Report **privately** only — see [`SECURITY.md`](https://github.com/plastic-labs/honcho/blob/main/SECURITY.md) (GitHub Private Vulnerability Reporting or email). Do not open a public issue.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,7 @@ OPENAI_API_KEY=your-openai-api-key
|
||||||
ANTHROPIC_API_KEY=your-anthropic-api-key
|
ANTHROPIC_API_KEY=your-anthropic-api-key
|
||||||
|
|
||||||
# Database will be created automatically by Docker
|
# Database will be created automatically by Docker
|
||||||
DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
|
DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/honcho
|
||||||
|
|
||||||
|
|
||||||
# Disable auth for local development
|
# Disable auth for local development
|
||||||
AUTH_USE_AUTH=false
|
AUTH_USE_AUTH=false
|
||||||
|
|
@ -135,21 +134,24 @@ Download from [postgresql.org](https://www.postgresql.org/download/windows/)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --name honcho-db \
|
docker run --name honcho-db \
|
||||||
|
-e POSTGRES_DB=honcho \
|
||||||
-e POSTGRES_USER=postgres \
|
-e POSTGRES_USER=postgres \
|
||||||
-e POSTGRES_PASSWORD=postgres \
|
-e POSTGRES_PASSWORD=postgres \
|
||||||
-p 5432:5432 \
|
-p 5432:5432 \
|
||||||
-d pgvector/pgvector:pg15
|
-d pgvector/pgvector:pg15
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Enable Extensions
|
### 3. Create Database and Enable Extensions
|
||||||
|
|
||||||
Connect to PostgreSQL and enable pgvector:
|
Connect to PostgreSQL and set up the database:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Connect to PostgreSQL
|
# Connect to PostgreSQL
|
||||||
psql -U postgres
|
psql -U postgres
|
||||||
|
|
||||||
# Enable extensions on the default database
|
# Create database and enable extensions
|
||||||
|
CREATE DATABASE honcho;
|
||||||
|
\c honcho
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
CREATE EXTENSION IF NOT EXISTS vector;
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||||
\q
|
\q
|
||||||
|
|
@ -167,7 +169,7 @@ Edit `.env` with your configuration:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Database connection
|
# Database connection
|
||||||
DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/postgres
|
DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/honcho
|
||||||
|
|
||||||
# Optional API keys (required for LLM features)
|
# Optional API keys (required for LLM features)
|
||||||
OPENAI_API_KEY=your-openai-api-key
|
OPENAI_API_KEY=your-openai-api-key
|
||||||
|
|
@ -277,7 +279,7 @@ const client = new Honcho({
|
||||||
- **Explore the API**: Check out the [API Reference](/v2/api-reference/introduction)
|
- **Explore the API**: Check out the [API Reference](/v2/api-reference/introduction)
|
||||||
- **Try the SDKs**: See our [guides](/v2/guides) for examples
|
- **Try the SDKs**: See our [guides](/v2/guides) for examples
|
||||||
- **Configure Honcho**: Visit the [Configuration Guide](./configuration) for detailed settings
|
- **Configure Honcho**: Visit the [Configuration Guide](./configuration) for detailed settings
|
||||||
- **Join the community**: [Discord](https://discord.gg/honcho)
|
- **Join the community**: [Discord](https://discord.gg/plasticlabs)
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|
@ -308,7 +310,7 @@ const client = new Honcho({
|
||||||
### Getting Help
|
### Getting Help
|
||||||
|
|
||||||
- **GitHub Issues**: [Report bugs](https://github.com/plastic-labs/honcho/issues)
|
- **GitHub Issues**: [Report bugs](https://github.com/plastic-labs/honcho/issues)
|
||||||
- **Discord**: [Join our community](https://discord.gg/honcho)
|
- **Discord**: [Join our community](https://discord.gg/plasticlabs)
|
||||||
- **Documentation**: Check the [Configuration Guide](./configuration) for detailed settings
|
- **Documentation**: Check the [Configuration Guide](./configuration) for detailed settings
|
||||||
|
|
||||||
## Production Considerations
|
## Production Considerations
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,17 @@ sidebarTitle: "Architecture"
|
||||||
|
|
||||||
<Note> The goal of this page is to build an intuition for the primitives in Honcho and how they fit together </Note>
|
<Note> The goal of this page is to build an intuition for the primitives in Honcho and how they fit together </Note>
|
||||||
|
|
||||||
Honcho has 2 main components that work together to manage agent identity and context.
|
Honcho has 3 main components that work together to manage agent identity and context.
|
||||||
|
|
||||||
- **The Memory Layer**: The Memory layer for storing interaction history for your agents
|
- **The Storage API**: The Memory layer for storing interaction history for your agents
|
||||||
- **The Reasoning Layer**: The background processing layer that builds representations of users and agents
|
- **The Deriver**: The background processing layer that builds representations of users and agents
|
||||||
|
- **The Dialectic API**: The natural language API for chatting with representations
|
||||||
|
|
||||||
Below we'll deep dive into these different areas, discussing the data
|
Below we'll deep dive into these different areas, discussing the data
|
||||||
primitives, the flow of data through the system, artifacts Honcho produces, and
|
primitives, the flow of data through the system, artifacts Honcho produces, and
|
||||||
how to use them.
|
how to use them.
|
||||||
|
|
||||||
|
|
||||||
## Data Model
|
## Data Model
|
||||||
|
|
||||||
Honcho has a hierarchical data model centered around the entities below.
|
Honcho has a hierarchical data model centered around the entities below.
|
||||||
|
|
@ -35,9 +37,9 @@ Honcho has a hierarchical data model centered around the entities below.
|
||||||
style SM fill:#e8f5e9,stroke:#2e7d32,color:#000
|
style SM fill:#e8f5e9,stroke:#2e7d32,color:#000
|
||||||
```
|
```
|
||||||
|
|
||||||
- A `Workspaces` has `Peers` & `Sessions`
|
There are `Workspaces` at the top that contain `Peers` and `Sessions`. A `Peer`
|
||||||
- A `Peer` can be in multiple `Sessions` and can send `Messages` in a `Session`.
|
can be part of many `Sessions` and a `Session` can have many `Peers`. `Sessions`
|
||||||
- A `Session` can have many `Peers` and stores `Messages` sent by its `Peers`.
|
hold messages that are sent by `Peers`.
|
||||||
|
|
||||||
### <Icon icon="building" /> Workspaces
|
### <Icon icon="building" /> Workspaces
|
||||||
|
|
||||||
|
|
@ -124,38 +126,19 @@ with a single peer and structure the data as messages.
|
||||||
- File uploads (PDFs, text files, JSON documents)
|
- File uploads (PDFs, text files, JSON documents)
|
||||||
|
|
||||||
|
|
||||||
## Reasoning Layer
|
## Deriver
|
||||||
|
|
||||||
The raw data you store in Honcho is useful, but it's not in a format that's most
|
At the core of developing representations of Peers, we have the Deriver. The
|
||||||
useful for an LLM to consume. There may be too many tokens that need to be
|
Deriver refers to a set of processes in Honcho that enqueue new messages sent
|
||||||
compacted, key facts about what happened may be hard to piece together because
|
by peers and reasons over them to extract facts, insights, and context.
|
||||||
they involve messages from across different sessions, etc.
|
|
||||||
|
|
||||||
To solve this problem, Honcho has a reasoning layer that continually processes
|
|
||||||
incoming data to form the most informationally dense and useful representations of `Peers`
|
|
||||||
that we can then expose to agents. Honcho does the following tasks in
|
|
||||||
the reasoning engine.
|
|
||||||
|
|
||||||
- **Fact Derivation**
|
|
||||||
- **Generate Summaries**
|
|
||||||
- **Generate Peer Cards**
|
|
||||||
- **Dreaming**
|
|
||||||
|
|
||||||
|
|
||||||
Honcho will reason about each `Message` it
|
|
||||||
ingests to generate new facts and insights that are spelled out and easy to
|
|
||||||
consume in an LLM prompt.
|
|
||||||
|
|
||||||
We refer to this module of Honcho as the `Deriver`, because it's constantly
|
|
||||||
deriving new insights from messages. The sum total of all these generated
|
|
||||||
insights are what we refer to as a `Representation`, all the data related to who
|
|
||||||
and what a `Peer` is.
|
|
||||||
|
|
||||||
Depending on the configuration of a `Peer` or `Session`, the deriver will behave
|
Depending on the configuration of a `Peer` or `Session`, the deriver will behave
|
||||||
differently and update different representations.
|
differently and update different representations.
|
||||||
|
|
||||||
Facts derived here are used in the Dialectic chat endpoint, get_context
|
Facts derived here are used in the Dialectic chat endpoint to generate
|
||||||
endpoint,
|
context-aware responses that can correctly reference both concrete facts
|
||||||
|
extracted from messages and social insights deduced from facts, tone, and
|
||||||
|
opinion.
|
||||||
|
|
||||||
<Info>
|
<Info>
|
||||||
Deriver tasks are processed in parallel, but tasks affecting the same peer representation will always be processed serially in order of message creation, so as to properly understand their cumulative effect.
|
Deriver tasks are processed in parallel, but tasks affecting the same peer representation will always be processed serially in order of message creation, so as to properly understand their cumulative effect.
|
||||||
|
|
@ -166,7 +149,7 @@ There are two types of tasks that the deriver currently does:
|
||||||
- **Representation Tasks**: Generate/update peer representations
|
- **Representation Tasks**: Generate/update peer representations
|
||||||
- **Summary Tasks**: Generate conversation summaries
|
- **Summary Tasks**: Generate conversation summaries
|
||||||
|
|
||||||
### Local & Global Representations
|
### Peer Representations
|
||||||
|
|
||||||
Peer representations are more of an abstract concept, as they are made up of
|
Peer representations are more of an abstract concept, as they are made up of
|
||||||
various pieces of data stored throughout Honcho. There are however
|
various pieces of data stored throughout Honcho. There are however
|
||||||
|
|
|
||||||
|
|
@ -1,144 +1,14 @@
|
||||||
---
|
---
|
||||||
title: 'Configure Reasoning'
|
title: 'Configuration'
|
||||||
description: 'Customizing how Honcho handles peers, sessions, and messages'
|
description: 'Customizing how Honcho handles peers and sessions'
|
||||||
icon: 'wrench'
|
icon: 'wrench'
|
||||||
---
|
---
|
||||||
|
|
||||||
Honcho's reasoning engine (the "deriver") can be configured at multiple levels to control how it processes messages, generates facts, creates summaries, and builds peer representations.
|
Entities in Honcho can sometimes be configured to change the behavior of the deriver, which is responsible for generating and storing facts, summaries, and user representations.
|
||||||
|
|
||||||
Configuration follows a hierarchy: **message > session > workspace > global defaults**. Settings at lower levels override those at higher levels, giving you fine-grained control over behavior.
|
These configurations can be set at the peer, session, and session-peer level (AKA the state of a peer within a specific session).
|
||||||
|
|
||||||
## Configuration Hierarchy
|
### Peer Configuration
|
||||||
|
|
||||||
Honcho uses a hierarchical configuration system where more specific settings override more general ones:
|
|
||||||
|
|
||||||
1. **Global Defaults**: Built-in system defaults
|
|
||||||
2. **Workspace Configuration**: Settings that apply to all sessions in a workspace
|
|
||||||
3. **Session Configuration**: Settings that apply to all messages in a session
|
|
||||||
4. **Message Configuration**: Settings that apply to a specific message
|
|
||||||
|
|
||||||
<Info>
|
|
||||||
All configuration fields are optional. If not specified, the value is inherited from the next level up in the hierarchy.
|
|
||||||
</Info>
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
### Deriver Configuration
|
|
||||||
|
|
||||||
Controls the core reasoning engine that extracts facts and insights from messages.
|
|
||||||
|
|
||||||
| Field | Type | Description |
|
|
||||||
|-------|------|-------------|
|
|
||||||
| `enabled` | `bool` | Whether to enable deriver functionality. When disabled, no facts or representations are generated. |
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
from honcho import Honcho
|
|
||||||
|
|
||||||
honcho = Honcho()
|
|
||||||
|
|
||||||
# Disable deriver at session level
|
|
||||||
session = honcho.session("private-session", config={
|
|
||||||
"deriver": {"enabled": False}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
```typescript TypeScript
|
|
||||||
import { Honcho } from "@honcho-ai/sdk";
|
|
||||||
|
|
||||||
const honcho = new Honcho({});
|
|
||||||
|
|
||||||
// Disable deriver at session level
|
|
||||||
const session = await honcho.session("private-session", {
|
|
||||||
config: {
|
|
||||||
deriver: { enabled: false }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
### Peer Card Configuration
|
|
||||||
|
|
||||||
Controls how peer cards (concise summaries of what's known about a peer) are generated and used.
|
|
||||||
|
|
||||||
| Field | Type | Description |
|
|
||||||
|-------|------|-------------|
|
|
||||||
| `use` | `bool` | Whether to use peer cards during the deriver process. |
|
|
||||||
| `create` | `bool` | Whether to generate peer cards based on message content. |
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Disable peer card generation but still use existing cards
|
|
||||||
session = honcho.session("my-session", config={
|
|
||||||
"peer_card": {"create": False, "use": True}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
```typescript TypeScript
|
|
||||||
// Disable peer card generation but still use existing cards
|
|
||||||
const session = await honcho.session("my-session", {
|
|
||||||
config: {
|
|
||||||
peer_card: { create: false, use: true }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
### Summary Configuration
|
|
||||||
|
|
||||||
Controls automatic conversation summarization. Available at workspace and session levels only.
|
|
||||||
|
|
||||||
| Field | Type | Description |
|
|
||||||
|-------|------|-------------|
|
|
||||||
| `enabled` | `bool` | Whether to enable summary functionality. |
|
|
||||||
| `messages_per_short_summary` | `int` | Number of messages between short summaries. Must be ≥ 10. |
|
|
||||||
| `messages_per_long_summary` | `int` | Number of messages between long summaries. Must be ≥ 20 and greater than `messages_per_short_summary`. |
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Customize summary frequency
|
|
||||||
session = honcho.session("verbose-session", config={
|
|
||||||
"summary": {
|
|
||||||
"enabled": True,
|
|
||||||
"messages_per_short_summary": 15,
|
|
||||||
"messages_per_long_summary": 45
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
```typescript TypeScript
|
|
||||||
// Customize summary frequency
|
|
||||||
const session = await honcho.session("verbose-session", {
|
|
||||||
config: {
|
|
||||||
summary: {
|
|
||||||
enabled: true,
|
|
||||||
messages_per_short_summary: 15,
|
|
||||||
messages_per_long_summary: 45
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
### Dream Configuration
|
|
||||||
|
|
||||||
Controls the "dreaming" process that consolidates and refines representations. Available at workspace and session levels only.
|
|
||||||
|
|
||||||
| Field | Type | Description |
|
|
||||||
|-------|------|-------------|
|
|
||||||
| `enabled` | `bool` | Whether to enable dream functionality. Automatically disabled if deriver is disabled. |
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Disable dreams for a workspace
|
|
||||||
# (done via API when creating/updating workspace)
|
|
||||||
```
|
|
||||||
```typescript TypeScript
|
|
||||||
// Disable dreams for a workspace
|
|
||||||
// (done via API when creating/updating workspace)
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Peer Configuration
|
|
||||||
|
|
||||||
By default, all peers are "observed" by Honcho. This means that Honcho will derive facts from messages sent by the peer and generate a representation of them. In most cases, this is why you use Honcho! However, sometimes an application requires a peer that should not be observed: for example, an assistant or game NPC that your program will never need to ask questions about.
|
By default, all peers are "observed" by Honcho. This means that Honcho will derive facts from messages sent by the peer and generate a representation of them. In most cases, this is why you use Honcho! However, sometimes an application requires a peer that should not be observed: for example, an assistant or game NPC that your program will never need to ask questions about.
|
||||||
|
|
||||||
|
|
@ -157,7 +27,7 @@ honcho = Honcho()
|
||||||
peer = honcho.peer("my-peer", config={"observe_me": False})
|
peer = honcho.peer("my-peer", config={"observe_me": False})
|
||||||
|
|
||||||
# Change peer's configuration
|
# Change peer's configuration
|
||||||
peer.set_config({"observe_me": True})
|
peer.set_peer_config({"observe_me": True})
|
||||||
|
|
||||||
# Note: creating the same peer again will also replace the configuration
|
# Note: creating the same peer again will also replace the configuration
|
||||||
peer = honcho.peer("my-peer", config={"observe_me": False})
|
peer = honcho.peer("my-peer", config={"observe_me": False})
|
||||||
|
|
@ -173,7 +43,7 @@ import { Honcho } from "@honcho-ai/sdk";
|
||||||
const peer = await honcho.peer("my-peer", { config: { observe_me: false } });
|
const peer = await honcho.peer("my-peer", { config: { observe_me: false } });
|
||||||
|
|
||||||
// Change peer's configuration
|
// Change peer's configuration
|
||||||
await peer.setConfig({ observe_me: true });
|
await peer.setPeerConfig({ observe_me: true });
|
||||||
|
|
||||||
// Note: creating the same peer again will also replace the configuration
|
// Note: creating the same peer again will also replace the configuration
|
||||||
await honcho.peer("my-peer", { config: { observe_me: false } });
|
await honcho.peer("my-peer", { config: { observe_me: false } });
|
||||||
|
|
@ -181,9 +51,9 @@ import { Honcho } from "@honcho-ai/sdk";
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
||||||
## Session Configuration
|
### Session Configuration
|
||||||
|
|
||||||
Sessions support the full configuration schema. You can disable the deriver entirely for a session, customize summary behavior, or adjust peer card settings.
|
By default, all sessions have the deriver enabled, much like peers. You may create a session that escapes the deriver's watchful eye by setting the `deriver_disabled` flag to `true`. You can update the flag by calling `get_or_create` on the session with a new configuration.
|
||||||
|
|
||||||
<CodeGroup>
|
<CodeGroup>
|
||||||
```python Python
|
```python Python
|
||||||
|
|
@ -192,18 +62,8 @@ from honcho import Honcho
|
||||||
# Initialize client
|
# Initialize client
|
||||||
honcho = Honcho()
|
honcho = Honcho()
|
||||||
|
|
||||||
# Create session with deriver disabled
|
# Create session with configuration
|
||||||
session = honcho.session("my-session", config={
|
session = honcho.session("my-session", config={"deriver_disabled": True})
|
||||||
"deriver": {"enabled": False}
|
|
||||||
})
|
|
||||||
|
|
||||||
# Create session with custom summary settings
|
|
||||||
session = honcho.session("detailed-session", config={
|
|
||||||
"summary": {
|
|
||||||
"messages_per_short_summary": 10,
|
|
||||||
"messages_per_long_summary": 30
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
```typescript TypeScript
|
```typescript TypeScript
|
||||||
import { Honcho } from "@honcho-ai/sdk";
|
import { Honcho } from "@honcho-ai/sdk";
|
||||||
|
|
@ -212,73 +72,15 @@ import { Honcho } from "@honcho-ai/sdk";
|
||||||
// Initialize client
|
// Initialize client
|
||||||
const honcho = new Honcho({});
|
const honcho = new Honcho({});
|
||||||
|
|
||||||
// Create session with deriver disabled
|
// Create session with configuration
|
||||||
const session = await honcho.session("my-session", {
|
const session = await honcho.session("my-session", { config: { deriver_disabled: true } });
|
||||||
config: { deriver: { enabled: false } }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create session with custom summary settings
|
|
||||||
const detailedSession = await honcho.session("detailed-session", {
|
|
||||||
config: {
|
|
||||||
summary: {
|
|
||||||
messages_per_short_summary: 10,
|
|
||||||
messages_per_long_summary: 30
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
})();
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
||||||
## Message Configuration
|
### Session-Peer Configuration
|
||||||
|
|
||||||
Individual messages can override session and workspace configuration for fine-grained control. This is useful for excluding specific messages from processing or adjusting behavior on a per-message basis.
|
Configuration at the session-peer level is the most common use case for configuration flags. You will often want to arrange a session such that certain peers observe others in order to form "local representations" of them. There are two flags that can be set at the session-peer level:
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
from honcho import Honcho
|
|
||||||
|
|
||||||
honcho = Honcho()
|
|
||||||
session = honcho.session("my-session")
|
|
||||||
user = honcho.peer("user")
|
|
||||||
|
|
||||||
# Create a message that skips deriver processing
|
|
||||||
session.add_messages([
|
|
||||||
user.message("This message won't be analyzed", config={
|
|
||||||
"deriver": {"enabled": False}
|
|
||||||
})
|
|
||||||
])
|
|
||||||
|
|
||||||
# Create a message with custom peer card settings
|
|
||||||
session.add_messages([
|
|
||||||
user.message("Use existing card but don't update it", config={
|
|
||||||
"peer_card": {"use": True, "create": False}
|
|
||||||
})
|
|
||||||
])
|
|
||||||
```
|
|
||||||
```typescript TypeScript
|
|
||||||
import { Honcho } from "@honcho-ai/sdk";
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const honcho = new Honcho({});
|
|
||||||
const session = await honcho.session("my-session");
|
|
||||||
const user = await honcho.peer("user");
|
|
||||||
|
|
||||||
// Create a message that skips deriver processing
|
|
||||||
await session.addMessages([
|
|
||||||
user.message("This message won't be analyzed", {
|
|
||||||
configuration: { deriver: { enabled: false } }
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
## Session-Peer Configuration
|
|
||||||
|
|
||||||
Configuration at the session-peer level controls how peers observe each other within a specific session. This is the most common use case for enabling "local representations" — where one peer forms a model of another peer based only on what they observe in that session.
|
|
||||||
|
|
||||||
There are two flags that can be set at the session-peer level:
|
|
||||||
|
|
||||||
- `observe_me`: Whether this peer should *be observed* by others in the session. By default, this is `true`. This overrides the peer-level `observe_me` flag.
|
- `observe_me`: Whether this peer should *be observed* by others in the session. By default, this is `true`. This overrides the peer-level `observe_me` flag.
|
||||||
|
|
||||||
|
|
@ -294,7 +96,7 @@ You can dynamically change the configuration of a session-peer by calling `set_p
|
||||||
|
|
||||||
<CodeGroup>
|
<CodeGroup>
|
||||||
```python Python
|
```python Python
|
||||||
from honcho import Honcho, SessionPeerConfig
|
from honcho import Honcho
|
||||||
|
|
||||||
# Initialize client
|
# Initialize client
|
||||||
honcho = Honcho()
|
honcho = Honcho()
|
||||||
|
|
@ -311,11 +113,11 @@ session.add_peers([alice, bob])
|
||||||
|
|
||||||
# Add another peer to the session with a custom configuration
|
# Add another peer to the session with a custom configuration
|
||||||
charlie = honcho.peer("charlie")
|
charlie = honcho.peer("charlie")
|
||||||
session.add_peers([(charlie, SessionPeerConfig(observe_me=False, observe_others=True))])
|
session.add_peers([charlie, {"observe_me": False, "observe_others": True}])
|
||||||
|
|
||||||
# Set session-peer configuration
|
# Set session-peer configuration
|
||||||
session.set_peer_config(alice, SessionPeerConfig(observe_others=True))
|
session.set_peer_config(alice, {"observe_others": True})
|
||||||
session.set_peer_config(bob, SessionPeerConfig(observe_me=False))
|
session.set_peer_config(bob, {"observe_me": False})
|
||||||
|
|
||||||
# Get session-peer configuration
|
# Get session-peer configuration
|
||||||
charlie_config = session.get_peer_config(charlie)
|
charlie_config = session.get_peer_config(charlie)
|
||||||
|
|
@ -340,7 +142,7 @@ import { Honcho } from "@honcho-ai/sdk";
|
||||||
|
|
||||||
// Add another peer to the session with a custom configuration
|
// Add another peer to the session with a custom configuration
|
||||||
const charlie = await honcho.peer("charlie");
|
const charlie = await honcho.peer("charlie");
|
||||||
await session.addPeers([[charlie, { observe_me: false, observe_others: true }]]);
|
await session.addPeers([charlie, { observe_me: false, observe_others: true }]);
|
||||||
|
|
||||||
// Set session-peer configuration
|
// Set session-peer configuration
|
||||||
await session.setPeerConfig(alice, { observe_others: true });
|
await session.setPeerConfig(alice, { observe_others: true });
|
||||||
|
|
@ -352,59 +154,3 @@ import { Honcho } from "@honcho-ai/sdk";
|
||||||
})();
|
})();
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
||||||
### Observation and Peer Join Order
|
|
||||||
|
|
||||||
Reasoning tasks are scheduled at the time a message is created, based on which peers are in the session **at that moment**. Honcho does not retroactively schedule reasoning for peers that join later.
|
|
||||||
|
|
||||||
This means:
|
|
||||||
|
|
||||||
- If Peer C joins a session **after** messages from Peer A and Peer B have already been sent, Peer C will **not** receive reasoning tasks for those earlier messages—even if Peer C has `observe_others` enabled.
|
|
||||||
- Peer C will only begin observing new messages sent after they join the session.
|
|
||||||
- Similarly, if a peer leaves a session, they stop being included as an observer for any messages sent after their departure.
|
|
||||||
|
|
||||||
<Warning>
|
|
||||||
There is no retroactive reasoning. If your application needs an observer peer to reason about prior conversation history, add the peer to the session **before** messages are sent. Alternatively use the .chat() endpoint to include the conversation history in the agent's context, regardless of if they were reasoned against or not
|
|
||||||
</Warning>
|
|
||||||
|
|
||||||
## Full Configuration Schema Reference
|
|
||||||
|
|
||||||
### Workspace & Session Configuration
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"deriver": {
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"peer_card": {
|
|
||||||
"use": true,
|
|
||||||
"create": true
|
|
||||||
},
|
|
||||||
"summary": {
|
|
||||||
"enabled": true,
|
|
||||||
"messages_per_short_summary": 20,
|
|
||||||
"messages_per_long_summary": 60
|
|
||||||
},
|
|
||||||
"dream": {
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Message Configuration
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"deriver": {
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"peer_card": {
|
|
||||||
"use": true,
|
|
||||||
"create": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
Message configuration only supports `deriver` and `peer_card` settings. Summary and dream configurations are session/workspace-level only.
|
|
||||||
</Note>
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
title: 'Features'
|
||||||
|
description: 'Key features and capabilities of Honcho'
|
||||||
|
icon: 'star'
|
||||||
|
---
|
||||||
|
|
||||||
|
This page is a quick overview of the features within Honcho. In-depth
|
||||||
|
guides are available for each feature in the [Spellbooks - Design Patterns](../../guides/overview#design-patterns) section.
|
||||||
|
|
||||||
|
### Local vs Global Representation
|
||||||
|
Peers in Honcho are abstract entities that can represent humans, agents, or NPCs. Honcho has a two-layer approach to forming representations of Peers.
|
||||||
|
- **Global Representation**: Representation owned by a Peer that is constructed from everything the Peer has sent within Honcho.
|
||||||
|
- **Local Representation**: The representation that a Peer forms of other Peers, based on the messages those other Peers have sent (as observed by the Peer forming the representation).
|
||||||
|
- At the Session level, you can configure which Peers are able to observe messages from other Peers in that Session. This determines which Peers form representations of others within the Session.
|
||||||
|
|
||||||
|
### Queue Status
|
||||||
|
To help developers understand when a Peer's representation is fully up to date, Honcho exposes the ability to poll the status of Peer-centric queues that construct representations.
|
||||||
|
- If no Session is specified, the queue status reflects pending work for the Peer's global representation.
|
||||||
|
- If a Session is specified, the queue status reflects pending work for the Peer's working representation in that Session.
|
||||||
|
|
||||||
|
### Search
|
||||||
|
Honcho implements a powerful search endpoint that allows you to search for messages across a workspace, session, or peer with complex [filters](/v2/guides/using-filters).
|
||||||
|
|
||||||
|
The search process combines full-text and semantic search using reciprocal rank fusion. By default, all messages ingested into Honcho have embeddings generated and stored in the database, enabling semantic search -- if this feature is disabled, the search process will only use full-text search.
|
||||||
|
|
||||||
|
Results are returned in the form of a list of Message objects, and you may choose how many results to return. The default is 10 results, with a maximum of 100.
|
||||||
|
|
||||||
|
In the SDK, search is available on `Workspace`, `Session`, and `Peer` objects, and an optional `filters` parameter may be used to apply a narrower search scope such as a time range or developer-defined metadata attached to messages.
|
||||||
|
|
||||||
|
Note that results are not ordered by recency, only relevance. Results can be sorted by timestamp or a filter on the `created_at` field can limit results to recent messages.
|
||||||
|
|
||||||
|
[Look here for examples of how to use search in the SDK](/v2/guides/search).
|
||||||
|
|
||||||
|
### Scoped API Keys
|
||||||
|
Builders can create scoped API keys to control access to different resources within Honcho.
|
||||||
|
- **Workspace-Level Keys**: Access to everything scoped to a Workspace.
|
||||||
|
- **Peer-Level Keys**: Access to everything scoped to a Peer.
|
||||||
|
- **Session-Level Keys**: Access to everything scoped to a Session.
|
||||||
|
|
||||||
|
### Get Context
|
||||||
|
Honcho provides a powerful context retrieval feature that delivers formatted conversation context from sessions, making it easy to integrate with LLMs like OpenAI, Anthropic, and others.
|
||||||
|
- By default, the context includes a blend of summary and messages which covers the entire history of the session.
|
||||||
|
- Summaries are generated automatically at intervals, and recent messages are included based on your specified token budget for the context.
|
||||||
|
- You can set any token limit, and if you prefer, you can disable summaries so that the context consists entirely of the most recent messages up to your chosen limit.
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
---
|
|
||||||
title: Local vs Global Representations
|
|
||||||
description: Model directional relationships between Peers in Honcho
|
|
||||||
icon: location-pin
|
|
||||||
---
|
|
||||||
|
|
||||||
One of the unique affordances of Honcho is that it allows developers to model
|
|
||||||
directional relationships between Peers. What I mean by this is you can model
|
|
||||||
how one `Peer` thinks about another `Peer`.
|
|
||||||
|
|
||||||
There are many use cases where you don't want every agent or human to know
|
|
||||||
everything about another user such as games or multi-agent workflows. To
|
|
||||||
illustrate this, the following examples shows 2 conversations.
|
|
||||||
|
|
||||||
Conversation #1 (With Bob and Alice)
|
|
||||||
```
|
|
||||||
Alice: I had a great breakfast today.
|
|
||||||
Bob: What did you eat?
|
|
||||||
Alice: I had pancakes and eggs and bacon
|
|
||||||
```
|
|
||||||
|
|
||||||
Conversation #2 (With Alice and Charlie)
|
|
||||||
```
|
|
||||||
Alice: I actually didn't eat any breakfast today.
|
|
||||||
Charlie: Oh that's too bad.
|
|
||||||
Alice: But I lied to Bob and told him I did, so back me up if you see them.
|
|
||||||
```
|
|
||||||
|
|
||||||
Alice told Bob a lie in this conversation. If we stored both of these
|
|
||||||
conversations in Honcho with Alice, Bob, and Charlie as `Peers` and let them
|
|
||||||
use Honcho to get insights on each other then Bob would immediately know this
|
|
||||||
deception. For example:
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Bob could run
|
|
||||||
alice.chat("What did Alice eat today?")
|
|
||||||
# Response: Alice did not eat anything today
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
This is a problem. Bob shouldn't be able to know everything about Alice in this
|
|
||||||
situation. So to support these situations we support what we call **Local
|
|
||||||
Representations**.
|
|
||||||
|
|
||||||
By default insights generated for a `Peer` are scoped globally. This means every
|
|
||||||
message sent by that `Peer` in any conversation updates the same representation
|
|
||||||
of that `Peer`. However, we can enable **Local Representations** so Bob can
|
|
||||||
form a representation Alice based only on what they observe Alice do.
|
|
||||||
|
|
||||||
This feature is illustrated in the graphic below:
|
|
||||||
<img src="/images/local-vs-global-reps.png" alt="Peer Representations" />
|
|
||||||
|
|
||||||
We can enable local representation for a `Peer` by setting `observe_others=True`.
|
|
||||||
This is shown in the [Configure
|
|
||||||
Reasoning](/v2/documentation/core-concepts/configuration) page.
|
|
||||||
|
|
||||||
Now if we used Bob's local representation of Alice then Bob would only get
|
|
||||||
insights on what they've seen Alice say to them.
|
|
||||||
|
|
||||||
```python
|
|
||||||
bob.chat(target="alice", query="What did Alice eat today?")
|
|
||||||
# Response: Alice ate pancakes, eggs, and bacon
|
|
||||||
```
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
Local Representations are turned off by default
|
|
||||||
</Note>
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
---
|
|
||||||
title: Queue Status
|
|
||||||
description: Learn how to check the status of the Deriver
|
|
||||||
icon: lines-leaning
|
|
||||||
---
|
|
||||||
|
|
||||||
Whenever `Messages` are stored in Honcho, a background process called the
|
|
||||||
[Deriver](/docs/v2/documentation/core-concepts/architecture#reasoning-layer) is
|
|
||||||
triggered to reason about the conversation and generate insights.
|
|
||||||
|
|
||||||
The Deriver is an asynchronous process and, depending on load may not immediately
|
|
||||||
generated insights for the latest message you've sent. To help with this, Honcho
|
|
||||||
provides several utilities to check the status of the Deriver.
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
from honcho import Honcho
|
|
||||||
honcho = Honcho()
|
|
||||||
|
|
||||||
status = honcho.get_deriver_status()
|
|
||||||
honcho.poll_deriver_status()
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript typescript
|
|
||||||
import { Honcho } from '@honcho-ai/sdk';
|
|
||||||
|
|
||||||
const honcho = new Honcho({});
|
|
||||||
|
|
||||||
const status = await honcho.getDeriverStatus();
|
|
||||||
await honcho.pollDeriverStatus();
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
Output types
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
class DeriverStatus(BaseModel):
|
|
||||||
completed_work_units: int
|
|
||||||
"""Completed work units"""
|
|
||||||
|
|
||||||
in_progress_work_units: int
|
|
||||||
"""Work units currently being processed"""
|
|
||||||
|
|
||||||
pending_work_units: int
|
|
||||||
"""Work units waiting to be processed"""
|
|
||||||
|
|
||||||
total_work_units: int
|
|
||||||
"""Total work units"""
|
|
||||||
|
|
||||||
sessions: Optional[Dict[str, Sessions]] = None
|
|
||||||
"""Per-session status when not filtered by session"""
|
|
||||||
```
|
|
||||||
```typescript TypeScript
|
|
||||||
Promise<{
|
|
||||||
totalWorkUnits: number
|
|
||||||
completedWorkUnits: number
|
|
||||||
inProgressWorkUnits: number
|
|
||||||
pendingWorkUnits: number
|
|
||||||
sessions?: Record<string, DeriverStatus.Sessions>
|
|
||||||
}>
|
|
||||||
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
Whenever a `Message` is sent it will generate several tasks. These could
|
|
||||||
be tasks such as generating insights, cleaning up a representation, summarizing
|
|
||||||
a conversation etc. These tasks are defined based on who is sending the
|
|
||||||
message, what `Session` the message is in, and potentially who is observing the
|
|
||||||
message. We call the combination of these parameters a `work_unit`
|
|
||||||
|
|
||||||
This has a few different implications.
|
|
||||||
|
|
||||||
- tasks within the same work_unit are processed sequentially, but multiple
|
|
||||||
work_units will be processed in parallel
|
|
||||||
- If local representations are turned in a Session then a `Message` will
|
|
||||||
generate an additional work unit for every `Peer` that has `observe_others=True`
|
|
||||||
|
|
||||||
The `get_deriver_status` and `poll_deriver_status` methods can take additional
|
|
||||||
parameters to scope the status to a specific work unit
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
def get_deriver_status(
|
|
||||||
self,
|
|
||||||
observer_id: str | None = None,
|
|
||||||
sender_id: str | None = None,
|
|
||||||
session_id: str | None = None,
|
|
||||||
) -> DeriverStatus:
|
|
||||||
```
|
|
||||||
```typescript TypeScript
|
|
||||||
|
|
||||||
export const DeriverStatusOptionsSchema = z.object({
|
|
||||||
observerId: z.string().optional(),
|
|
||||||
senderId: z.string().optional(),
|
|
||||||
sessionId: z.string().optional(),
|
|
||||||
timeoutMs: z
|
|
||||||
.number()
|
|
||||||
.positive('Timeout must be a positive number')
|
|
||||||
.optional(),
|
|
||||||
})
|
|
||||||
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
Additionally, there are deriver status and polling deriver status methods
|
|
||||||
available on the `Session` objects in each of the SDKs.
|
|
||||||
|
|
||||||
Below are the function signatures for the session level deriver status method
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python python
|
|
||||||
@validate_call
|
|
||||||
def get_deriver_status(
|
|
||||||
self,
|
|
||||||
observer_id: str | None = None,
|
|
||||||
sender_id: str | None = None,
|
|
||||||
) -> DeriverStatus:
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript TypeScript
|
|
||||||
async getDeriverStatus(
|
|
||||||
options?: Omit<DeriverStatusOptions, 'sessionId'>
|
|
||||||
): Promise<{
|
|
||||||
totalWorkUnits: number
|
|
||||||
completedWorkUnits: number
|
|
||||||
inProgressWorkUnits: number
|
|
||||||
pendingWorkUnits: number
|
|
||||||
sessions?: Record<string, DeriverStatus.Sessions>
|
|
||||||
}>
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
---
|
|
||||||
title: Storing Data
|
|
||||||
description: "Store Data in Honcho to Generate Memories and Insights"
|
|
||||||
icon: "memory"
|
|
||||||
---
|
|
||||||
|
|
||||||
The most basic building block of Honcho's data model is the `Message` object.
|
|
||||||
A `Message` is sent by a `Peer` and saved in a `Session`
|
|
||||||
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
from honcho import Honcho
|
|
||||||
|
|
||||||
honcho = Honcho()
|
|
||||||
|
|
||||||
peer = honcho.peer("sample-peer")
|
|
||||||
|
|
||||||
session = honcho.session("sample-session")
|
|
||||||
|
|
||||||
message = peer.message("Hello, world!")
|
|
||||||
|
|
||||||
session.add_messages([message])
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript TypeScript
|
|
||||||
import { Honcho } from '@honcho-ai/sdk';
|
|
||||||
|
|
||||||
const honcho = new Honcho({});
|
|
||||||
|
|
||||||
const peer = await honcho.peer('sample-peer');
|
|
||||||
|
|
||||||
const session = await honcho.session('sample-session');
|
|
||||||
|
|
||||||
const message = peer.message('Hello, world!');
|
|
||||||
|
|
||||||
await session.addMessages([message]);
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
Once a `Message` is saved in Honcho, it will kick off a background task that
|
|
||||||
looks at the new data to generate insights about the `Peer` that sent the `Message`
|
|
||||||
|
|
||||||
This is the default behavior of Honcho and can be turned off by [configuring the
|
|
||||||
Peer or Session](/v2/documentation/core-concepts/configuration)
|
|
||||||
|
|
||||||
This pattern of having a Peer, Session, and Messages is highly flexible and
|
|
||||||
works for many different use cases and agent setups. Some use cases may only
|
|
||||||
need a single Peer, but many Sessions. Others will only use a single `Session`
|
|
||||||
for their entire app. These are flexible components that work in any situation.
|
|
||||||
|
|
||||||
## Chat Bots
|
|
||||||
|
|
||||||
A common use case for Honcho to is to build a chatbot like ChatGPT or Claude.
|
|
||||||
In this case you can simply
|
|
||||||
|
|
||||||
- Make a `Peer` for the User
|
|
||||||
- Make a `Peer` for the AI
|
|
||||||
|
|
||||||
Then you can make a `Session` for each thread of conversation and save
|
|
||||||
`Messages` from the user and assistant in each turn of conversation
|
|
||||||
|
|
@ -5,102 +5,108 @@ icon: "brain"
|
||||||
sidebarTitle: "Overview"
|
sidebarTitle: "Overview"
|
||||||
---
|
---
|
||||||
|
|
||||||
Honcho is an AI-native memory library for building agents with
|
When building agents developers often run into the same walls:
|
||||||
[state-of-the-art](https://blog.plasticlabs.ai/research/Introducing-Neuromancer-XR)
|
|
||||||
long-term memory.
|
|
||||||
|
|
||||||
Agents using Honcho have perfect recall with a wide variety of tools to traverse
|
> "My agent forgets everything between chats"
|
||||||
their history and get the exact context they need when they need it.
|
|
||||||
|
|
||||||
It then goes beyond basic memory by reasoning about the stored history
|
You need memory: session management, message storage, context handling. It's table stakes, but surprisingly complex to get right.
|
||||||
to expand the latent information available to your agent. Agents using Honcho
|
|
||||||
will understand who they are, who they are interacting with, what happened, and
|
|
||||||
when it happened — all without you having to think about it.
|
|
||||||
|
|
||||||
Use it to build
|
> "My agent treats everyone exactly the same"
|
||||||
|
|
||||||
- Highly personalized experiences
|
You need personalization: user modeling, preference learning, behavioral adaptation. Now you're building a [social cognition](../core-concepts/glossary#social-cognition) engine.
|
||||||
- Agents with social cognition
|
|
||||||
- Agents with rich identity that evolve over time
|
|
||||||
- Multi-agent systems with complex social dynamics
|
|
||||||
|
|
||||||
|
> "I'm writing infrastructure instead of features"
|
||||||
|
|
||||||
|
You need Honcho
|
||||||
|
|
||||||
|
<img src="/images/agent_hierarchy.png" alt="Honcho's Hiearchy of Agents" />
|
||||||
|
|
||||||
|
Honcho delivers production-ready memory infrastructure from day one. Store
|
||||||
|
conversations, manage sessions, get perfectly formatted context for any LLM.
|
||||||
|
But here's the magic: while your agents are chatting, Honcho is learning. It
|
||||||
|
builds Theory of Mind models automatically, transforming raw conversations into
|
||||||
|
rich psychological understanding.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Start simple by just adding messages
|
# Start simple - just add messages
|
||||||
session.add_messages([alice.message("I learn best with examples")])
|
session.add_messages([alice.message("I learn best with examples")])
|
||||||
|
|
||||||
# Honcho will automatically reason about the message to generate insights about Alice
|
# Get powerful - query user psychology
|
||||||
|
|
||||||
# Get insights by chatting with the agent
|
|
||||||
insight = peer.chat("How should I explain this concept?")
|
insight = peer.chat("How should I explain this concept?")
|
||||||
# > "This user learns best through concrete examples..."
|
# > "This user learns best through concrete examples..."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Your agents evolve from goldfish to counselor, on the same infrastructure. That's Honcho.
|
||||||
|
|
||||||
Designed for developers and agents alike:
|
Designed for developers and agents alike:
|
||||||
- **Natural Language Queries**: Chat with Honcho in natural language via the [Dialectic API](../core-concepts/architecture#dialectic-api) to get insights about your users and agents
|
- **Natural Language Queries**: Chat with Honcho in natural language via the [Dialectic API](../core-concepts/architecture#dialectic-api) and let agents backchannel
|
||||||
- **Automatic Context Management**: Smart conversation summaries to have infinite chats
|
- **Automatic Context Management**: Smart summarization that respects token limits
|
||||||
- **Native multi-agent support**: Sessions can natively have as many participants as you need
|
- **Native multi-agent support**: Break out of User/Assistant Paradigms and build complex multi-agent systems
|
||||||
- **Agent-first interfaces**: MCP connections and APIs designed for agents to consume and use as tools
|
- **Agent-first interfaces**: MCP connections and APIs designed for agents to consume and use as tools
|
||||||
- **Provider Agnostic**: Works with any LLM or Agent Framework
|
- **Provider Agnostic**: Works with any LLM or Agent Framework
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
<Accordion title="High Level Diagram" defaultOpen="true">
|
### Storage
|
||||||
<Frame>
|
|
||||||
<img src="/images/overview/honcho-overview.svg" alt="High Level Honcho Diagram" />
|
|
||||||
</Frame>
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
At a high level Honcho works very simply:
|
Developers use Honcho to store information about their users and application via
|
||||||
|
two integrated layers:
|
||||||
|
|
||||||
1. Store messages sent by users and agents in Honcho
|
<img src="/images/basic_honcho_flowchart.png" alt="Basic Honcho Flowchart" />
|
||||||
2. Honcho reasons about the messages to generate insights about each entity in
|
|
||||||
the system
|
|
||||||
3. At runtime your agents can leverage insights from Honcho to get the exact
|
|
||||||
context they need
|
|
||||||
|
|
||||||
There are several API endpoints to leverage the memory & insights in Honcho.
|
**Memory Layer**: Captures all user interactions - messages, preferences, and
|
||||||
|
behavioral patterns - in a peer-centric data model that scales from individual
|
||||||
|
conversations to complex multi-agent scenarios. This also queues up messages for
|
||||||
|
the reasoning layer to process.
|
||||||
|
|
||||||
### Get Context
|
**Reasoning Layer**: Continuously analyzes stored interactions to build
|
||||||
|
psychological profiles using [theory of mind](../core-concepts/glossary#theory-of-mind)
|
||||||
|
inference, extracting patterns about communication style, decision-making
|
||||||
|
preferences, and mental models.
|
||||||
|
|
||||||
This is the easiest way to leverage Honcho. simply call get context and get the
|
### Retrieval
|
||||||
most relevant information for your conversation. This endpoint is highly
|
|
||||||
customizable so you can specify parameters such as:
|
|
||||||
|
|
||||||
- A number of tokens you want
|
Once data is stored and generated within Honcho, the API exposes several
|
||||||
- An option to include summaries of the conversation
|
different ways to retrieve and use those insights.
|
||||||
- An option to get a profile of a specific user (Peer Card & Representation)
|
|
||||||
|
|
||||||
### Search
|
**[Dialectic API](/v2/guides/dialectic-endpoint)**: This is the
|
||||||
|
flagship endpoint that allows developers to send natural language queries to
|
||||||
|
Honcho to chat with the representation of each user in your system to get
|
||||||
|
dynamic, in-context actionable insights.
|
||||||
|
|
||||||
This endpoint lets you search across Honcho for relevant messages using a
|
Example Queries
|
||||||
hybrid search strategy that combines full-text and semantic search.
|
|
||||||
|
|
||||||
You can optionally scope the endpoint to a specific workspace, peer, or session.
|
|
||||||
|
|
||||||
### Working Representation
|
|
||||||
|
|
||||||
This endpoint gives you a snapshot of a user or what we call a
|
|
||||||
**Representation**. Essentially, a list of explicit and deductive facts about
|
|
||||||
the user that are relevant to the current conversation.
|
|
||||||
|
|
||||||
Plug this into your prompt to get a quick overview of the user.
|
|
||||||
|
|
||||||
### Dialectic API
|
|
||||||
|
|
||||||
This endpoint lets you chat with Honcho about any entity in your system. Honcho
|
|
||||||
will leverage what it has remembered and learned about the entity to provide in-context actionable insights.
|
|
||||||
|
|
||||||
This is especially helpful when you want your agent to back-channel with Honcho to
|
|
||||||
change its behavior at runtime.
|
|
||||||
|
|
||||||
Example Queries:
|
|
||||||
- "What's the best way to explain technical concepts to this user?"
|
- "What's the best way to explain technical concepts to this user?"
|
||||||
- "Is this user more task-oriented or relationship-oriented?"
|
- "Is this user more task-oriented or relationship-oriented?"
|
||||||
- "What time of day is this user most engaged?"
|
- "What time of day is this user most engaged?"
|
||||||
- "How does this user prefer to receive feedback?"
|
- "How does this user prefer to receive feedback?"
|
||||||
- "What are this user's core values based on our conversations?"
|
- "What are this user's core values based on our conversations?"
|
||||||
|
|
||||||
|
**[Get Context](/v2/guides/get-context)**: This endpoint abstracts context window
|
||||||
|
constraints and continuously retrieves the most relevant and recent data from a
|
||||||
|
conversation. Provide a token budget and Honcho will return a combination of
|
||||||
|
summaries and messages that provide session context. Use this for creating
|
||||||
|
long-running conversations. We crafted our summaries to provide the most
|
||||||
|
[coverage of a session possible](../core-concepts/summarizer).
|
||||||
|
|
||||||
|
**[Search](/v2/guides/search)**: This endpoint allows you to search across Honcho
|
||||||
|
for relevant messages either at the workspace, peer, or session level. This
|
||||||
|
endpoint uses a hybrid search strategy that combines text search and cosine
|
||||||
|
similarity.
|
||||||
|
|
||||||
|
**[Working Representations](/v2/guides/working-rep)**: Get a cached, snapshot
|
||||||
|
of a user in the context of a session. Instead of waiting for an LLM to
|
||||||
|
synthesize an in-context response via the Dialectic endpoint, use this to get
|
||||||
|
recent insights you can plug into your context window.
|
||||||
|
|
||||||
|
## Ideal For
|
||||||
|
|
||||||
|
**Personalized AI assistants** that need to understand individual psychology, not just remember conversations.
|
||||||
|
|
||||||
|
**Customer-facing agents** that must adapt their approach based on user communication preferences and emotional context.
|
||||||
|
|
||||||
|
**Multi-agent systems** where AI needs to understand human collaborators' working styles and decision-making patterns.
|
||||||
|
|
||||||
|
**NPCs** where you want autonomous agents with a rich and deep personality that isn't the average sycophantic llm
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
|
@ -109,11 +115,11 @@ Ready to integrate Honcho into your application?
|
||||||
<CardGroup cols={2}> <Card title="Quickstart Guide" icon="rocket"
|
<CardGroup cols={2}> <Card title="Quickstart Guide" icon="rocket"
|
||||||
href="/v2/documentation/introduction/quickstart"> Get up and running with
|
href="/v2/documentation/introduction/quickstart"> Get up and running with
|
||||||
Honcho in minutes </Card> <Card title="Core Concepts" icon="brain"
|
Honcho in minutes </Card> <Card title="Core Concepts" icon="brain"
|
||||||
href="/v2/documentation/core-concepts/architecture"> Understand Honcho's
|
href="/v2/documentation/core-concepts/glossary"> Understand Honcho's
|
||||||
fundamental concepts </Card> </CardGroup>
|
fundamental concepts </Card> </CardGroup>
|
||||||
|
|
||||||
## Community & Support
|
## Community & Support
|
||||||
|
|
||||||
- **GitHub**: [plastic-labs/honcho](https://github.com/plastic-labs/honcho)
|
- **GitHub**: [plastic-labs/honcho](https://github.com/plastic-labs/honcho)
|
||||||
- **Discord**: [Join our community](http://discord.gg/honcho)
|
- **Discord**: [Join our community](http://discord.gg/plasticlabs)
|
||||||
- **Issues**: Report bugs and request features on GitHub
|
- **Issues**: Report bugs and request features on GitHub
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ The Honcho client is the main entry point for interacting with Honcho's API. By
|
||||||
from honcho import Honcho
|
from honcho import Honcho
|
||||||
|
|
||||||
# Initialize client (uses demo environment and default workspace)
|
# Initialize client (uses demo environment and default workspace)
|
||||||
honcho = Honcho()
|
client = Honcho()
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ honcho = Honcho()
|
||||||
import { Honcho } from '@honcho-ai/sdk';
|
import { Honcho } from '@honcho-ai/sdk';
|
||||||
|
|
||||||
// Initialize client (uses demo environment and default workspace)
|
// Initialize client (uses demo environment and default workspace)
|
||||||
const honcho = new Honcho({});
|
const client = new Honcho({});
|
||||||
|
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
@ -83,7 +83,7 @@ import os
|
||||||
from honcho import Honcho
|
from honcho import Honcho
|
||||||
|
|
||||||
# Production environment with API key
|
# Production environment with API key
|
||||||
honcho = Honcho(
|
client = Honcho(
|
||||||
api_key=os.environ["HONCHO_API_KEY"],
|
api_key=os.environ["HONCHO_API_KEY"],
|
||||||
environment="production",
|
environment="production",
|
||||||
# Create a workspace, otherwise set to "default"
|
# Create a workspace, otherwise set to "default"
|
||||||
|
|
@ -95,7 +95,7 @@ honcho = Honcho(
|
||||||
import { Honcho } from '@honcho-ai/sdk';
|
import { Honcho } from '@honcho-ai/sdk';
|
||||||
|
|
||||||
// Production environment with API key
|
// Production environment with API key
|
||||||
const honcho = new Honcho({
|
const client = new Honcho({
|
||||||
apiKey: process.env.HONCHO_API_KEY!,
|
apiKey: process.env.HONCHO_API_KEY!,
|
||||||
environment: "production",
|
environment: "production",
|
||||||
// Create a workspace, otherwise set to "default"
|
// Create a workspace, otherwise set to "default"
|
||||||
|
|
@ -110,13 +110,13 @@ Peers represent individual users, AI agents, or any conversational entity in you
|
||||||
|
|
||||||
<CodeGroup>
|
<CodeGroup>
|
||||||
```python Python
|
```python Python
|
||||||
alice = honcho.peer("alice")
|
alice = client.peer("alice")
|
||||||
bob = honcho.peer("bob")
|
bob = client.peer("bob")
|
||||||
```
|
```
|
||||||
|
|
||||||
```typescript TypeScript
|
```typescript TypeScript
|
||||||
const alice = await honcho.peer("alice")
|
const alice = await client.peer("alice")
|
||||||
const bob = await honcho.peer("bob")
|
const bob = await client.peer("bob")
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
||||||
|
|
@ -126,12 +126,12 @@ Sessions are independent conversations that can include multiple peers:
|
||||||
|
|
||||||
<CodeGroup>
|
<CodeGroup>
|
||||||
```python Python
|
```python Python
|
||||||
session = honcho.session("session_1")
|
session = client.session("session_1")
|
||||||
session.add_peers([alice, bob])
|
session.add_peers([alice, bob])
|
||||||
```
|
```
|
||||||
|
|
||||||
```typescript TypeScript
|
```typescript TypeScript
|
||||||
const session = await honcho.session("session_1")
|
const session = await client.session("session_1")
|
||||||
await session.addPeers([alice, bob])
|
await session.addPeers([alice, bob])
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
@ -171,7 +171,7 @@ Now ask Honcho what it's learned - this is where the magic happens:
|
||||||
<CodeGroup>
|
<CodeGroup>
|
||||||
```python Python
|
```python Python
|
||||||
# Ask what Bob is like
|
# Ask what Bob is like
|
||||||
response = bob.chat("Tell me about Bob's interests and habits")
|
response = alice.chat("Tell me about Bob's interests and habits")
|
||||||
print(response)
|
print(response)
|
||||||
|
|
||||||
# Returns rich context like:
|
# Returns rich context like:
|
||||||
|
|
@ -182,128 +182,36 @@ print(response)
|
||||||
```
|
```
|
||||||
|
|
||||||
```typescript TypeScript
|
```typescript TypeScript
|
||||||
bob.chat("Tell me about Bob's interests and habits").then((response) => {
|
(async () => {
|
||||||
|
// Ask what Bob is like
|
||||||
|
const response = await alice.chat("Tell me about Bob's interests and habits");
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
|
||||||
// Returns rich context like:
|
// Returns rich context like:
|
||||||
// "Bob is health-conscious and has been working on getting back in shape.
|
// "Bob is health-conscious and has been working on getting back in shape.
|
||||||
// He regularly goes to the gym, particularly in the evenings, and finds
|
// He regularly goes to the gym, particularly in the evenings, and finds
|
||||||
// exercise helps him relax. He's encouraging about fitness and willing
|
// exercise helps him relax. He's encouraging about fitness and willing
|
||||||
// to share advice about workout routines."
|
// to share advice about workout routines."
|
||||||
})
|
})();
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
## 7. Putting it all together
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
import os
|
|
||||||
from honcho import Honcho
|
|
||||||
|
|
||||||
# Create your client
|
|
||||||
honcho = Honcho(
|
|
||||||
api_key=os.environ["HONCHO_API_KEY"],
|
|
||||||
environment="production",
|
|
||||||
# Create a workspace, otherwise set to "default"
|
|
||||||
# workspaceId="your-workspace-id"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get your Peers
|
|
||||||
alice = honcho.peer("alice")
|
|
||||||
bob = honcho.peer("bob")
|
|
||||||
|
|
||||||
# Make a Session and add your Peers
|
|
||||||
session = honcho.session("session_1")
|
|
||||||
session.add_peers([alice, bob])
|
|
||||||
|
|
||||||
# Add messages sent by your Peers
|
|
||||||
session.add_messages([
|
|
||||||
alice.message("Hi Bob, how are you?"),
|
|
||||||
bob.message("I'm good, thank you!"),
|
|
||||||
alice.message("What are you doing today after work?"),
|
|
||||||
bob.message("I'm going to the gym! I've been trying to get back in shape."),
|
|
||||||
alice.message("That's great! I should probably start exercising too."),
|
|
||||||
bob.message("You should! I find that evening workouts help me relax."),
|
|
||||||
])
|
|
||||||
|
|
||||||
# Get insights about your Peers
|
|
||||||
response = bob.chat("Tell me about Bob's interests and habits")
|
|
||||||
print(response)
|
|
||||||
|
|
||||||
# Returns rich context like:
|
|
||||||
# "Bob is health-conscious and has been working on getting back in shape.
|
|
||||||
# He regularly goes to the gym, particularly in the evenings, and finds
|
|
||||||
# exercise helps him relax. He's encouraging about fitness and willing
|
|
||||||
# to share advice about workout routines."
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript TypeScript
|
|
||||||
import { Honcho } from '@honcho-ai/sdk';
|
|
||||||
|
|
||||||
// Create your client
|
|
||||||
const honcho = new Honcho({
|
|
||||||
apiKey: process.env.HONCHO_API_KEY!,
|
|
||||||
environment: "production",
|
|
||||||
// Create a workspace, otherwise set to "default"
|
|
||||||
// workspace: "your-workspace-id"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get your Peers
|
|
||||||
const alice = await honcho.peer("alice")
|
|
||||||
const bob = await honcho.peer("bob")
|
|
||||||
|
|
||||||
// Make a Session and add your peers
|
|
||||||
const session = await honcho.session("session_1")
|
|
||||||
await session.addPeers([alice, bob])
|
|
||||||
|
|
||||||
// Add messages sent by your Peers
|
|
||||||
await session.addMessages([
|
|
||||||
alice.message("Hi Bob, how are you?"),
|
|
||||||
bob.message("I'm good, thank you!"),
|
|
||||||
alice.message("What are you doing today after work?"),
|
|
||||||
bob.message("I'm going to the gym! I've been trying to get back in shape."),
|
|
||||||
alice.message("That's great! I should probably start exercising too."),
|
|
||||||
bob.message("You should! I find that evening workouts help me relax."),
|
|
||||||
])
|
|
||||||
|
|
||||||
// Get insights about your peers
|
|
||||||
bob.chat("Tell me about Bob's interests and habits").then((response) => {
|
|
||||||
console.log(response);
|
|
||||||
// Returns rich context like:
|
|
||||||
// "Bob is health-conscious and has been working on getting back in shape.
|
|
||||||
// He regularly goes to the gym, particularly in the evenings, and finds
|
|
||||||
// exercise helps him relax. He's encouraging about fitness and willing
|
|
||||||
// to share advice about workout routines."
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
||||||
## What Just Happened?
|
## What Just Happened?
|
||||||
|
|
||||||
You just got through building a simple conversation between two people, Alice
|
Honcho automatically built rich psychological profiles from just a few messages:
|
||||||
and Bob. We:
|
|
||||||
|
|
||||||
1. Set up our connection to Honcho.
|
- **Theory of Mind Processing**: Understanding personality, preferences, and patterns
|
||||||
2. Setup who the participants of our conversation are, these are called `Peers`.
|
- **Ambient Learning**: No surveys or explicit training - just natural conversation
|
||||||
3. Made a `Session` and added our `Peers` to it.
|
- **Rich Context**: Far more detailed than simple conversation history
|
||||||
4. Sent messages from our `Peers`
|
|
||||||
5. Chat with Honcho to get insights about one of the `Peers` in the conversation
|
|
||||||
|
|
||||||
As soon as you save a message in Honcho, it will start to reason about it to
|
The response isn't just retrieving stored text - it's synthesizing insights about Bob's personality, habits, and communication style.
|
||||||
pull out insights and develop a profile of the user. This is the default
|
|
||||||
behavior and can be toggled off via [the configuration](/v2/documentation/core-concepts/configuration).
|
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
<CardGroup cols={3}>
|
This covers the core concepts: **peers**, **sessions**, **messages**, and **dialectic queries**.
|
||||||
<Card title="Architecture" icon="rocket"
|
|
||||||
href="/v2/documentation/core-concepts/architecture">
|
- For production use, [sign up for the managed platform](https://app.honcho.dev) or get an [overview here](../reference/platform).
|
||||||
Learn about the data primitives in Honcho and how they work together
|
- For detailed API reference, check out our [SDK documentation](../reference/sdk).
|
||||||
</Card>
|
- For more examples, explore our [guides](../guides/overview).
|
||||||
<Card title="Start Building" icon="brain" href="https://app.honcho.dev">
|
|
||||||
Sign up for Managed Honcho and get started building agents now.
|
---
|
||||||
</Card>
|
|
||||||
<Card title="Guides" icon="book" href="/v2/guides/overview">
|
|
||||||
Check out spellbooks to see different examples apps built with Honcho
|
|
||||||
</Card>
|
|
||||||
</CardGroup>
|
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,7 @@ description: "Universal starter prompt for building with Honcho"
|
||||||
sidebarTitle: 'Vibecoding Setup'
|
sidebarTitle: 'Vibecoding Setup'
|
||||||
---
|
---
|
||||||
|
|
||||||
These docs are designed to be easily consumable for LLMs. Each page has a button
|
Copy this prompt into Cursor, Claude, or any AI coding assistant to start building with Honcho.
|
||||||
the lets you copy the page as Markdown or paste directly into ChatGPT or Claude.
|
|
||||||
|
|
||||||
Additionally, we follow the llms.txt standard. There are both an llms.txt and
|
|
||||||
llms-full.txt available.
|
|
||||||
|
|
||||||
- [llms.txt](/llms.txt)
|
|
||||||
- [llms-full.txt](/llms-full.txt)
|
|
||||||
|
|
||||||
Additionally, we provide a starter prompt to paste into a coding assistant to
|
|
||||||
quickly get started building with Honcho.
|
|
||||||
|
|
||||||
## 🚀 Universal Starter Prompt
|
## 🚀 Universal Starter Prompt
|
||||||
|
|
||||||
|
|
@ -25,10 +15,10 @@ I want to start building with Honcho - a memory and personalization platform for
|
||||||
## Honcho Resources
|
## Honcho Resources
|
||||||
|
|
||||||
**Documentation:**
|
**Documentation:**
|
||||||
- Main docs: https://honcho.dev/docs
|
- Main docs: https://docs.honcho.dev
|
||||||
- API Reference: https://honcho.dev/docs/v2/api-reference/introduction
|
- API Reference: https://docs.honcho.dev/v2/api-reference/introduction
|
||||||
- Quickstart: https://honcho.dev/docs/v2/documentation/introduction/quickstart
|
- Quickstart: https://docs.honcho.dev/v2/documentation/introduction/quickstart
|
||||||
- Architecture: https://honcho.dev/docs/v2/documentation/reference/architecture
|
- Architecture: https://docs.honcho.dev/v2/documentation/reference/architecture
|
||||||
|
|
||||||
**Code & Examples:**
|
**Code & Examples:**
|
||||||
- Core repo: https://github.com/plastic-labs/honcho
|
- Core repo: https://github.com/plastic-labs/honcho
|
||||||
|
|
|
||||||
|
|
@ -422,4 +422,4 @@ Congratulations! You've built a complete personal AI assistant with Honcho that
|
||||||
- [SDK Reference](/v2/documentation/reference/sdk)
|
- [SDK Reference](/v2/documentation/reference/sdk)
|
||||||
- [API Reference](/v2/api-reference/introduction)
|
- [API Reference](/v2/api-reference/introduction)
|
||||||
- [More Examples](/v2/guides/overview)
|
- [More Examples](/v2/guides/overview)
|
||||||
- [Discord Community](http://discord.gg/honcho)
|
- [Discord Community](http://discord.gg/plasticlabs)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
title: "The Honcho Dashboard"
|
title: "Managed Honcho Platform"
|
||||||
icon: "rocket"
|
icon: "rocket"
|
||||||
description: "Build socially intelligent agents without worrying about infrastructure"
|
description: "Build socially intelligent agents without worrying about infrastructure"
|
||||||
sidebarTitle: "Dashboard Overview"
|
sidebarTitle: "Platform Overview"
|
||||||
---
|
---
|
||||||
|
|
||||||
<Card title="Sign up to start using Honcho!" icon="rocket" href="https://app.honcho.dev">
|
<Card title="Sign up to start using Honcho!" icon="rocket" href="https://app.honcho.dev">
|
||||||
|
|
@ -26,32 +26,39 @@ prompting you to create a new one.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
Once you've created an organization, you'll be taken to the welcome dashboard.
|
Once you've created an organization, you'll be taken to the dashboard and see
|
||||||
|
the Welcome page with integration guidance and links to documentation.
|
||||||
|
|
||||||
<Frame>
|
<Frame>
|
||||||
<img src="/images/app-screenshots/get-started-copy.png" alt="Honcho Dashboard Getting Started" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
<img src="/images/app-screenshots/get-started-copy.png" alt="Honcho Dashboard Getting Started" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
||||||
</Frame>
|
</Frame>
|
||||||
|
|
||||||
Each organization has dedicated infrastructure running to isolate your workloads.
|
Each organization has dedicated infrastructure running to isolate your
|
||||||
Once you add a valid payment method under the
|
workloads. Until you activate a subscription under the
|
||||||
[Billing](https://app.honcho.dev/billing) page, your instance will turn on.
|
[Billing](https://app.honcho.dev/billing) page, the infrastructure will remain
|
||||||
|
inactive.
|
||||||
|
|
||||||
## 2. Activate your Honcho instance
|
## 2. Activate your Honcho instance
|
||||||
|
|
||||||
With credits and a payment method on file (managed via the [Billing](https://app.honcho.dev/billing) page), your Honcho instance will be provisioned and ready to use. Monitor your machine status, and check for version upgrades on the [Instance Status](https://app.honcho.dev/status) page.
|
Navigate to the [Billing](https://app.honcho.dev/billing) page to activate your subscription. Your Honcho instance provisions automatically, and you can monitor the deployment on the [Instance Status](https://app.honcho.dev/status) page until all systems show a green check mark.
|
||||||
|
|
||||||
<Frame>
|
<Frame>
|
||||||
<img src="/images/app-screenshots/status-page.png" alt="Instance Status Page" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
<img src="/images/app-screenshots/status-page.png" alt="Instance Status Page" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
||||||
</Frame>
|
</Frame>
|
||||||
|
|
||||||
If there is an upgrade available you will see an indicator like this:
|
You can also upgrade Honcho when new versions are made available directly from the status page.
|
||||||
|
|
||||||
<div style={{ maxWidth: "700px", margin: "0 auto" }}>
|
<div style={{ maxWidth: "700px", margin: "0 auto" }}>
|
||||||
<Frame>
|
<Frame>
|
||||||
<img src="/images/app-screenshots/upgrade-honcho.png" alt="Upgrade Honcho" loading="lazy" decoding="async" fetchpriority="low" style={{ width: "100%", height: "auto" }} />
|
<img src="/images/app-screenshots/upgrade-honcho.png" alt="Upgrade Honcho" loading="lazy" decoding="async" fetchpriority="low" style={{ width: "100%", height: "auto" }} />
|
||||||
</Frame>
|
</Frame>
|
||||||
</div>
|
</div>
|
||||||
Upgrading your machines may take a few minutes and you can monitor progress. If for any reason your machine goes offline, you will see a red status indicator. Navigate to the [Instance Status](https://app.honcho.dev/status) page and try to trigger a refresh of your machines.
|
|
||||||
|
The **Performance** page provides comprehensive monitoring with usage metrics, health analytics, API response times, and endpoint usage across Honcho.
|
||||||
|
|
||||||
|
<Frame>
|
||||||
|
<img src="/images/app-screenshots/performance-analytics.png" alt="Performance Analytics Dashboard" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
||||||
|
</Frame>
|
||||||
|
|
||||||
## 3. Manage API Keys
|
## 3. Manage API Keys
|
||||||
The [API Keys](https://app.honcho.dev/api-keys) page allows you to create and manage authentication tokens for different environments. You can create admin-level keys with full instance access or scope keys to specific `Workspaces`, `Peers`, or `Sessions`.
|
The [API Keys](https://app.honcho.dev/api-keys) page allows you to create and manage authentication tokens for different environments. You can create admin-level keys with full instance access or scope keys to specific `Workspaces`, `Peers`, or `Sessions`.
|
||||||
|
|
@ -61,48 +68,12 @@ The [API Keys](https://app.honcho.dev/api-keys) page allows you to create and ma
|
||||||
</Frame>
|
</Frame>
|
||||||
|
|
||||||
## 4. Test with API Playground
|
## 4. Test with API Playground
|
||||||
The [API Playground](https://app.honcho.dev/playground) provides a developer-friendly interface to quickly iterate and test queries, explore endpoints, and validate your integration directly from your browser—no code required.
|
The [API Playground](https://app.honcho.dev/playground) provides a Postman-like interface to test queries, explore endpoints, and validate your integration. Authenticate with an API key and send requests directly to your Honcho instance with real-time responses and full request/response logging.
|
||||||
|
|
||||||
<Frame>
|
<Frame>
|
||||||
<img src="/images/app-screenshots/api-playground.png" alt="API Playground Interface" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
<img src="/images/app-screenshots/api-playground.png" alt="API Playground Interface" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
||||||
</Frame>
|
</Frame>
|
||||||
|
|
||||||
|
|
||||||
The playground automatically loads all available API endpoints to match your Honcho instance version. Endpoints can be filtered by category at the top of the list.
|
|
||||||
|
|
||||||
If you prefer, you can set up your queries in the UI and copy to cURL for help with building scripts or general terminal use.
|
|
||||||
|
|
||||||
### Step-by-Step:
|
|
||||||
|
|
||||||
**1. Select an Endpoint**: Choose your desired endpoint from the available list.
|
|
||||||
|
|
||||||
**2. Choose Path Parameters**: For fields like Workspace, Session, or Peer, select options from dropdown menus—no need to manually enter IDs.
|
|
||||||
|
|
||||||
**3. Add Request Body Data**: Complete any required fields for POST/PUT requests.
|
|
||||||
|
|
||||||
**4. Execute or Copy Request**: Run it directly or copy as cURL to use elsewhere (just add your API key).
|
|
||||||
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
Build a complete conversation flow without ever leaving the playground:
|
|
||||||
|
|
||||||
1. **Create a Workspace**:
|
|
||||||
select `POST Get or Create Workspace` and type workspace name into the request body
|
|
||||||
→ Returns workspace ID
|
|
||||||
|
|
||||||
2. **Create new Peer(s)**:
|
|
||||||
select `POST Get or Create Peer,` choose your workspace from the dropdown, and type peer name into the request body. (repeat for each peer)
|
|
||||||
→ Returns peer ID
|
|
||||||
|
|
||||||
3. **POST Get or Create Session**
|
|
||||||
`POST /sessions` selecting your workspace from the dropdown then type session name into the request body
|
|
||||||
→ Returns session ID and adds to dropdown
|
|
||||||
|
|
||||||
4. **POST Create Messages for Session**
|
|
||||||
`POST /messages` selecting your workspace & session from the dropdown then type peer id(s) and message content into the request body
|
|
||||||
→ Adds message(s) to the session
|
|
||||||
|
|
||||||
## 5. Workspaces
|
## 5. Workspaces
|
||||||
The [Explore](https://app.honcho.dev/explore) page provides comprehensive `Workspace` management where you can create workspaces and begin exploring the platform. Each `Workspace` serves as a container for organizing your Honcho data.
|
The [Explore](https://app.honcho.dev/explore) page provides comprehensive `Workspace` management where you can create workspaces and begin exploring the platform. Each `Workspace` serves as a container for organizing your Honcho data.
|
||||||
|
|
||||||
|
|
@ -167,27 +138,14 @@ Here you can:
|
||||||
<img src="/images/app-screenshots/get-context.png" alt="Get Context" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
<img src="/images/app-screenshots/get-context.png" alt="Get Context" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
||||||
</Frame>
|
</Frame>
|
||||||
|
|
||||||
## 8. Performance Monitoring & Analytics
|
## 8. Webhooks Integration
|
||||||
|
The [Webhooks](https://app.honcho.dev/webhooks) page enables Webhook creation and management.
|
||||||
The **Performance** page offers comprehensive monitoring tools, including usage metrics, health analytics, API response times, and endpoint usage across Honcho.
|
|
||||||
|
|
||||||
<Frame>
|
|
||||||
<img src="/images/app-screenshots/performance-analytics.png" alt="Performance Analytics Dashboard" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
|
||||||
</Frame>
|
|
||||||
|
|
||||||
## 9. Webhooks Integration
|
|
||||||
|
|
||||||
The [Webhooks](https://app.honcho.dev/webhooks) page allows managing and creation of webhooks for Honcho. React to events in real-time—such as message delivery, session updates, peer state changes, and more—by sending event payloads via HTTP POST requests to your provided endpoints.
|
|
||||||
|
|
||||||
<Frame>
|
<Frame>
|
||||||
<img src="/images/app-screenshots/webhooks-page.png" alt="Webhooks Dashboard" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
<img src="/images/app-screenshots/webhooks-page.png" alt="Webhooks Dashboard" width="1200" height="800" loading="lazy" decoding="async" fetchpriority="low" />
|
||||||
</Frame>
|
</Frame>
|
||||||
<Frame>
|
|
||||||
<img src="/images/app-screenshots/webhooks-create.png" alt="Create New Webhook" width="400" height="267" loading="lazy" decoding="async" fetchpriority="low" />
|
|
||||||
</Frame>
|
|
||||||
|
|
||||||
## 10. Organization Member Access
|
|
||||||
|
|
||||||
|
## 9. Organization Member Access
|
||||||
The [Members](https://app.honcho.dev/members) page provides organization administration to manage your team's access to Honcho with the ability to grant admin permissions.
|
The [Members](https://app.honcho.dev/members) page provides organization administration to manage your team's access to Honcho with the ability to grant admin permissions.
|
||||||
|
|
||||||
<Frame>
|
<Frame>
|
||||||
|
|
@ -206,7 +164,7 @@ Dive into our [API Reference](/v2/api-reference) to explore all available endpoi
|
||||||
<Card title="Sign up to Honcho Platform" icon="rocket" href="https://app.honcho.dev">
|
<Card title="Sign up to Honcho Platform" icon="rocket" href="https://app.honcho.dev">
|
||||||
Get started with managed Honcho instances
|
Get started with managed Honcho instances
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Join our Discord" icon="discord" href="http://discord.gg/honcho">
|
<Card title="Join our Discord" icon="discord" href="http://discord.gg/plasticlabs">
|
||||||
Connect with 1000+ developers building with Honcho
|
Connect with 1000+ developers building with Honcho
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Contribute to Honcho" icon="code" href="/v2/contributing/guidelines">
|
<Card title="Contribute to Honcho" icon="code" href="/v2/contributing/guidelines">
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ response = alice.chat("What does the user know about weather?")
|
||||||
response = alice.chat("What does the user know about the assistant?", target=assistant)
|
response = alice.chat("What does the user know about the assistant?", target=assistant)
|
||||||
|
|
||||||
# Query scoped to a specific session
|
# Query scoped to a specific session
|
||||||
response = alice.chat("What happened in our conversation?", session=session.id)
|
response = alice.chat("What happened in our conversation?", session_id=session.id)
|
||||||
```
|
```
|
||||||
|
|
||||||
```typescript TypeScript
|
```typescript TypeScript
|
||||||
|
|
@ -258,7 +258,7 @@ print(f"Workspace: {alice.workspace_id}")
|
||||||
# Chat with peer's representations (supports streaming)
|
# Chat with peer's representations (supports streaming)
|
||||||
response = alice.chat("What did I have for breakfast?")
|
response = alice.chat("What did I have for breakfast?")
|
||||||
response = alice.chat("What do I know about Bob?", target="bob")
|
response = alice.chat("What do I know about Bob?", target="bob")
|
||||||
response = alice.chat("What happened in session-1?", session="session-1")
|
response = alice.chat("What happened in session-1?", session_id="session-1")
|
||||||
|
|
||||||
# Add content to a session with a peer
|
# Add content to a session with a peer
|
||||||
session = honcho.session("session-1")
|
session = honcho.session("session-1")
|
||||||
|
|
@ -278,17 +278,6 @@ results = alice.search("programming")
|
||||||
metadata = alice.get_metadata()
|
metadata = alice.get_metadata()
|
||||||
metadata["location"] = "Paris"
|
metadata["location"] = "Paris"
|
||||||
alice.set_metadata(metadata)
|
alice.set_metadata(metadata)
|
||||||
|
|
||||||
# Get peer context (representation + peer card in one call)
|
|
||||||
context = alice.get_context()
|
|
||||||
context = alice.get_context(target="bob") # What alice knows about bob
|
|
||||||
|
|
||||||
# Get working representation with semantic search
|
|
||||||
rep = alice.working_rep(search_query="preferences", search_top_k=10)
|
|
||||||
|
|
||||||
# Access observations
|
|
||||||
self_observations = alice.observations.list() # Self-observations
|
|
||||||
bob_observations = alice.observations_of("bob").list() # Observations of bob
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```typescript TypeScript
|
```typescript TypeScript
|
||||||
|
|
@ -329,165 +318,9 @@ await alice.setMetadata({
|
||||||
...metadata,
|
...metadata,
|
||||||
location: "Paris"
|
location: "Paris"
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get peer context (representation + peer card in one call)
|
|
||||||
const context = await alice.getContext();
|
|
||||||
const targetContext = await alice.getContext("bob"); // What alice knows about bob
|
|
||||||
|
|
||||||
// Get working representation with semantic search
|
|
||||||
const rep = await alice.workingRep(undefined, undefined, {
|
|
||||||
searchQuery: "preferences",
|
|
||||||
searchTopK: 10
|
|
||||||
});
|
|
||||||
|
|
||||||
// Access observations
|
|
||||||
const selfObs = await alice.observations.list(); // Self-observations
|
|
||||||
const bobObs = await alice.observationsOf("bob").list(); // Observations of bob
|
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
||||||
### Peer Context
|
|
||||||
|
|
||||||
The `get_context()` method on peers retrieves both the working representation and peer card in a single API call:
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Get peer's own context
|
|
||||||
context = alice.get_context()
|
|
||||||
print(context.representation) # Working representation
|
|
||||||
print(context.peer_card) # Peer card as list of strings
|
|
||||||
|
|
||||||
# Get context about another peer (what alice knows about bob)
|
|
||||||
bob_context = alice.get_context(target="bob")
|
|
||||||
|
|
||||||
# Get context with semantic search
|
|
||||||
context = alice.get_context(
|
|
||||||
target="bob",
|
|
||||||
search_query="work preferences",
|
|
||||||
search_top_k=10,
|
|
||||||
search_max_distance=0.8,
|
|
||||||
include_most_derived=True,
|
|
||||||
max_observations=50
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript TypeScript
|
|
||||||
// Get peer's own context
|
|
||||||
const context = await alice.getContext();
|
|
||||||
console.log(context.representation); // Working representation
|
|
||||||
console.log(context.peerCard); // Peer card as array of strings
|
|
||||||
|
|
||||||
// Get context about another peer (what alice knows about bob)
|
|
||||||
const bobContext = await alice.getContext("bob");
|
|
||||||
|
|
||||||
// Get context with semantic search
|
|
||||||
const searchedContext = await alice.getContext("bob", {
|
|
||||||
searchQuery: "work preferences",
|
|
||||||
searchTopK: 10,
|
|
||||||
searchMaxDistance: 0.8,
|
|
||||||
includeMostDerived: true,
|
|
||||||
maxObservations: 50
|
|
||||||
});
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
### Observations
|
|
||||||
|
|
||||||
Peers can access their observations (facts derived from messages) through the `observations` property and `observations_of()` method:
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Access self-observations (what honcho knows about alice)
|
|
||||||
self_obs = alice.observations
|
|
||||||
|
|
||||||
# List self-observations
|
|
||||||
obs_list = self_obs.list()
|
|
||||||
|
|
||||||
# Search self-observations semantically
|
|
||||||
results = self_obs.query("food preferences")
|
|
||||||
|
|
||||||
# Delete an observation
|
|
||||||
self_obs.delete("observation-id")
|
|
||||||
|
|
||||||
# Access observations of another peer (what alice knows about bob)
|
|
||||||
bob_obs = alice.observations_of("bob")
|
|
||||||
bob_obs_list = bob_obs.list()
|
|
||||||
bob_search = bob_obs.query("work history")
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript TypeScript
|
|
||||||
// Access self-observations (what honcho knows about alice)
|
|
||||||
const selfObs = alice.observations;
|
|
||||||
|
|
||||||
// List self-observations
|
|
||||||
const obsList = await selfObs.list();
|
|
||||||
|
|
||||||
// Search self-observations semantically
|
|
||||||
const results = await selfObs.query("food preferences");
|
|
||||||
|
|
||||||
// Delete an observation
|
|
||||||
await selfObs.delete("observation-id");
|
|
||||||
|
|
||||||
// Access observations of another peer (what alice knows about bob)
|
|
||||||
const bobObs = alice.observationsOf("bob");
|
|
||||||
const bobObsList = await bobObs.list();
|
|
||||||
const bobSearch = await bobObs.query("work history");
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
#### Creating Observations Manually
|
|
||||||
|
|
||||||
You can also create observations directly, which is useful for importing data or adding explicit facts:
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Create observations for what alice knows about bob
|
|
||||||
bob_obs = alice.observations_of("bob")
|
|
||||||
|
|
||||||
# Create a single observation
|
|
||||||
created = bob_obs.create([
|
|
||||||
{"content": "User prefers dark mode", "session_id": "session-1"}
|
|
||||||
])
|
|
||||||
|
|
||||||
# Create multiple observations in batch
|
|
||||||
created = bob_obs.create([
|
|
||||||
{"content": "User prefers dark mode", "session_id": "session-1"},
|
|
||||||
{"content": "User works late at night", "session_id": "session-1"},
|
|
||||||
{"content": "User enjoys programming", "session_id": "session-1"},
|
|
||||||
])
|
|
||||||
|
|
||||||
# Returns list of created Observation objects with IDs
|
|
||||||
for obs in created:
|
|
||||||
print(f"Created observation: {obs.id} - {obs.content}")
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript TypeScript
|
|
||||||
// Create observations for what alice knows about bob
|
|
||||||
const bobObs = alice.observationsOf("bob");
|
|
||||||
|
|
||||||
// Create a single observation
|
|
||||||
const created = await bobObs.create([
|
|
||||||
{ content: "User prefers dark mode", sessionId: "session-1" }
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Create multiple observations in batch
|
|
||||||
const batchCreated = await bobObs.create([
|
|
||||||
{ content: "User prefers dark mode", sessionId: "session-1" },
|
|
||||||
{ content: "User works late at night", sessionId: "session-1" },
|
|
||||||
{ content: "User enjoys programming", sessionId: "session-1" },
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Returns array of created Observation objects with IDs
|
|
||||||
for (const obs of batchCreated) {
|
|
||||||
console.log(`Created observation: ${obs.id} - ${obs.content}`);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
<Info>
|
|
||||||
Manually created observations are marked as "explicit" and are treated the same as system-derived observations. Each observation must be tied to a session and the content length is validated against the embedding token limit.
|
|
||||||
</Info>
|
|
||||||
|
|
||||||
### Session
|
### Session
|
||||||
|
|
||||||
Manages multi-party conversations:
|
Manages multi-party conversations:
|
||||||
|
|
@ -528,50 +361,12 @@ messages = session.get_messages()
|
||||||
# Get conversation context
|
# Get conversation context
|
||||||
context = session.get_context(summary=True, tokens=2000)
|
context = session.get_context(summary=True, tokens=2000)
|
||||||
|
|
||||||
# Get context with peer representation included
|
|
||||||
context = session.get_context(
|
|
||||||
tokens=2000,
|
|
||||||
peer_target="user",
|
|
||||||
peer_perspective="assistant",
|
|
||||||
search_query="What are my preferences?",
|
|
||||||
limit_to_session=True,
|
|
||||||
search_top_k=10,
|
|
||||||
search_max_distance=0.8,
|
|
||||||
include_most_derived=True,
|
|
||||||
max_observations=25
|
|
||||||
)
|
|
||||||
|
|
||||||
# Search session content
|
# Search session content
|
||||||
results = session.search("help")
|
results = session.search("help")
|
||||||
|
|
||||||
# Working representation queries with semantic search
|
# Working representation queries
|
||||||
global_rep = session.working_rep("alice")
|
global_rep = session.working_rep("alice")
|
||||||
targeted_rep = session.working_rep(alice, target=bob)
|
targeted_rep = session.working_rep(alice, bob)
|
||||||
searched_rep = session.working_rep(
|
|
||||||
"alice",
|
|
||||||
search_query="preferences",
|
|
||||||
search_top_k=10,
|
|
||||||
include_most_derived=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Upload a file to create messages
|
|
||||||
messages = session.upload_file(
|
|
||||||
file=open("document.pdf", "rb"),
|
|
||||||
peer="user",
|
|
||||||
metadata={"source": "upload"},
|
|
||||||
created_at="2024-01-15T10:30:00Z"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Clone a session (creates a copy with all data)
|
|
||||||
# Copies: messages, metadata, configuration, peers, and peer configurations
|
|
||||||
cloned = session.clone()
|
|
||||||
|
|
||||||
# Clone up to a specific message (inclusive)
|
|
||||||
# Only messages up to and including the specified message are copied
|
|
||||||
cloned_partial = session.clone(message_id="msg-123")
|
|
||||||
|
|
||||||
# Delete session (async - returns 202)
|
|
||||||
session.delete()
|
|
||||||
|
|
||||||
# Metadata management
|
# Metadata management
|
||||||
session.set_metadata({"topic": "product planning", "status": "active"})
|
session.set_metadata({"topic": "product planning", "status": "active"})
|
||||||
|
|
@ -607,51 +402,12 @@ const messages = await session.getMessages();
|
||||||
// Get conversation context
|
// Get conversation context
|
||||||
const context = await session.getContext({ summary: true, tokens: 2000 });
|
const context = await session.getContext({ summary: true, tokens: 2000 });
|
||||||
|
|
||||||
// Get context with peer representation included
|
|
||||||
const richContext = await session.getContext({
|
|
||||||
tokens: 2000,
|
|
||||||
peerTarget: "user",
|
|
||||||
peerPerspective: "assistant",
|
|
||||||
searchQuery: "What are my preferences?",
|
|
||||||
limitToSession: true,
|
|
||||||
searchTopK: 10,
|
|
||||||
searchMaxDistance: 0.8,
|
|
||||||
includeMostDerived: true,
|
|
||||||
maxObservations: 25
|
|
||||||
});
|
|
||||||
|
|
||||||
// Search session content
|
// Search session content
|
||||||
const results = await session.search("help");
|
const results = await session.search("help");
|
||||||
|
|
||||||
// Working representation queries with semantic search
|
// Working representation queries
|
||||||
const globalRep = await session.workingRep("alice");
|
const globalRep = await session.workingRep("alice");
|
||||||
const targetedRep = await session.workingRep(alice, { target: bob });
|
const targetedRep = await session.workingRep(alice, bob);
|
||||||
const searchedRep = await session.workingRep("alice", undefined, {
|
|
||||||
searchQuery: "preferences",
|
|
||||||
searchTopK: 10,
|
|
||||||
includeMostDerived: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Upload a file to create messages
|
|
||||||
const messages = await session.uploadFile(
|
|
||||||
fileBuffer,
|
|
||||||
"user",
|
|
||||||
{
|
|
||||||
metadata: { source: "upload" },
|
|
||||||
createdAt: "2024-01-15T10:30:00Z"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Clone a session (creates a copy with all data)
|
|
||||||
// Copies: messages, metadata, configuration, peers, and peer configurations
|
|
||||||
const cloned = await session.clone();
|
|
||||||
|
|
||||||
// Clone up to a specific message (inclusive)
|
|
||||||
// Only messages up to and including the specified message are copied
|
|
||||||
const clonedPartial = await session.clone("msg-123");
|
|
||||||
|
|
||||||
// Delete session (async - returns 202)
|
|
||||||
await session.delete();
|
|
||||||
|
|
||||||
// Metadata management
|
// Metadata management
|
||||||
await session.setMetadata({
|
await session.setMetadata({
|
||||||
|
|
@ -738,27 +494,10 @@ The SessionContext object has the following structure:
|
||||||
"message_id": 123,
|
"message_id": 123,
|
||||||
"summary_type": "short|long",
|
"summary_type": "short|long",
|
||||||
"created_at": "2024-01-15T10:30:00Z"
|
"created_at": "2024-01-15T10:30:00Z"
|
||||||
},
|
}
|
||||||
"peer_representation": "string (optional)",
|
|
||||||
"peer_card": ["string"] // optional, included when peer_target is provided
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Session Context Parameters:**
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
|-----------|------|-------------|
|
|
||||||
| `summary` | `bool` | Whether to include summary (default: true) |
|
|
||||||
| `tokens` | `int` | Maximum tokens to include |
|
|
||||||
| `peer_target` | `str` | Peer ID to get representation for |
|
|
||||||
| `peer_perspective` | `str` | Peer ID for perspective (requires peer_target) |
|
|
||||||
| `search_query` | `str` | Query string for semantic search |
|
|
||||||
| `limit_to_session` | `bool` | Limit representation to session only |
|
|
||||||
| `search_top_k` | `int` | Number of semantic search results (1-100) |
|
|
||||||
| `search_max_distance` | `float` | Max semantic distance (0.0-1.0) |
|
|
||||||
| `include_most_derived` | `bool` | Include most derived observations |
|
|
||||||
| `max_observations` | `int` | Max observations to include (1-100) |
|
|
||||||
|
|
||||||
## Advanced Usage
|
## Advanced Usage
|
||||||
|
|
||||||
### Multi-Party Conversations
|
### Multi-Party Conversations
|
||||||
|
|
@ -782,7 +521,7 @@ group_chat.add_messages([
|
||||||
|
|
||||||
# Query different perspectives
|
# Query different perspectives
|
||||||
user_perspective = users[0].chat("What are people's concerns?")
|
user_perspective = users[0].chat("What are people's concerns?")
|
||||||
moderator_view = moderator.chat("What feedback am I getting?", session=group_chat.id)
|
moderator_view = moderator.chat("What feedback am I getting?", session_id=group_chat.id)
|
||||||
```
|
```
|
||||||
|
|
||||||
```typescript TypeScript
|
```typescript TypeScript
|
||||||
|
|
|
||||||
|
|
@ -84,4 +84,4 @@ for await (const line of responseStream.iter_text()) {
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
||||||
We've designed the Dialectic endpoint to be infinitely flexible. We wrote an incomplete list of ideas on how to use it on our blog [here](https://blog.plasticlabs.ai/archive/ARCHIVED;-Introducing-Honcho's-Dialectic-API#how-it-works).
|
We've designed the Dialectic endpoint to be infinitely flexible. We wrote an incomplete list of ideas on how to use it on our blog [here](https://blog.plasticlabs.ai/blog/Introducing-Honcho's-Dialectic-API#how-it-works).
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
title: 'Get Context'
|
title: 'Working with Session Context'
|
||||||
description: 'Learn how to use get_context() to retrieve and format conversation context for LLM integration'
|
description: 'Learn how to use get_context() to retrieve and format conversation context for LLM integration'
|
||||||
icon: 'messages'
|
icon: 'messages'
|
||||||
---
|
---
|
||||||
|
|
@ -93,127 +93,6 @@ context = session.get_context(summary=False, tokens=2000)
|
||||||
```
|
```
|
||||||
</CodeGroup>
|
</CodeGroup>
|
||||||
|
|
||||||
### Peer Representation in Context
|
|
||||||
|
|
||||||
You can include a peer's representation and peer card in the context by specifying `peer_target`. This is useful for providing the LLM with knowledge about a specific peer.
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Get context with peer representation included
|
|
||||||
context = session.get_context(
|
|
||||||
tokens=2000,
|
|
||||||
peer_target="user-123" # Include representation of user-123
|
|
||||||
)
|
|
||||||
|
|
||||||
# Access the representation and peer card
|
|
||||||
print(context.peer_representation) # String representation
|
|
||||||
print(context.peer_card) # List of peer card items
|
|
||||||
|
|
||||||
# Get representation from a specific peer's perspective
|
|
||||||
context = session.get_context(
|
|
||||||
tokens=2000,
|
|
||||||
peer_target="user-123",
|
|
||||||
peer_perspective="assistant" # From assistant's viewpoint
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript TypeScript
|
|
||||||
(async () => {
|
|
||||||
// Get context with peer representation included
|
|
||||||
const context = await session.getContext({
|
|
||||||
tokens: 2000,
|
|
||||||
peerTarget: "user-123" // Include representation of user-123
|
|
||||||
});
|
|
||||||
|
|
||||||
// Access the representation and peer card
|
|
||||||
console.log(context.peerRepresentation); // String representation
|
|
||||||
console.log(context.peerCard); // Array of peer card items
|
|
||||||
|
|
||||||
// Get representation from a specific peer's perspective
|
|
||||||
const perspectiveContext = await session.getContext({
|
|
||||||
tokens: 2000,
|
|
||||||
peerTarget: "user-123",
|
|
||||||
peerPerspective: "assistant" // From assistant's viewpoint
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
### Semantic Search
|
|
||||||
|
|
||||||
Use `search_query` to fetch semantically relevant observations based on a query string:
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Get context with semantic search based on query
|
|
||||||
context = session.get_context(
|
|
||||||
tokens=2000,
|
|
||||||
peer_target="user-123",
|
|
||||||
search_query="What are my account preferences?",
|
|
||||||
search_top_k=10, # Number of relevant observations
|
|
||||||
search_max_distance=0.8, # Max semantic distance (0.0-1.0)
|
|
||||||
include_most_derived=True, # Include most recent observations
|
|
||||||
max_observations=25 # Cap total observations
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript TypeScript
|
|
||||||
(async () => {
|
|
||||||
// Get context with semantic search based on query
|
|
||||||
const context = await session.getContext({
|
|
||||||
tokens: 2000,
|
|
||||||
peerTarget: "user-123",
|
|
||||||
searchQuery: "What are my account preferences?",
|
|
||||||
searchTopK: 10, // Number of relevant observations
|
|
||||||
searchMaxDistance: 0.8, // Max semantic distance (0.0-1.0)
|
|
||||||
includeMostDerived: true, // Include most recent observations
|
|
||||||
maxObservations: 25 // Cap total observations
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
### Session-Scoped Representations
|
|
||||||
|
|
||||||
Use `limit_to_session` to only include observations from the current session:
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```python Python
|
|
||||||
# Get context limited to this session's observations only
|
|
||||||
context = session.get_context(
|
|
||||||
tokens=2000,
|
|
||||||
peer_target="user-123",
|
|
||||||
limit_to_session=True # Only observations from this session
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript TypeScript
|
|
||||||
(async () => {
|
|
||||||
// Get context limited to this session's observations only
|
|
||||||
const context = await session.getContext({
|
|
||||||
tokens: 2000,
|
|
||||||
peerTarget: "user-123",
|
|
||||||
limitToSession: true // Only observations from this session
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
|
|
||||||
### All Parameters Reference
|
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
|
||||||
|-----------|------|-------------|
|
|
||||||
| `summary` | `bool` | Include summary in context (default: true) |
|
|
||||||
| `tokens` | `int` | Maximum tokens to include |
|
|
||||||
| `peer_target` | `str` | Peer ID to include representation for |
|
|
||||||
| `peer_perspective` | `str` | Peer ID for perspective (requires peer_target) |
|
|
||||||
| `search_query` | `str` | Query for semantic search (requires peer_target) |
|
|
||||||
| `limit_to_session` | `bool` | Limit to session observations only |
|
|
||||||
| `search_top_k` | `int` | Semantic search results to include (1-100) |
|
|
||||||
| `search_max_distance` | `float` | Max semantic distance (0.0-1.0) |
|
|
||||||
| `include_most_derived` | `bool` | Include most recently derived observations |
|
|
||||||
| `max_observations` | `int` | Maximum observations to include (1-100) |
|
|
||||||
|
|
||||||
## Converting to LLM Formats
|
## Converting to LLM Formats
|
||||||
|
|
||||||
The `SessionContext` object provides methods to convert the context into formats compatible with popular LLM APIs. When converting to OpenAI format, you must specify the assistant peer to format the context in such a way that the LLM can understand it.
|
The `SessionContext` object provides methods to convert the context into formats compatible with popular LLM APIs. When converting to OpenAI format, you must specify the assistant peer to format the context in such a way that the LLM can understand it.
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
title: "Model Context Protocol (MCP)"
|
title: "Honcho MCP"
|
||||||
icon: 'star-of-life'
|
icon: 'star-of-life'
|
||||||
description: "Use Honcho in Claude Desktop"
|
description: "Use Honcho in Claude Desktop"
|
||||||
sidebarTitle: 'MCP'
|
sidebarTitle: 'MCP Integration'
|
||||||
---
|
---
|
||||||
|
|
||||||
You can let Claude use Honcho to manage its own memory in the native desktop app by using the Honcho MCP integration! Follow these steps:
|
You can let Claude use Honcho to manage its own memory in the native desktop app by using the Honcho MCP integration! Follow these steps:
|
||||||
|
|
@ -70,4 +70,4 @@ You may customize your assistant name and/or workspace ID. Both are optional.
|
||||||
|
|
||||||
4. Finally, Claude needs instructions on how to use Honcho. The Desktop app doesn't allow you to add system prompts directly, but you can create a project and paste these [instructions](https://raw.githubusercontent.com/plastic-labs/honcho/refs/heads/main/mcp/instructions.md) into the "Project Instructions" field.
|
4. Finally, Claude needs instructions on how to use Honcho. The Desktop app doesn't allow you to add system prompts directly, but you can create a project and paste these [instructions](https://raw.githubusercontent.com/plastic-labs/honcho/refs/heads/main/mcp/instructions.md) into the "Project Instructions" field.
|
||||||
|
|
||||||
Claude should then query for insights before responding and write your messages to storage! If you come up with more creative ways to get Claude to manage its own memory with Honcho, feel free to [let us know](https://discord.gg/honcho) or make a PR on this [repo](https://github.com/plastic-labs/honcho/tree/main/mcp)!
|
Claude should then query for insights before responding and write your messages to storage! If you come up with more creative ways to get Claude to manage its own memory with Honcho, feel free to [let us know](https://discord.gg/plasticlabs) or make a PR on this [repo](https://github.com/plastic-labs/honcho/tree/main/mcp)!
|
||||||
|
|
@ -1,758 +0,0 @@
|
||||||
---
|
|
||||||
title: "n8n"
|
|
||||||
icon: 'share-nodes'
|
|
||||||
description: "Connect Honcho to your n8n workflows to build intelligent automation workflows and agents that leverage persistent memory across sessions."
|
|
||||||
sidebarTitle: 'n8n'
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
- n8n instance (self-hosted or cloud)
|
|
||||||
- Honcho API key ([get one here](https://app.honcho.dev))
|
|
||||||
- Basic understanding of n8n workflows
|
|
||||||
- Basic understanding of [Honcho architecture](/v2/documentation/core-concepts/architecture). Specifically **workspaces**, **sessions**, **peers**, and **messages**.
|
|
||||||
|
|
||||||
### Before You Start
|
|
||||||
|
|
||||||
**This integration uses HTTP Request nodes.** There's no native Honcho node for n8n yet. While this requires more setup, it gives you full control over the API and works with any n8n version.
|
|
||||||
|
|
||||||
**This tutorial is instructional, not production-ready.** We load a single Gmail message with hardcoded IDs to demonstrate the concepts clearly. See [Next Steps](#next-steps) for handling multiple messages and dynamic configurations.
|
|
||||||
|
|
||||||
**Why Honcho over n8n's built-in memory?** n8n's "memory" nodes are vector databases for RAG-style retrieval. Honcho offers richer context and reasoning—it builds understanding of users over time, not just similarity search. [Learn more](https://blog.plasticlabs.ai/blog/Memory-as-Reasoning).
|
|
||||||
|
|
||||||
### Setting Up the HTTP Request Node
|
|
||||||
|
|
||||||
The Honcho integration in n8n uses the HTTP Request node to interact with the Honcho API. Here's how to configure it:
|
|
||||||
|
|
||||||
1. Add an **HTTP Request** node to your workflow (Core > HTTP Request)
|
|
||||||
|
|
||||||
<div style={{ maxWidth: "400px" }}>
|
|
||||||
<Frame>
|
|
||||||
<img src="/images/integrations/n8n/Http_request_core.png" alt="Adding HTTP Request node from Core nodes" />
|
|
||||||
</Frame>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
2. Set the **Method** based on your operation (typically `POST` for creating resources, `GET` for retrieving)
|
|
||||||
|
|
||||||
3. Set the **URL** to the appropriate Honcho API endpoint. For example, create workspace is `https://api.honcho.dev/v2/workspaces`
|
|
||||||
|
|
||||||
4. For authentication, select **Generic Credential Type** and then **Bearer Auth**
|
|
||||||
|
|
||||||
5. Click **Create New Credential** and paste your Honcho API key in the Bearer Token field
|
|
||||||
|
|
||||||
<div style={{ maxWidth: "400px" }}>
|
|
||||||
<Frame>
|
|
||||||
<img src="/images/integrations/n8n/Bearer_auth_cred.png" alt="Bearer Auth credential setup in n8n" />
|
|
||||||
</Frame>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
6. Check **Send Body** and select **JSON** as the Body Content Type when creating resources
|
|
||||||
|
|
||||||
## Step-by-Step Tutorial
|
|
||||||
|
|
||||||
We'll build a workflow that ingests Gmail emails into Honcho, then uses that memory to power a conversational AI chatbot.
|
|
||||||
|
|
||||||
The workflow has two parts (separated by sticky notes in the canvas):
|
|
||||||
|
|
||||||
1. **Data Ingestion**: Manual trigger → Workspace → Session → Gmail → Extract Peers → Create Peers → Add to Session → Create Messages
|
|
||||||
|
|
||||||
2. **AI Chat Interface**: Chat Trigger → Agent (with LLM and Honcho tools)
|
|
||||||
|
|
||||||
The Agent uses Honcho's `get_context()` endpoint to retrieve relevant information about email conversations, enabling contextual conversations about your email data.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Part 1: Loading Email Data into Honcho
|
|
||||||
|
|
||||||
These nodes handle the initial setup and data ingestion:
|
|
||||||
|
|
||||||
<Tip>
|
|
||||||
**Pro Tip: Copy from API Playground**
|
|
||||||
|
|
||||||
The fastest way to configure any Honcho endpoint is to copy the curl command directly from the [app.honcho.dev](https://app.honcho.dev) API playground:
|
|
||||||
|
|
||||||
1. Navigate to the endpoint you want to use in the API playground
|
|
||||||
2. Fill in your parameters, verify the results and click **Copy as cURL**
|
|
||||||
3. Then in n8n use the **import cURL** button to directly import the request (be sure to verify the bearer token imported correctly)
|
|
||||||
|
|
||||||
</Tip>
|
|
||||||
|
|
||||||
#### Step 1: Manual Trigger
|
|
||||||
|
|
||||||
Start with a **Manual Trigger** node to execute the workflow on demand. This is useful for initial setup and testing before automating with a Gmail trigger.
|
|
||||||
|
|
||||||
#### Step 2: Get or Create Workspace
|
|
||||||
|
|
||||||
1. Add an **HTTP Request** node
|
|
||||||
2. **Method**: `POST`
|
|
||||||
3. **URL**: `https://api.honcho.dev/v2/workspaces`
|
|
||||||
4. **Body** (JSON): `{ "id": "email-test", "metadata": {} }`
|
|
||||||
|
|
||||||
<Tip>
|
|
||||||
**Verify Your Data in Honcho**
|
|
||||||
As you build the data ingestion workflow, verify everything is created correctly in your [Honcho instance](https://app.honcho.dev/).
|
|
||||||
</Tip>
|
|
||||||
|
|
||||||
#### Step 3: Get or Create Session
|
|
||||||
|
|
||||||
1. Add another **HTTP Request** node
|
|
||||||
2. **Method**: `POST`
|
|
||||||
3. **URL**: `https://api.honcho.dev/v2/workspaces/{{ $('Get or Create Workspace').item.json.id }}/sessions`
|
|
||||||
4. **Body** (JSON): `{ "id": "new_session" }`
|
|
||||||
|
|
||||||
#### Step 4: Get Gmail Message
|
|
||||||
|
|
||||||
1. Add a **Gmail** node
|
|
||||||
2. **Operation**: Get
|
|
||||||
3. **Message ID**: Your target message ID (a string of letters & numbers)
|
|
||||||
4. Configure your Gmail OAuth2 credentials
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
**Finding the Gmail Message ID**
|
|
||||||
The easiest way to find a Gmail message ID is to use n8n's Gmail Get Many operation. Temporarily add it, set the limit to 1, and execute. Use the message ID in the output for the message ID field.
|
|
||||||
|
|
||||||
In this tutorial, we load in only a single message to demonstrate the workflow.
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
#### Step 5: Extract Peers from Email
|
|
||||||
|
|
||||||
Use native n8n nodes to extract email participants as peers:
|
|
||||||
|
|
||||||
**5a. Add a Set node ("Combine Email Fields")**
|
|
||||||
- Combines From, To, Cc, Bcc into an array of individual emails
|
|
||||||
- **Field name**: `allEmails`
|
|
||||||
- **Type**: Array
|
|
||||||
- **Value**: `{{ [$json.From, $json.To, $json.Cc, $json.Bcc].filter(Boolean).flatMap(field => field.split(',').map(e => e.trim())).filter(Boolean) }}`
|
|
||||||
|
|
||||||
**5b. Add a Split Out node**
|
|
||||||
- Splits the array into individual items (one per email address)
|
|
||||||
- **Field to Split Out**: `allEmails`
|
|
||||||
|
|
||||||
**5c. Add a Set node ("Clean Names")**
|
|
||||||
- Extracts the display name from each email and formats it
|
|
||||||
- **Field name**: `name`
|
|
||||||
- **Value**: `{{ $json.allEmails.split('<')[0].trim().replace(/ /g, '_') }}`
|
|
||||||
|
|
||||||
#### Step 6: Get or Create Peer
|
|
||||||
|
|
||||||
1. Add an **HTTP Request** node
|
|
||||||
2. **Method**: `POST`
|
|
||||||
3. **URL**: `https://api.honcho.dev/v2/workspaces/{{ $('Get or Create Workspace').item.json.id }}/peers`
|
|
||||||
4. **Body**: `{ "id": "{{ $json.name }}" }`
|
|
||||||
|
|
||||||
This creates a peer for each email participant, allowing Honcho to build understanding of each person.
|
|
||||||
|
|
||||||
#### Step 7: Add Peers to Session
|
|
||||||
|
|
||||||
1. Add an **HTTP Request** node
|
|
||||||
2. **Method**: `POST`
|
|
||||||
3. **URL**: `https://api.honcho.dev/v2/workspaces/{{ $('Get or Create Workspace').item.json.id }}/sessions/{{ $json.id }}/peers`
|
|
||||||
4. **Body**: `{ "{{ $json.id }}": {} }`
|
|
||||||
|
|
||||||
#### Step 8: Limit Node
|
|
||||||
|
|
||||||
Add a **Limit** node to control the flow so the message is only added once to the session.
|
|
||||||
|
|
||||||
#### Step 9: Create Message for Session
|
|
||||||
|
|
||||||
1. Add an **HTTP Request** node
|
|
||||||
2. **Method**: `POST`
|
|
||||||
3. **URL**: `https://api.honcho.dev/v2/workspaces/{{ $('Get or Create Workspace').item.json.id }}/sessions/{{ $('Get or Create Session').item.json.id }}/messages/`
|
|
||||||
4. **Body** (JSON): `{ "messages": [{ "content": "{{ $('Get a message').item.json.snippet }}", "peer_id": "{{ $('Get a message').item.json.From.split('<')[0].trim().replace(/ /g, '_') }}" }] }`
|
|
||||||
|
|
||||||
The `peer_id` must exactly match a peer created in Step 6. The expression above uses the same cleaning logic as the Clean Names node (`split('<')[0].trim().replace(/ /g, '_')`).
|
|
||||||
|
|
||||||
### Part 2: Building a Stateful AI Chatbot
|
|
||||||
|
|
||||||
Now that data is loaded into Honcho, create a chat interface that leverages this memory:
|
|
||||||
|
|
||||||
#### Step 1: Chat Trigger
|
|
||||||
|
|
||||||
Add a **When chat message received** node (from LangChain nodes) to create an interactive chat interface.
|
|
||||||
|
|
||||||
#### Step 2: AI Agent
|
|
||||||
|
|
||||||
1. Add an **Agent** node (LangChain)
|
|
||||||
2. Configure the system message:
|
|
||||||
```
|
|
||||||
You are a helpful assistant that retrieves context about email conversations.
|
|
||||||
|
|
||||||
Use the Get_Context tool to retrieve session context.
|
|
||||||
|
|
||||||
Today's date: {{ $now }}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 3: Connect LLM
|
|
||||||
|
|
||||||
Add an **OpenAI Chat Model** node (or your preferred LLM) and connect it to the Agent.
|
|
||||||
|
|
||||||
#### Step 4: Add Honcho Tools
|
|
||||||
|
|
||||||
Create an HTTP Request Tool node for Honcho's context retrieval:
|
|
||||||
|
|
||||||
**Get Context Tool:**
|
|
||||||
- **Method**: `GET`
|
|
||||||
- **URL**: `https://api.honcho.dev/v2/workspaces/email-test/sessions/new_session/context`
|
|
||||||
- Returns formatted context for the entire session including all messages and peer interactions
|
|
||||||
|
|
||||||
Connect the tool to the Agent node. The URL uses the same workspace (`email-test`) and session (`new_session`) IDs created during data ingestion.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Import the Workflow
|
|
||||||
|
|
||||||
Want to skip the manual setup? Import this workflow directly into n8n. In n8n, go to **Workflows** → **Import from URL** (use the raw JSON link below) or **Import from File**.
|
|
||||||
|
|
||||||
[Import from URL (raw JSON)](https://raw.githubusercontent.com/plastic-labs/honcho/main/examples/n8n/n8n.json) or expand below to copy:
|
|
||||||
|
|
||||||
<Accordion title="Click to expand workflow JSON">
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "Honcho Empowered Email AI Agent",
|
|
||||||
"nodes": [
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"content": "## Data Ingestion\nLoads Gmail email into Honcho.\n\n**Run this section first** by clicking 'Execute workflow'.",
|
|
||||||
"height": 356,
|
|
||||||
"width": 2008
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.stickyNote",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [
|
|
||||||
-16,
|
|
||||||
-64
|
|
||||||
],
|
|
||||||
"id": "53f767a8-8df9-4ad7-8a3d-37c145495627",
|
|
||||||
"name": "Sticky Note - Data Ingestion"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"content": "## AI Chat With Honcho get_context()\nQuery your email data using natural language.\n\n**Run after data ingestion** to chat with the agent.",
|
|
||||||
"height": 480,
|
|
||||||
"width": 752
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.stickyNote",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [
|
|
||||||
32,
|
|
||||||
464
|
|
||||||
],
|
|
||||||
"id": "34066961-d29e-4fe8-93bf-8f7043e142b0",
|
|
||||||
"name": "Sticky Note - AI Chat"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"model": "gpt-4o",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"id": "16da7a98-5622-427a-bbab-1be39f828d0b",
|
|
||||||
"name": "OpenAI Chat Model",
|
|
||||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
|
||||||
"position": [
|
|
||||||
240,
|
|
||||||
800
|
|
||||||
],
|
|
||||||
"typeVersion": 1,
|
|
||||||
"credentials": {
|
|
||||||
"openAiApi": {
|
|
||||||
"id": "qrvGphL3ydUODxQZ",
|
|
||||||
"name": "OpenAi account"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"options": {
|
|
||||||
"systemMessage": "You are a helpful assistant that retrieves context about email conversations.\n\nUse the Get_Context tool to retrieve session context.\n\nToday's date: {{ $now }}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": "049c3c19-756c-4755-88d9-94312857d9bb",
|
|
||||||
"name": "AI Agent",
|
|
||||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
|
||||||
"position": [
|
|
||||||
368,
|
|
||||||
576
|
|
||||||
],
|
|
||||||
"typeVersion": 1.7
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"id": "b9a2ef6a-0e81-45c9-a5ea-16e74a9aa77d",
|
|
||||||
"name": "When chat message received",
|
|
||||||
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
|
|
||||||
"position": [
|
|
||||||
80,
|
|
||||||
576
|
|
||||||
],
|
|
||||||
"webhookId": "c91764c2-0b51-4025-ad74-d5f44127aa5a",
|
|
||||||
"typeVersion": 1.1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"method": "POST",
|
|
||||||
"url": "=https://api.honcho.dev/v2/workspaces/{{ $('Get or Create Workspace').item.json.id }}/peers",
|
|
||||||
"authentication": "predefinedCredentialType",
|
|
||||||
"nodeCredentialType": "httpBearerAuth",
|
|
||||||
"sendBody": true,
|
|
||||||
"bodyParameters": {
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"value": "={{ $json.name }}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.httpRequest",
|
|
||||||
"typeVersion": 4.3,
|
|
||||||
"position": [
|
|
||||||
1296,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "f2e81445-a866-4d9f-9a8a-b2dc8cbaea8b",
|
|
||||||
"name": "Get or Create Peer",
|
|
||||||
"credentials": {
|
|
||||||
"httpBearerAuth": {
|
|
||||||
"id": "NbrkGo1GdYWQY3OX",
|
|
||||||
"name": "Bearer Auth account"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"operation": "get",
|
|
||||||
"messageId": "19b8fee837985953"
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.gmail",
|
|
||||||
"typeVersion": 2.2,
|
|
||||||
"position": [
|
|
||||||
608,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "ee5d8cc7-876b-4096-b6e5-14f1b92084a6",
|
|
||||||
"name": "Get a message",
|
|
||||||
"webhookId": "4ab02540-af03-405d-a6eb-dfcaa76477fc",
|
|
||||||
"credentials": {
|
|
||||||
"gmailOAuth2": {
|
|
||||||
"id": "a2RvA5NMNjfOeHtd",
|
|
||||||
"name": "Gmail account 2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"method": "POST",
|
|
||||||
"url": "=https://api.honcho.dev/v2/workspaces/{{ $('Get or Create Workspace').item.json.id }}/sessions",
|
|
||||||
"authentication": "predefinedCredentialType",
|
|
||||||
"nodeCredentialType": "httpBearerAuth",
|
|
||||||
"sendBody": true,
|
|
||||||
"bodyParameters": {
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"value": "=new_session"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.httpRequest",
|
|
||||||
"typeVersion": 4.3,
|
|
||||||
"position": [
|
|
||||||
448,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "eef01db2-3355-484d-97b8-34480c40fcf8",
|
|
||||||
"name": "Get or Create Session",
|
|
||||||
"credentials": {
|
|
||||||
"httpBearerAuth": {
|
|
||||||
"id": "NbrkGo1GdYWQY3OX",
|
|
||||||
"name": "Bearer Auth account"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"type": "n8n-nodes-base.manualTrigger",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [
|
|
||||||
48,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "a9de9d8f-7911-444f-99bd-7d287f587eff",
|
|
||||||
"name": "When clicking 'Execute workflow'"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"method": "POST",
|
|
||||||
"url": "https://api.honcho.dev/v2/workspaces",
|
|
||||||
"authentication": "predefinedCredentialType",
|
|
||||||
"nodeCredentialType": "httpBearerAuth",
|
|
||||||
"sendBody": true,
|
|
||||||
"specifyBody": "json",
|
|
||||||
"jsonBody": "{\n \"id\": \"email-test\",\n \"metadata\": {}\n}",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.httpRequest",
|
|
||||||
"typeVersion": 4.3,
|
|
||||||
"position": [
|
|
||||||
240,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "49b91f82-1cd8-49aa-85a5-57a8ad7b5128",
|
|
||||||
"name": "Get or Create Workspace",
|
|
||||||
"credentials": {
|
|
||||||
"httpBearerAuth": {
|
|
||||||
"id": "NbrkGo1GdYWQY3OX",
|
|
||||||
"name": "Bearer Auth account"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"assignments": {
|
|
||||||
"assignments": [
|
|
||||||
{
|
|
||||||
"id": "allEmails",
|
|
||||||
"name": "allEmails",
|
|
||||||
"type": "array",
|
|
||||||
"value": "={{ [$json.From, $json.To, $json.Cc, $json.Bcc].filter(Boolean).flatMap(field => field.split(',').map(e => e.trim())).filter(Boolean) }}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.set",
|
|
||||||
"typeVersion": 3.4,
|
|
||||||
"position": [
|
|
||||||
784,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "3eb4ce63-d95c-42d7-8a16-f35e2419d8c0",
|
|
||||||
"name": "Combine Email Fields"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"fieldToSplitOut": "allEmails",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.splitOut",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [
|
|
||||||
960,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "4702001d-40f5-4b44-b443-6fb0b94e58a9",
|
|
||||||
"name": "Split Out"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"assignments": {
|
|
||||||
"assignments": [
|
|
||||||
{
|
|
||||||
"id": "name",
|
|
||||||
"name": "name",
|
|
||||||
"type": "string",
|
|
||||||
"value": "={{ $json.allEmails.split('<')[0].trim().replace(/ /g, '_') }}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.set",
|
|
||||||
"typeVersion": 3.4,
|
|
||||||
"position": [
|
|
||||||
1136,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "93327201-bfbe-4385-bfcd-7646f0513a62",
|
|
||||||
"name": "Clean Names"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"method": "POST",
|
|
||||||
"url": "=https://api.honcho.dev/v2/workspaces/{{ $('Get or Create Workspace').item.json.id }}/sessions/{{ $('Get or Create Session').item.json.id }}/messages/",
|
|
||||||
"authentication": "predefinedCredentialType",
|
|
||||||
"nodeCredentialType": "httpBearerAuth",
|
|
||||||
"sendBody": true,
|
|
||||||
"specifyBody": "json",
|
|
||||||
"jsonBody": "={\"messages\": [{\"content\": \"{{ $('Get a message').item.json.snippet }}\", \"peer_id\": \"{{ $('Get a message').item.json.From.split('<')[0].trim().replace(/ /g, '_') }}\"}]}",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.httpRequest",
|
|
||||||
"typeVersion": 4.3,
|
|
||||||
"position": [
|
|
||||||
1808,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "cb6732d0-e5a9-4d6e-98ef-8fe1c290740b",
|
|
||||||
"name": "Create Message for Session",
|
|
||||||
"credentials": {
|
|
||||||
"httpBearerAuth": {
|
|
||||||
"id": "NbrkGo1GdYWQY3OX",
|
|
||||||
"name": "Bearer Auth account"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"method": "POST",
|
|
||||||
"url": "=https://api.honcho.dev/v2/workspaces/{{ $('Get or Create Workspace').item.json.id }}/sessions/{{ $('Get or Create Session').item.json.id }}/peers",
|
|
||||||
"authentication": "predefinedCredentialType",
|
|
||||||
"nodeCredentialType": "httpBearerAuth",
|
|
||||||
"sendBody": true,
|
|
||||||
"specifyBody": "json",
|
|
||||||
"jsonBody": "={\"{{ $json.id }}\": {}}",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.httpRequest",
|
|
||||||
"typeVersion": 4.3,
|
|
||||||
"position": [
|
|
||||||
1472,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "3c2065b6-bd68-4698-a740-db3cd52f2267",
|
|
||||||
"name": "Add Peers to Session",
|
|
||||||
"credentials": {
|
|
||||||
"httpBearerAuth": {
|
|
||||||
"id": "NbrkGo1GdYWQY3OX",
|
|
||||||
"name": "Bearer Auth account"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {},
|
|
||||||
"type": "n8n-nodes-base.limit",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [
|
|
||||||
1632,
|
|
||||||
96
|
|
||||||
],
|
|
||||||
"id": "d7c38f49-1a76-4a67-a540-4bec7aae1d9f",
|
|
||||||
"name": "Limit"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"url": "https://api.honcho.dev/v2/workspaces/email-test/sessions/new_session/context",
|
|
||||||
"authentication": "predefinedCredentialType",
|
|
||||||
"nodeCredentialType": "httpBearerAuth",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"type": "n8n-nodes-base.httpRequestTool",
|
|
||||||
"typeVersion": 4.3,
|
|
||||||
"position": [
|
|
||||||
656,
|
|
||||||
784
|
|
||||||
],
|
|
||||||
"id": "095e62d6-aae3-4eb8-9b9c-c473bfe2716d",
|
|
||||||
"name": "Get_Context",
|
|
||||||
"credentials": {
|
|
||||||
"httpBearerAuth": {
|
|
||||||
"id": "NbrkGo1GdYWQY3OX",
|
|
||||||
"name": "Bearer Auth account"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"pinData": {},
|
|
||||||
"connections": {
|
|
||||||
"OpenAI Chat Model": {
|
|
||||||
"ai_languageModel": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "AI Agent",
|
|
||||||
"type": "ai_languageModel",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"When chat message received": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "AI Agent",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"When clicking 'Execute workflow'": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Get or Create Workspace",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Get or Create Workspace": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Get or Create Session",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Get a message": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Combine Email Fields",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Get or Create Session": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Get a message",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Combine Email Fields": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Split Out",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Split Out": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Clean Names",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Clean Names": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Get or Create Peer",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Get or Create Peer": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Add Peers to Session",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Add Peers to Session": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Limit",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Limit": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Create Message for Session",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Get_Context": {
|
|
||||||
"ai_tool": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "AI Agent",
|
|
||||||
"type": "ai_tool",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"active": false,
|
|
||||||
"settings": {
|
|
||||||
"executionOrder": "v1",
|
|
||||||
"availableInMCP": false
|
|
||||||
},
|
|
||||||
"versionId": "ba0a3b77-cc19-49fd-9189-aaee65b35f99",
|
|
||||||
"meta": {
|
|
||||||
"templateCredsSetupCompleted": true,
|
|
||||||
"instanceId": "4e34c96e55eb26be21fa69ca62c4851a5d09b678190481f5d47c084b6b327003"
|
|
||||||
},
|
|
||||||
"id": "dKOYeEOdrZOetmFRmIAUJ",
|
|
||||||
"tags": []
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
**Important:** After importing, you'll need to:
|
|
||||||
- Add your Honcho API key to the Bearer Auth credential
|
|
||||||
- Connect your Gmail OAuth2 credential
|
|
||||||
- Add your OpenAI API key (or swap for your preferred LLM)
|
|
||||||
- Update the Gmail Message ID in "Get a message" node
|
|
||||||
|
|
||||||
**Running the workflow:**
|
|
||||||
1. First, execute the data ingestion section (click "Execute workflow")
|
|
||||||
2. Then use the chat interface to query your email data
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
Once you have the basic workflow running, consider these enhancements:
|
|
||||||
|
|
||||||
- **Dynamic IDs**: Use n8n variables instead of hardcoding `email-test` and `new_session`
|
|
||||||
- **Chat with Peers**: Add an HTTP Request Tool for natural language queries about peer representations. Read more in the [docs](/v2/documentation/core-concepts/features/dialectic).
|
|
||||||
- **Load more messages**: Use Gmail's "Get All" operation to load entire conversation threads
|
|
||||||
- **Make it real-time**: Add a **Gmail Trigger** node to automatically ingest new emails as they arrive
|
|
||||||
- **Add error handling**: Connect an **Error Trigger** node with notifications (Email, Slack) and retry logic
|
|
||||||
- **Expand to other data sources**: Honcho works with Slack messages, CRM interactions, support tickets, and more
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Related Resources
|
|
||||||
|
|
||||||
- [Honcho Architecture](/v2/documentation/core-concepts/architecture) - Understand workspaces, sessions, peers, and messages
|
|
||||||
- [Get Context](/v2/documentation/core-concepts/features/get-context) - Learn about retrieving formatted conversation context
|
|
||||||
- [API Reference](/v2/api-reference) - Complete API documentation
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
@ -11,29 +11,40 @@ AI development often feels like magic - you craft the right prompt and get exact
|
||||||
|
|
||||||
Whether you're integrating Honcho into existing platforms, exploring advanced features, or getting up and running quickly, these guides provide concrete examples and implementation patterns.
|
Whether you're integrating Honcho into existing platforms, exploring advanced features, or getting up and running quickly, these guides provide concrete examples and implementation patterns.
|
||||||
|
|
||||||
Each spellbook focuses on a specific use case with working code you can adapt to your needs. The goal is to get you from idea to working prototype as quickly as possible, then provide the depth you need to scale and customize.
|
## What You'll Find Here
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
|
||||||
## Getting Started
|
**[Overview](/v2/guides/overview)** - You are here
|
||||||
Quick integration guides to get up and running:
|
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
**[MCP Integration](/v2/guides/mcp)** - Get Honcho running with a single prompt in Cursor or Claude Code
|
||||||
<Card title="MCP Integration" icon="link" href="/v2/integrations/mcp">
|
|
||||||
Get Honcho running with a single prompt in Claude Code
|
|
||||||
</Card>
|
|
||||||
<Card title="LangGraph" icon="diagram-project" href="/v2/integrations/langgraph">
|
|
||||||
Add persistent memory and theory of mind to your LangGraph agents
|
|
||||||
</Card>
|
|
||||||
</CardGroup>
|
|
||||||
|
|
||||||
## Application Interfaces
|
### Application Interfaces
|
||||||
Ready-to-use integration patterns for popular platforms:
|
Ready-to-use integration patterns for popular platforms:
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
**[Discord Bot](/v2/guides/discord)** - Build a Discord bot that remembers users across conversations
|
||||||
<Card title="Discord Bot" icon="discord" href="/v2/guides/discord">
|
|
||||||
Build a Discord bot that remembers users across conversations
|
**[Telegram Bot](/v2/guides/telegram)** - Create a Telegram bot with persistent user understanding
|
||||||
</Card>
|
|
||||||
<Card title="Telegram Bot" icon="telegram" href="/v2/guides/telegram">
|
### Design Patterns
|
||||||
Create a Telegram bot with persistent user understanding
|
Implementation patterns for Honcho's core capabilities:
|
||||||
</Card>
|
|
||||||
</CardGroup>
|
**[Dialectic Endpoint](/v2/guides/dialectic-endpoint)** - Query user psychology in natural language
|
||||||
|
|
||||||
|
**[Working with Session Context](/v2/guides/get-context)** - Manage conversation flow and context windows
|
||||||
|
|
||||||
|
**[Search](/v2/guides/search)** - Search your data using natural language
|
||||||
|
|
||||||
|
**[Working Representations](/v2/guides/working-rep)** - Understanding and customizing user models
|
||||||
|
|
||||||
|
**[Streaming Responses](/v2/guides/streaming-response)** - Handle real-time interactions efficiently
|
||||||
|
|
||||||
|
**[Using Filters](/v2/guides/using-filters)** - Control what data gets processed and how
|
||||||
|
|
||||||
|
**[File Uploads](/v2/guides/file-uploads)** - Upload PDF, text, or JSON files to create messages
|
||||||
|
|
||||||
|
## Philosophy
|
||||||
|
|
||||||
|
These aren't just API documentation - they're implementation patterns that solve real problems. Each spellbook focuses on a specific use case with working code you can adapt to your needs.
|
||||||
|
|
||||||
|
The goal is to get you from idea to working prototype as quickly as possible, then provide the depth you need to scale and customize.
|
||||||
|
|
|
||||||