gobby 0.2.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gobby/__init__.py +3 -0
- gobby/adapters/__init__.py +30 -0
- gobby/adapters/base.py +93 -0
- gobby/adapters/claude_code.py +276 -0
- gobby/adapters/codex.py +1292 -0
- gobby/adapters/gemini.py +343 -0
- gobby/agents/__init__.py +37 -0
- gobby/agents/codex_session.py +120 -0
- gobby/agents/constants.py +112 -0
- gobby/agents/context.py +362 -0
- gobby/agents/definitions.py +133 -0
- gobby/agents/gemini_session.py +111 -0
- gobby/agents/registry.py +618 -0
- gobby/agents/runner.py +968 -0
- gobby/agents/session.py +259 -0
- gobby/agents/spawn.py +916 -0
- gobby/agents/spawners/__init__.py +77 -0
- gobby/agents/spawners/base.py +142 -0
- gobby/agents/spawners/cross_platform.py +266 -0
- gobby/agents/spawners/embedded.py +225 -0
- gobby/agents/spawners/headless.py +226 -0
- gobby/agents/spawners/linux.py +125 -0
- gobby/agents/spawners/macos.py +277 -0
- gobby/agents/spawners/windows.py +308 -0
- gobby/agents/tty_config.py +319 -0
- gobby/autonomous/__init__.py +32 -0
- gobby/autonomous/progress_tracker.py +447 -0
- gobby/autonomous/stop_registry.py +269 -0
- gobby/autonomous/stuck_detector.py +383 -0
- gobby/cli/__init__.py +67 -0
- gobby/cli/__main__.py +8 -0
- gobby/cli/agents.py +529 -0
- gobby/cli/artifacts.py +266 -0
- gobby/cli/daemon.py +329 -0
- gobby/cli/extensions.py +526 -0
- gobby/cli/github.py +263 -0
- gobby/cli/init.py +53 -0
- gobby/cli/install.py +614 -0
- gobby/cli/installers/__init__.py +37 -0
- gobby/cli/installers/antigravity.py +65 -0
- gobby/cli/installers/claude.py +363 -0
- gobby/cli/installers/codex.py +192 -0
- gobby/cli/installers/gemini.py +294 -0
- gobby/cli/installers/git_hooks.py +377 -0
- gobby/cli/installers/shared.py +737 -0
- gobby/cli/linear.py +250 -0
- gobby/cli/mcp.py +30 -0
- gobby/cli/mcp_proxy.py +698 -0
- gobby/cli/memory.py +304 -0
- gobby/cli/merge.py +384 -0
- gobby/cli/projects.py +79 -0
- gobby/cli/sessions.py +622 -0
- gobby/cli/tasks/__init__.py +30 -0
- gobby/cli/tasks/_utils.py +658 -0
- gobby/cli/tasks/ai.py +1025 -0
- gobby/cli/tasks/commits.py +169 -0
- gobby/cli/tasks/crud.py +685 -0
- gobby/cli/tasks/deps.py +135 -0
- gobby/cli/tasks/labels.py +63 -0
- gobby/cli/tasks/main.py +273 -0
- gobby/cli/tasks/search.py +178 -0
- gobby/cli/tui.py +34 -0
- gobby/cli/utils.py +513 -0
- gobby/cli/workflows.py +927 -0
- gobby/cli/worktrees.py +481 -0
- gobby/config/__init__.py +129 -0
- gobby/config/app.py +551 -0
- gobby/config/extensions.py +167 -0
- gobby/config/features.py +472 -0
- gobby/config/llm_providers.py +98 -0
- gobby/config/logging.py +66 -0
- gobby/config/mcp.py +346 -0
- gobby/config/persistence.py +247 -0
- gobby/config/servers.py +141 -0
- gobby/config/sessions.py +250 -0
- gobby/config/tasks.py +784 -0
- gobby/hooks/__init__.py +104 -0
- gobby/hooks/artifact_capture.py +213 -0
- gobby/hooks/broadcaster.py +243 -0
- gobby/hooks/event_handlers.py +723 -0
- gobby/hooks/events.py +218 -0
- gobby/hooks/git.py +169 -0
- gobby/hooks/health_monitor.py +171 -0
- gobby/hooks/hook_manager.py +856 -0
- gobby/hooks/hook_types.py +575 -0
- gobby/hooks/plugins.py +813 -0
- gobby/hooks/session_coordinator.py +396 -0
- gobby/hooks/verification_runner.py +268 -0
- gobby/hooks/webhooks.py +339 -0
- gobby/install/claude/commands/gobby/bug.md +51 -0
- gobby/install/claude/commands/gobby/chore.md +51 -0
- gobby/install/claude/commands/gobby/epic.md +52 -0
- gobby/install/claude/commands/gobby/eval.md +235 -0
- gobby/install/claude/commands/gobby/feat.md +49 -0
- gobby/install/claude/commands/gobby/nit.md +52 -0
- gobby/install/claude/commands/gobby/ref.md +52 -0
- gobby/install/claude/hooks/HOOK_SCHEMAS.md +632 -0
- gobby/install/claude/hooks/hook_dispatcher.py +364 -0
- gobby/install/claude/hooks/validate_settings.py +102 -0
- gobby/install/claude/hooks-template.json +118 -0
- gobby/install/codex/hooks/hook_dispatcher.py +153 -0
- gobby/install/codex/prompts/forget.md +7 -0
- gobby/install/codex/prompts/memories.md +7 -0
- gobby/install/codex/prompts/recall.md +7 -0
- gobby/install/codex/prompts/remember.md +13 -0
- gobby/install/gemini/hooks/hook_dispatcher.py +268 -0
- gobby/install/gemini/hooks-template.json +138 -0
- gobby/install/shared/plugins/code_guardian.py +456 -0
- gobby/install/shared/plugins/example_notify.py +331 -0
- gobby/integrations/__init__.py +10 -0
- gobby/integrations/github.py +145 -0
- gobby/integrations/linear.py +145 -0
- gobby/llm/__init__.py +40 -0
- gobby/llm/base.py +120 -0
- gobby/llm/claude.py +578 -0
- gobby/llm/claude_executor.py +503 -0
- gobby/llm/codex.py +322 -0
- gobby/llm/codex_executor.py +513 -0
- gobby/llm/executor.py +316 -0
- gobby/llm/factory.py +34 -0
- gobby/llm/gemini.py +258 -0
- gobby/llm/gemini_executor.py +339 -0
- gobby/llm/litellm.py +287 -0
- gobby/llm/litellm_executor.py +303 -0
- gobby/llm/resolver.py +499 -0
- gobby/llm/service.py +236 -0
- gobby/mcp_proxy/__init__.py +29 -0
- gobby/mcp_proxy/actions.py +175 -0
- gobby/mcp_proxy/daemon_control.py +198 -0
- gobby/mcp_proxy/importer.py +436 -0
- gobby/mcp_proxy/lazy.py +325 -0
- gobby/mcp_proxy/manager.py +798 -0
- gobby/mcp_proxy/metrics.py +609 -0
- gobby/mcp_proxy/models.py +139 -0
- gobby/mcp_proxy/registries.py +215 -0
- gobby/mcp_proxy/schema_hash.py +381 -0
- gobby/mcp_proxy/semantic_search.py +706 -0
- gobby/mcp_proxy/server.py +549 -0
- gobby/mcp_proxy/services/__init__.py +0 -0
- gobby/mcp_proxy/services/fallback.py +306 -0
- gobby/mcp_proxy/services/recommendation.py +224 -0
- gobby/mcp_proxy/services/server_mgmt.py +214 -0
- gobby/mcp_proxy/services/system.py +72 -0
- gobby/mcp_proxy/services/tool_filter.py +231 -0
- gobby/mcp_proxy/services/tool_proxy.py +309 -0
- gobby/mcp_proxy/stdio.py +565 -0
- gobby/mcp_proxy/tools/__init__.py +27 -0
- gobby/mcp_proxy/tools/agents.py +1103 -0
- gobby/mcp_proxy/tools/artifacts.py +207 -0
- gobby/mcp_proxy/tools/hub.py +335 -0
- gobby/mcp_proxy/tools/internal.py +337 -0
- gobby/mcp_proxy/tools/memory.py +543 -0
- gobby/mcp_proxy/tools/merge.py +422 -0
- gobby/mcp_proxy/tools/metrics.py +283 -0
- gobby/mcp_proxy/tools/orchestration/__init__.py +23 -0
- gobby/mcp_proxy/tools/orchestration/cleanup.py +619 -0
- gobby/mcp_proxy/tools/orchestration/monitor.py +380 -0
- gobby/mcp_proxy/tools/orchestration/orchestrate.py +746 -0
- gobby/mcp_proxy/tools/orchestration/review.py +736 -0
- gobby/mcp_proxy/tools/orchestration/utils.py +16 -0
- gobby/mcp_proxy/tools/session_messages.py +1056 -0
- gobby/mcp_proxy/tools/task_dependencies.py +219 -0
- gobby/mcp_proxy/tools/task_expansion.py +591 -0
- gobby/mcp_proxy/tools/task_github.py +393 -0
- gobby/mcp_proxy/tools/task_linear.py +379 -0
- gobby/mcp_proxy/tools/task_orchestration.py +77 -0
- gobby/mcp_proxy/tools/task_readiness.py +522 -0
- gobby/mcp_proxy/tools/task_sync.py +351 -0
- gobby/mcp_proxy/tools/task_validation.py +843 -0
- gobby/mcp_proxy/tools/tasks/__init__.py +25 -0
- gobby/mcp_proxy/tools/tasks/_context.py +112 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +516 -0
- gobby/mcp_proxy/tools/tasks/_factory.py +176 -0
- gobby/mcp_proxy/tools/tasks/_helpers.py +129 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +517 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +301 -0
- gobby/mcp_proxy/tools/tasks/_resolution.py +55 -0
- gobby/mcp_proxy/tools/tasks/_search.py +215 -0
- gobby/mcp_proxy/tools/tasks/_session.py +125 -0
- gobby/mcp_proxy/tools/workflows.py +973 -0
- gobby/mcp_proxy/tools/worktrees.py +1264 -0
- gobby/mcp_proxy/transports/__init__.py +0 -0
- gobby/mcp_proxy/transports/base.py +95 -0
- gobby/mcp_proxy/transports/factory.py +44 -0
- gobby/mcp_proxy/transports/http.py +139 -0
- gobby/mcp_proxy/transports/stdio.py +213 -0
- gobby/mcp_proxy/transports/websocket.py +136 -0
- gobby/memory/backends/__init__.py +116 -0
- gobby/memory/backends/mem0.py +408 -0
- gobby/memory/backends/memu.py +485 -0
- gobby/memory/backends/null.py +111 -0
- gobby/memory/backends/openmemory.py +537 -0
- gobby/memory/backends/sqlite.py +304 -0
- gobby/memory/context.py +87 -0
- gobby/memory/manager.py +1001 -0
- gobby/memory/protocol.py +451 -0
- gobby/memory/search/__init__.py +66 -0
- gobby/memory/search/text.py +127 -0
- gobby/memory/viz.py +258 -0
- gobby/prompts/__init__.py +13 -0
- gobby/prompts/defaults/expansion/system.md +119 -0
- gobby/prompts/defaults/expansion/user.md +48 -0
- gobby/prompts/defaults/external_validation/agent.md +72 -0
- gobby/prompts/defaults/external_validation/external.md +63 -0
- gobby/prompts/defaults/external_validation/spawn.md +83 -0
- gobby/prompts/defaults/external_validation/system.md +6 -0
- gobby/prompts/defaults/features/import_mcp.md +22 -0
- gobby/prompts/defaults/features/import_mcp_github.md +17 -0
- gobby/prompts/defaults/features/import_mcp_search.md +16 -0
- gobby/prompts/defaults/features/recommend_tools.md +32 -0
- gobby/prompts/defaults/features/recommend_tools_hybrid.md +35 -0
- gobby/prompts/defaults/features/recommend_tools_llm.md +30 -0
- gobby/prompts/defaults/features/server_description.md +20 -0
- gobby/prompts/defaults/features/server_description_system.md +6 -0
- gobby/prompts/defaults/features/task_description.md +31 -0
- gobby/prompts/defaults/features/task_description_system.md +6 -0
- gobby/prompts/defaults/features/tool_summary.md +17 -0
- gobby/prompts/defaults/features/tool_summary_system.md +6 -0
- gobby/prompts/defaults/research/step.md +58 -0
- gobby/prompts/defaults/validation/criteria.md +47 -0
- gobby/prompts/defaults/validation/validate.md +38 -0
- gobby/prompts/loader.py +346 -0
- gobby/prompts/models.py +113 -0
- gobby/py.typed +0 -0
- gobby/runner.py +488 -0
- gobby/search/__init__.py +23 -0
- gobby/search/protocol.py +104 -0
- gobby/search/tfidf.py +232 -0
- gobby/servers/__init__.py +7 -0
- gobby/servers/http.py +636 -0
- gobby/servers/models.py +31 -0
- gobby/servers/routes/__init__.py +23 -0
- gobby/servers/routes/admin.py +416 -0
- gobby/servers/routes/dependencies.py +118 -0
- gobby/servers/routes/mcp/__init__.py +24 -0
- gobby/servers/routes/mcp/hooks.py +135 -0
- gobby/servers/routes/mcp/plugins.py +121 -0
- gobby/servers/routes/mcp/tools.py +1337 -0
- gobby/servers/routes/mcp/webhooks.py +159 -0
- gobby/servers/routes/sessions.py +582 -0
- gobby/servers/websocket.py +766 -0
- gobby/sessions/__init__.py +13 -0
- gobby/sessions/analyzer.py +322 -0
- gobby/sessions/lifecycle.py +240 -0
- gobby/sessions/manager.py +563 -0
- gobby/sessions/processor.py +225 -0
- gobby/sessions/summary.py +532 -0
- gobby/sessions/transcripts/__init__.py +41 -0
- gobby/sessions/transcripts/base.py +125 -0
- gobby/sessions/transcripts/claude.py +386 -0
- gobby/sessions/transcripts/codex.py +143 -0
- gobby/sessions/transcripts/gemini.py +195 -0
- gobby/storage/__init__.py +21 -0
- gobby/storage/agents.py +409 -0
- gobby/storage/artifact_classifier.py +341 -0
- gobby/storage/artifacts.py +285 -0
- gobby/storage/compaction.py +67 -0
- gobby/storage/database.py +357 -0
- gobby/storage/inter_session_messages.py +194 -0
- gobby/storage/mcp.py +680 -0
- gobby/storage/memories.py +562 -0
- gobby/storage/merge_resolutions.py +550 -0
- gobby/storage/migrations.py +860 -0
- gobby/storage/migrations_legacy.py +1359 -0
- gobby/storage/projects.py +166 -0
- gobby/storage/session_messages.py +251 -0
- gobby/storage/session_tasks.py +97 -0
- gobby/storage/sessions.py +817 -0
- gobby/storage/task_dependencies.py +223 -0
- gobby/storage/tasks/__init__.py +42 -0
- gobby/storage/tasks/_aggregates.py +180 -0
- gobby/storage/tasks/_crud.py +449 -0
- gobby/storage/tasks/_id.py +104 -0
- gobby/storage/tasks/_lifecycle.py +311 -0
- gobby/storage/tasks/_manager.py +889 -0
- gobby/storage/tasks/_models.py +300 -0
- gobby/storage/tasks/_ordering.py +119 -0
- gobby/storage/tasks/_path_cache.py +110 -0
- gobby/storage/tasks/_queries.py +343 -0
- gobby/storage/tasks/_search.py +143 -0
- gobby/storage/workflow_audit.py +393 -0
- gobby/storage/worktrees.py +547 -0
- gobby/sync/__init__.py +29 -0
- gobby/sync/github.py +333 -0
- gobby/sync/linear.py +304 -0
- gobby/sync/memories.py +284 -0
- gobby/sync/tasks.py +641 -0
- gobby/tasks/__init__.py +8 -0
- gobby/tasks/build_verification.py +193 -0
- gobby/tasks/commits.py +633 -0
- gobby/tasks/context.py +747 -0
- gobby/tasks/criteria.py +342 -0
- gobby/tasks/enhanced_validator.py +226 -0
- gobby/tasks/escalation.py +263 -0
- gobby/tasks/expansion.py +626 -0
- gobby/tasks/external_validator.py +764 -0
- gobby/tasks/issue_extraction.py +171 -0
- gobby/tasks/prompts/expand.py +327 -0
- gobby/tasks/research.py +421 -0
- gobby/tasks/tdd.py +352 -0
- gobby/tasks/tree_builder.py +263 -0
- gobby/tasks/validation.py +712 -0
- gobby/tasks/validation_history.py +357 -0
- gobby/tasks/validation_models.py +89 -0
- gobby/tools/__init__.py +0 -0
- gobby/tools/summarizer.py +170 -0
- gobby/tui/__init__.py +5 -0
- gobby/tui/api_client.py +281 -0
- gobby/tui/app.py +327 -0
- gobby/tui/screens/__init__.py +25 -0
- gobby/tui/screens/agents.py +333 -0
- gobby/tui/screens/chat.py +450 -0
- gobby/tui/screens/dashboard.py +377 -0
- gobby/tui/screens/memory.py +305 -0
- gobby/tui/screens/metrics.py +231 -0
- gobby/tui/screens/orchestrator.py +904 -0
- gobby/tui/screens/sessions.py +412 -0
- gobby/tui/screens/tasks.py +442 -0
- gobby/tui/screens/workflows.py +289 -0
- gobby/tui/screens/worktrees.py +174 -0
- gobby/tui/widgets/__init__.py +21 -0
- gobby/tui/widgets/chat.py +210 -0
- gobby/tui/widgets/conductor.py +104 -0
- gobby/tui/widgets/menu.py +132 -0
- gobby/tui/widgets/message_panel.py +160 -0
- gobby/tui/widgets/review_gate.py +224 -0
- gobby/tui/widgets/task_tree.py +99 -0
- gobby/tui/widgets/token_budget.py +166 -0
- gobby/tui/ws_client.py +258 -0
- gobby/utils/__init__.py +3 -0
- gobby/utils/daemon_client.py +235 -0
- gobby/utils/git.py +222 -0
- gobby/utils/id.py +38 -0
- gobby/utils/json_helpers.py +161 -0
- gobby/utils/logging.py +376 -0
- gobby/utils/machine_id.py +135 -0
- gobby/utils/metrics.py +589 -0
- gobby/utils/project_context.py +182 -0
- gobby/utils/project_init.py +263 -0
- gobby/utils/status.py +256 -0
- gobby/utils/validation.py +80 -0
- gobby/utils/version.py +23 -0
- gobby/workflows/__init__.py +4 -0
- gobby/workflows/actions.py +1310 -0
- gobby/workflows/approval_flow.py +138 -0
- gobby/workflows/artifact_actions.py +103 -0
- gobby/workflows/audit_helpers.py +110 -0
- gobby/workflows/autonomous_actions.py +286 -0
- gobby/workflows/context_actions.py +394 -0
- gobby/workflows/definitions.py +130 -0
- gobby/workflows/detection_helpers.py +208 -0
- gobby/workflows/engine.py +485 -0
- gobby/workflows/evaluator.py +669 -0
- gobby/workflows/git_utils.py +96 -0
- gobby/workflows/hooks.py +169 -0
- gobby/workflows/lifecycle_evaluator.py +613 -0
- gobby/workflows/llm_actions.py +70 -0
- gobby/workflows/loader.py +333 -0
- gobby/workflows/mcp_actions.py +60 -0
- gobby/workflows/memory_actions.py +272 -0
- gobby/workflows/premature_stop.py +164 -0
- gobby/workflows/session_actions.py +139 -0
- gobby/workflows/state_actions.py +123 -0
- gobby/workflows/state_manager.py +104 -0
- gobby/workflows/stop_signal_actions.py +163 -0
- gobby/workflows/summary_actions.py +344 -0
- gobby/workflows/task_actions.py +249 -0
- gobby/workflows/task_enforcement_actions.py +901 -0
- gobby/workflows/templates.py +52 -0
- gobby/workflows/todo_actions.py +84 -0
- gobby/workflows/webhook.py +223 -0
- gobby/workflows/webhook_executor.py +399 -0
- gobby/worktrees/__init__.py +5 -0
- gobby/worktrees/git.py +690 -0
- gobby/worktrees/merge/__init__.py +20 -0
- gobby/worktrees/merge/conflict_parser.py +177 -0
- gobby/worktrees/merge/resolver.py +485 -0
- gobby-0.2.5.dist-info/METADATA +351 -0
- gobby-0.2.5.dist-info/RECORD +383 -0
- gobby-0.2.5.dist-info/WHEEL +5 -0
- gobby-0.2.5.dist-info/entry_points.txt +2 -0
- gobby-0.2.5.dist-info/licenses/LICENSE.md +193 -0
- gobby-0.2.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Persistence configuration module.
|
|
3
|
+
|
|
4
|
+
Contains storage and sync-related Pydantic config models:
|
|
5
|
+
- MemoryConfig: Memory system settings (injection, decay, search)
|
|
6
|
+
- MemorySyncConfig: Memory file sync settings (debounce, export path)
|
|
7
|
+
|
|
8
|
+
Extracted from app.py using Strangler Fig pattern for code decomposition.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field, field_validator
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"MemoryConfig",
|
|
17
|
+
"MemorySyncConfig",
|
|
18
|
+
"Mem0Config",
|
|
19
|
+
"MemUConfig",
|
|
20
|
+
"OpenMemoryConfig",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Mem0Config(BaseModel):
|
|
25
|
+
"""Mem0 backend configuration.
|
|
26
|
+
|
|
27
|
+
Configure this section when using backend: 'mem0' for cloud-based
|
|
28
|
+
semantic memory storage via the Mem0 AI service (mem0ai package).
|
|
29
|
+
|
|
30
|
+
Requires: pip install mem0ai
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
api_key: str | None = Field(
|
|
34
|
+
default=None,
|
|
35
|
+
description="Mem0 API key for authentication (required when backend='mem0')",
|
|
36
|
+
)
|
|
37
|
+
user_id: str | None = Field(
|
|
38
|
+
default=None,
|
|
39
|
+
description="Default user ID for memories (optional, defaults to 'default')",
|
|
40
|
+
)
|
|
41
|
+
org_id: str | None = Field(
|
|
42
|
+
default=None,
|
|
43
|
+
description="Organization ID for multi-tenant use (optional)",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class MemUConfig(BaseModel):
|
|
48
|
+
"""MemU backend configuration.
|
|
49
|
+
|
|
50
|
+
Configure this section when using backend: 'memu' for structured
|
|
51
|
+
memory storage via the MemU SDK (NevaMind-AI/memU via memu-py).
|
|
52
|
+
|
|
53
|
+
Requires: pip install memu-py
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
database_type: str = Field(
|
|
57
|
+
default="inmemory",
|
|
58
|
+
description="Database type: 'inmemory', 'sqlite', or 'postgres'",
|
|
59
|
+
)
|
|
60
|
+
database_url: str | None = Field(
|
|
61
|
+
default=None,
|
|
62
|
+
description="Database connection URL (for sqlite/postgres)",
|
|
63
|
+
)
|
|
64
|
+
llm_api_key: str | None = Field(
|
|
65
|
+
default=None,
|
|
66
|
+
description="LLM API key for embeddings (optional, uses OpenAI by default)",
|
|
67
|
+
)
|
|
68
|
+
llm_base_url: str | None = Field(
|
|
69
|
+
default=None,
|
|
70
|
+
description="LLM API base URL (optional)",
|
|
71
|
+
)
|
|
72
|
+
user_id: str | None = Field(
|
|
73
|
+
default=None,
|
|
74
|
+
description="Default user ID for memories (optional)",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class OpenMemoryConfig(BaseModel):
|
|
79
|
+
"""OpenMemory backend configuration.
|
|
80
|
+
|
|
81
|
+
Configure this section when using backend: 'openmemory' for self-hosted
|
|
82
|
+
embedding-based memory storage via the OpenMemory REST API.
|
|
83
|
+
|
|
84
|
+
OpenMemory provides semantic search over memories using local embeddings.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
base_url: str = Field(
|
|
88
|
+
default="http://localhost:8080",
|
|
89
|
+
description="OpenMemory server base URL (required when backend='openmemory')",
|
|
90
|
+
)
|
|
91
|
+
api_key: str | None = Field(
|
|
92
|
+
default=None,
|
|
93
|
+
description="Optional API key for authentication",
|
|
94
|
+
)
|
|
95
|
+
user_id: str | None = Field(
|
|
96
|
+
default=None,
|
|
97
|
+
description="Default user ID for memories (optional, defaults to 'default')",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@field_validator("base_url")
|
|
101
|
+
@classmethod
|
|
102
|
+
def validate_url(cls, v: str) -> str:
|
|
103
|
+
"""Validate base_url is a valid URL format."""
|
|
104
|
+
if not v.startswith(("http://", "https://")):
|
|
105
|
+
raise ValueError("base_url must start with http:// or https://")
|
|
106
|
+
# Remove trailing slash for consistency
|
|
107
|
+
return v.rstrip("/")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class MemoryConfig(BaseModel):
|
|
111
|
+
"""Memory system configuration."""
|
|
112
|
+
|
|
113
|
+
enabled: bool = Field(
|
|
114
|
+
default=True,
|
|
115
|
+
description="Enable persistent memory system",
|
|
116
|
+
)
|
|
117
|
+
backend: str = Field(
|
|
118
|
+
default="sqlite",
|
|
119
|
+
description=(
|
|
120
|
+
"Storage backend for memories. Options: "
|
|
121
|
+
"'sqlite' (default, local SQLite database), "
|
|
122
|
+
"'mem0' (Mem0 cloud-based semantic memory via mem0ai), "
|
|
123
|
+
"'memu' (MemU structured memory via memu-py), "
|
|
124
|
+
"'openmemory' (self-hosted OpenMemory REST API), "
|
|
125
|
+
"'null' (no persistence, for testing)"
|
|
126
|
+
),
|
|
127
|
+
)
|
|
128
|
+
mem0: Mem0Config = Field(
|
|
129
|
+
default_factory=Mem0Config,
|
|
130
|
+
description="Mem0 backend configuration (only used when backend='mem0')",
|
|
131
|
+
)
|
|
132
|
+
memu: MemUConfig = Field(
|
|
133
|
+
default_factory=MemUConfig,
|
|
134
|
+
description="MemU backend configuration (only used when backend='memu')",
|
|
135
|
+
)
|
|
136
|
+
openmemory: OpenMemoryConfig = Field(
|
|
137
|
+
default_factory=OpenMemoryConfig,
|
|
138
|
+
description="OpenMemory backend configuration (only used when backend='openmemory')",
|
|
139
|
+
)
|
|
140
|
+
importance_threshold: float = Field(
|
|
141
|
+
default=0.7,
|
|
142
|
+
description="Minimum importance score for memory injection",
|
|
143
|
+
)
|
|
144
|
+
decay_enabled: bool = Field(
|
|
145
|
+
default=True,
|
|
146
|
+
description="Enable memory importance decay over time",
|
|
147
|
+
)
|
|
148
|
+
decay_rate: float = Field(
|
|
149
|
+
default=0.05,
|
|
150
|
+
description="Importance decay rate per month",
|
|
151
|
+
)
|
|
152
|
+
decay_floor: float = Field(
|
|
153
|
+
default=0.1,
|
|
154
|
+
description="Minimum importance score after decay",
|
|
155
|
+
)
|
|
156
|
+
search_backend: str = Field(
|
|
157
|
+
default="tfidf",
|
|
158
|
+
description=(
|
|
159
|
+
"Search backend for memory recall. Options: "
|
|
160
|
+
"'tfidf' (default, zero-dependency local search), "
|
|
161
|
+
"'text' (simple substring matching)"
|
|
162
|
+
),
|
|
163
|
+
)
|
|
164
|
+
auto_crossref: bool = Field(
|
|
165
|
+
default=False,
|
|
166
|
+
description="Automatically create cross-references between similar memories",
|
|
167
|
+
)
|
|
168
|
+
crossref_threshold: float = Field(
|
|
169
|
+
default=0.3,
|
|
170
|
+
description="Minimum similarity score to create a cross-reference (0.0-1.0)",
|
|
171
|
+
)
|
|
172
|
+
crossref_max_links: int = Field(
|
|
173
|
+
default=5,
|
|
174
|
+
description="Maximum number of cross-references to create per memory",
|
|
175
|
+
)
|
|
176
|
+
access_debounce_seconds: int = Field(
|
|
177
|
+
default=60,
|
|
178
|
+
description="Minimum seconds between access stat updates for the same memory",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
@field_validator("importance_threshold", "decay_rate", "decay_floor", "crossref_threshold")
|
|
182
|
+
@classmethod
|
|
183
|
+
def validate_probability(cls, v: float) -> float:
|
|
184
|
+
"""Validate value is between 0.0 and 1.0."""
|
|
185
|
+
if not (0.0 <= v <= 1.0):
|
|
186
|
+
raise ValueError("Value must be between 0.0 and 1.0")
|
|
187
|
+
return v
|
|
188
|
+
|
|
189
|
+
@field_validator("crossref_max_links")
|
|
190
|
+
@classmethod
|
|
191
|
+
def validate_positive_links(cls, v: int) -> int:
|
|
192
|
+
"""Validate crossref_max_links is positive."""
|
|
193
|
+
if v < 1:
|
|
194
|
+
raise ValueError("crossref_max_links must be at least 1")
|
|
195
|
+
return v
|
|
196
|
+
|
|
197
|
+
@field_validator("search_backend")
|
|
198
|
+
@classmethod
|
|
199
|
+
def validate_search_backend(cls, v: str) -> str:
|
|
200
|
+
"""Validate search_backend is a supported option."""
|
|
201
|
+
valid_backends = {"tfidf", "text"}
|
|
202
|
+
if v not in valid_backends:
|
|
203
|
+
raise ValueError(
|
|
204
|
+
f"Invalid search_backend '{v}'. Must be one of: {sorted(valid_backends)}"
|
|
205
|
+
)
|
|
206
|
+
return v
|
|
207
|
+
|
|
208
|
+
@field_validator("backend")
|
|
209
|
+
@classmethod
|
|
210
|
+
def validate_backend(cls, v: str) -> str:
|
|
211
|
+
"""Validate backend is a supported storage option."""
|
|
212
|
+
valid_backends = {"sqlite", "mem0", "memu", "openmemory", "null"}
|
|
213
|
+
if v not in valid_backends:
|
|
214
|
+
raise ValueError(f"Invalid backend '{v}'. Must be one of: {sorted(valid_backends)}")
|
|
215
|
+
return v
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class MemorySyncConfig(BaseModel):
|
|
219
|
+
"""Memory backup configuration (filesystem export).
|
|
220
|
+
|
|
221
|
+
Note: This was previously named for "sync" but is actually a backup mechanism.
|
|
222
|
+
Memories are stored in the database via MemoryBackendProtocol; this config
|
|
223
|
+
controls the JSONL backup file export (for disaster recovery/migration).
|
|
224
|
+
|
|
225
|
+
TODO: Consider renaming to MemoryBackupConfig in a future breaking change.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
enabled: bool = Field(
|
|
229
|
+
default=True,
|
|
230
|
+
description="Enable memory synchronization to filesystem",
|
|
231
|
+
)
|
|
232
|
+
export_debounce: float = Field(
|
|
233
|
+
default=5.0,
|
|
234
|
+
description="Seconds to wait before exporting after a change",
|
|
235
|
+
)
|
|
236
|
+
export_path: Path = Field(
|
|
237
|
+
default=Path(".gobby/memories.jsonl"),
|
|
238
|
+
description="Path to the memories export file (relative to project root or absolute)",
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
@field_validator("export_debounce")
|
|
242
|
+
@classmethod
|
|
243
|
+
def validate_positive(cls, v: float) -> float:
|
|
244
|
+
"""Validate value is non-negative."""
|
|
245
|
+
if v < 0:
|
|
246
|
+
raise ValueError("Value must be non-negative")
|
|
247
|
+
return v
|
gobby/config/servers.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Server configuration module.
|
|
3
|
+
|
|
4
|
+
Contains server and networking Pydantic config models:
|
|
5
|
+
- WebSocketSettings: WebSocket server settings (port, ping interval)
|
|
6
|
+
- MCPClientProxyConfig: MCP proxy settings (timeouts, embeddings, search mode)
|
|
7
|
+
|
|
8
|
+
Extracted from app.py using Strangler Fig pattern for code decomposition.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Literal
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field, field_validator
|
|
14
|
+
|
|
15
|
+
__all__ = ["WebSocketSettings", "MCPClientProxyConfig"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class WebSocketSettings(BaseModel):
|
|
19
|
+
"""WebSocket server configuration."""
|
|
20
|
+
|
|
21
|
+
enabled: bool = Field(
|
|
22
|
+
default=True,
|
|
23
|
+
description="Enable WebSocket server for real-time communication",
|
|
24
|
+
)
|
|
25
|
+
port: int = Field(
|
|
26
|
+
default=8766,
|
|
27
|
+
description="Port for WebSocket server to listen on",
|
|
28
|
+
)
|
|
29
|
+
ping_interval: int = Field(
|
|
30
|
+
default=30,
|
|
31
|
+
description="Ping interval in seconds for keepalive",
|
|
32
|
+
)
|
|
33
|
+
ping_timeout: int = Field(
|
|
34
|
+
default=10,
|
|
35
|
+
description="Pong timeout in seconds before considering connection dead",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@field_validator("port")
|
|
39
|
+
@classmethod
|
|
40
|
+
def validate_port(cls, v: int) -> int:
|
|
41
|
+
"""Validate port number is in valid range."""
|
|
42
|
+
if not (1024 <= v <= 65535):
|
|
43
|
+
raise ValueError("Port must be between 1024 and 65535")
|
|
44
|
+
return v
|
|
45
|
+
|
|
46
|
+
@field_validator("ping_interval", "ping_timeout")
|
|
47
|
+
@classmethod
|
|
48
|
+
def validate_positive(cls, v: int) -> int:
|
|
49
|
+
"""Validate value is positive."""
|
|
50
|
+
if v <= 0:
|
|
51
|
+
raise ValueError("Value must be positive")
|
|
52
|
+
return v
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class MCPClientProxyConfig(BaseModel):
|
|
56
|
+
"""MCP client proxy configuration for downstream MCP servers."""
|
|
57
|
+
|
|
58
|
+
enabled: bool = Field(
|
|
59
|
+
default=True,
|
|
60
|
+
description="Enable MCP client proxy for downstream MCP servers",
|
|
61
|
+
)
|
|
62
|
+
connect_timeout: float = Field(
|
|
63
|
+
default=30.0,
|
|
64
|
+
description="Timeout in seconds for establishing connections to MCP servers",
|
|
65
|
+
)
|
|
66
|
+
proxy_timeout: int = Field(
|
|
67
|
+
default=30,
|
|
68
|
+
description="Timeout in seconds for proxy calls to downstream MCP servers",
|
|
69
|
+
)
|
|
70
|
+
tool_timeout: int = Field(
|
|
71
|
+
default=30,
|
|
72
|
+
description="Timeout in seconds for tool schema operations",
|
|
73
|
+
)
|
|
74
|
+
tool_timeouts: dict[str, float] = Field(
|
|
75
|
+
default_factory=dict,
|
|
76
|
+
description="Map of tool names to specific timeouts in seconds",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Semantic search and embeddings
|
|
80
|
+
search_mode: Literal["llm", "semantic", "hybrid"] = Field(
|
|
81
|
+
default="llm",
|
|
82
|
+
description="Default search mode for tool recommendations: 'llm' (LLM-based), 'semantic' (embedding similarity), 'hybrid' (both)",
|
|
83
|
+
)
|
|
84
|
+
embedding_provider: str = Field(
|
|
85
|
+
default="openai",
|
|
86
|
+
description="Provider for embedding generation (openai, litellm)",
|
|
87
|
+
)
|
|
88
|
+
embedding_model: str = Field(
|
|
89
|
+
default="text-embedding-3-small",
|
|
90
|
+
description="Model to use for tool embedding generation",
|
|
91
|
+
)
|
|
92
|
+
min_similarity: float = Field(
|
|
93
|
+
default=0.3,
|
|
94
|
+
description="Minimum similarity threshold for semantic search results (0.0-1.0)",
|
|
95
|
+
)
|
|
96
|
+
top_k: int = Field(
|
|
97
|
+
default=10,
|
|
98
|
+
description="Default number of results to return for semantic search",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Refresh settings
|
|
102
|
+
refresh_on_server_add: bool = Field(
|
|
103
|
+
default=True,
|
|
104
|
+
description="Automatically refresh tool embeddings when adding a new MCP server",
|
|
105
|
+
)
|
|
106
|
+
refresh_timeout: float = Field(
|
|
107
|
+
default=300.0,
|
|
108
|
+
description="Timeout in seconds for tool refresh operations (embedding generation)",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
@field_validator("connect_timeout", "refresh_timeout")
|
|
112
|
+
@classmethod
|
|
113
|
+
def validate_connect_timeout(cls, v: float) -> float:
|
|
114
|
+
"""Validate timeout is positive."""
|
|
115
|
+
if v <= 0:
|
|
116
|
+
raise ValueError("Timeout must be positive")
|
|
117
|
+
return v
|
|
118
|
+
|
|
119
|
+
@field_validator("proxy_timeout", "tool_timeout")
|
|
120
|
+
@classmethod
|
|
121
|
+
def validate_timeout(cls, v: int) -> int:
|
|
122
|
+
"""Validate timeout is positive."""
|
|
123
|
+
if v <= 0:
|
|
124
|
+
raise ValueError("Timeout must be positive")
|
|
125
|
+
return v
|
|
126
|
+
|
|
127
|
+
@field_validator("min_similarity")
|
|
128
|
+
@classmethod
|
|
129
|
+
def validate_min_similarity(cls, v: float) -> float:
|
|
130
|
+
"""Validate min_similarity is between 0 and 1."""
|
|
131
|
+
if not 0.0 <= v <= 1.0:
|
|
132
|
+
raise ValueError("min_similarity must be between 0.0 and 1.0")
|
|
133
|
+
return v
|
|
134
|
+
|
|
135
|
+
@field_validator("top_k")
|
|
136
|
+
@classmethod
|
|
137
|
+
def validate_top_k(cls, v: int) -> int:
|
|
138
|
+
"""Validate top_k is positive."""
|
|
139
|
+
if v <= 0:
|
|
140
|
+
raise ValueError("top_k must be positive")
|
|
141
|
+
return v
|
gobby/config/sessions.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session configuration module.
|
|
3
|
+
|
|
4
|
+
Contains session-related Pydantic config models:
|
|
5
|
+
- ContextInjectionConfig: Subagent context injection settings
|
|
6
|
+
- SessionSummaryConfig: Session summary generation settings
|
|
7
|
+
- TitleSynthesisConfig: Session title synthesis settings
|
|
8
|
+
- MessageTrackingConfig: Session message tracking settings
|
|
9
|
+
- SessionLifecycleConfig: Session lifecycle management settings
|
|
10
|
+
|
|
11
|
+
Extracted from app.py using Strangler Fig pattern for code decomposition.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, Field, field_validator
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ArtifactHandoffConfig",
|
|
18
|
+
"ContextInjectionConfig",
|
|
19
|
+
"SessionSummaryConfig",
|
|
20
|
+
"TitleSynthesisConfig",
|
|
21
|
+
"MessageTrackingConfig",
|
|
22
|
+
"SessionLifecycleConfig",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ArtifactHandoffConfig(BaseModel):
|
|
27
|
+
"""Configuration for artifact inclusion in session handoffs.
|
|
28
|
+
|
|
29
|
+
Controls how artifacts are collected and formatted when generating
|
|
30
|
+
handoff context for session continuation.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
max_artifacts_in_handoff: int = Field(
|
|
34
|
+
default=10,
|
|
35
|
+
description="Maximum number of artifacts to include in handoff context",
|
|
36
|
+
)
|
|
37
|
+
max_context_size: int = Field(
|
|
38
|
+
default=50000,
|
|
39
|
+
description="Maximum size in characters for handoff context",
|
|
40
|
+
)
|
|
41
|
+
include_parent_artifacts: bool = Field(
|
|
42
|
+
default=True,
|
|
43
|
+
description="Include artifacts from parent session in handoff",
|
|
44
|
+
)
|
|
45
|
+
max_lineage_depth: int = Field(
|
|
46
|
+
default=3,
|
|
47
|
+
description="Maximum depth to traverse session lineage for artifacts",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@field_validator("max_artifacts_in_handoff", "max_context_size", "max_lineage_depth")
|
|
51
|
+
@classmethod
|
|
52
|
+
def validate_positive(cls, v: int) -> int:
|
|
53
|
+
"""Validate value is positive."""
|
|
54
|
+
if v <= 0:
|
|
55
|
+
raise ValueError("Value must be positive")
|
|
56
|
+
return v
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ContextInjectionConfig(BaseModel):
|
|
60
|
+
"""Context injection configuration for subagent spawning.
|
|
61
|
+
|
|
62
|
+
Controls how context is resolved and injected into subagent prompts.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
enabled: bool = Field(
|
|
66
|
+
default=True,
|
|
67
|
+
description="Enable context injection for subagents",
|
|
68
|
+
)
|
|
69
|
+
default_source: str = Field(
|
|
70
|
+
default="summary_markdown",
|
|
71
|
+
description="Default context source when not specified. "
|
|
72
|
+
"Options: summary_markdown, compact_markdown, session_id:<id>, "
|
|
73
|
+
"transcript:<n>, file:<path>",
|
|
74
|
+
)
|
|
75
|
+
max_file_size: int = Field(
|
|
76
|
+
default=51200,
|
|
77
|
+
description="Maximum file size in bytes for file: source (default: 50KB)",
|
|
78
|
+
)
|
|
79
|
+
max_content_size: int = Field(
|
|
80
|
+
default=51200,
|
|
81
|
+
description="Maximum content size in bytes for all sources (default: 50KB)",
|
|
82
|
+
)
|
|
83
|
+
max_transcript_messages: int = Field(
|
|
84
|
+
default=100,
|
|
85
|
+
description="Maximum number of messages for transcript: source",
|
|
86
|
+
)
|
|
87
|
+
truncation_suffix: str = Field(
|
|
88
|
+
default="\n\n[truncated: {bytes} bytes remaining]",
|
|
89
|
+
description="Suffix template appended when content is truncated",
|
|
90
|
+
)
|
|
91
|
+
context_template: str | None = Field(
|
|
92
|
+
default=None,
|
|
93
|
+
description="Custom template for context injection. "
|
|
94
|
+
"Use {{ context }} and {{ prompt }} placeholders. "
|
|
95
|
+
"If None, uses the default template.",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
@field_validator("max_file_size", "max_content_size", "max_transcript_messages")
|
|
99
|
+
@classmethod
|
|
100
|
+
def validate_positive(cls, v: int) -> int:
|
|
101
|
+
"""Validate value is positive."""
|
|
102
|
+
if v <= 0:
|
|
103
|
+
raise ValueError("Value must be positive")
|
|
104
|
+
return v
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class SessionSummaryConfig(BaseModel):
|
|
108
|
+
"""Session summary generation configuration."""
|
|
109
|
+
|
|
110
|
+
enabled: bool = Field(
|
|
111
|
+
default=True,
|
|
112
|
+
description="Enable LLM-based session summary generation",
|
|
113
|
+
)
|
|
114
|
+
provider: str = Field(
|
|
115
|
+
default="claude",
|
|
116
|
+
description="LLM provider to use for session summary",
|
|
117
|
+
)
|
|
118
|
+
model: str = Field(
|
|
119
|
+
default="claude-haiku-4-5",
|
|
120
|
+
description="Model to use for session summary generation",
|
|
121
|
+
)
|
|
122
|
+
prompt: str = Field(
|
|
123
|
+
default="""Generate a concise session summary for handoff to another agent or future session.
|
|
124
|
+
|
|
125
|
+
## Session Context
|
|
126
|
+
Transcript Summary:
|
|
127
|
+
{transcript_summary}
|
|
128
|
+
|
|
129
|
+
Git Status:
|
|
130
|
+
{git_status}
|
|
131
|
+
|
|
132
|
+
File Changes:
|
|
133
|
+
{file_changes}
|
|
134
|
+
|
|
135
|
+
{todo_list}
|
|
136
|
+
|
|
137
|
+
{session_tasks}
|
|
138
|
+
|
|
139
|
+
## Instructions
|
|
140
|
+
Create a summary with these sections:
|
|
141
|
+
1. **What was accomplished** - Key completions in 2-3 bullet points
|
|
142
|
+
2. **Current state** - What's in progress or pending
|
|
143
|
+
3. **Next steps** - Clear actionable items for continuation
|
|
144
|
+
|
|
145
|
+
Be concise. Focus on what the next agent needs to know to continue effectively.""",
|
|
146
|
+
description="Prompt template for session summary (use placeholders: {transcript_summary}, {git_status}, {file_changes}, {todo_list}, {session_tasks})",
|
|
147
|
+
)
|
|
148
|
+
summary_file_path: str = Field(
|
|
149
|
+
default="~/.gobby/session_summaries",
|
|
150
|
+
description="Directory path for session summary markdown files",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class TitleSynthesisConfig(BaseModel):
|
|
155
|
+
"""Title synthesis configuration."""
|
|
156
|
+
|
|
157
|
+
enabled: bool = Field(
|
|
158
|
+
default=True,
|
|
159
|
+
description="Enable title synthesis for sessions",
|
|
160
|
+
)
|
|
161
|
+
provider: str = Field(
|
|
162
|
+
default="claude",
|
|
163
|
+
description="LLM provider to use for title synthesis",
|
|
164
|
+
)
|
|
165
|
+
model: str = Field(
|
|
166
|
+
default="claude-haiku-4-5",
|
|
167
|
+
description="Model to use for title synthesis",
|
|
168
|
+
)
|
|
169
|
+
prompt: str | None = Field(
|
|
170
|
+
default=None,
|
|
171
|
+
description="Custom prompt template for title synthesis",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class MessageTrackingConfig(BaseModel):
|
|
176
|
+
"""Configuration for session message tracking."""
|
|
177
|
+
|
|
178
|
+
enabled: bool = Field(
|
|
179
|
+
default=True,
|
|
180
|
+
description="Enable session message tracking",
|
|
181
|
+
)
|
|
182
|
+
poll_interval: float = Field(
|
|
183
|
+
default=5.0,
|
|
184
|
+
description="Polling interval in seconds for transcript updates",
|
|
185
|
+
)
|
|
186
|
+
debounce_delay: float = Field(
|
|
187
|
+
default=1.0,
|
|
188
|
+
description="Debounce delay in seconds for message processing",
|
|
189
|
+
)
|
|
190
|
+
max_message_length: int = Field(
|
|
191
|
+
default=10000,
|
|
192
|
+
description="Maximum length of a single message content",
|
|
193
|
+
)
|
|
194
|
+
broadcast_enabled: bool = Field(
|
|
195
|
+
default=True,
|
|
196
|
+
description="Enable broadcasting message events",
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
@field_validator("poll_interval", "debounce_delay")
|
|
200
|
+
@classmethod
|
|
201
|
+
def validate_positive(cls, v: float) -> float:
|
|
202
|
+
"""Validate value is positive."""
|
|
203
|
+
if v <= 0:
|
|
204
|
+
raise ValueError("Value must be positive")
|
|
205
|
+
return v
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class SessionLifecycleConfig(BaseModel):
|
|
209
|
+
"""Configuration for session lifecycle management.
|
|
210
|
+
|
|
211
|
+
Handles:
|
|
212
|
+
- Pausing active sessions with no recent activity
|
|
213
|
+
- Expiring stale sessions (active/paused for too long)
|
|
214
|
+
- Background transcript processing for expired sessions
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
active_session_pause_minutes: int = Field(
|
|
218
|
+
default=30,
|
|
219
|
+
description="Minutes of inactivity before active sessions are marked paused",
|
|
220
|
+
)
|
|
221
|
+
stale_session_timeout_hours: int = Field(
|
|
222
|
+
default=24,
|
|
223
|
+
description="Hours after which inactive sessions are marked expired",
|
|
224
|
+
)
|
|
225
|
+
expire_check_interval_minutes: int = Field(
|
|
226
|
+
default=60,
|
|
227
|
+
description="How often to check for stale sessions (minutes)",
|
|
228
|
+
)
|
|
229
|
+
transcript_processing_interval_minutes: int = Field(
|
|
230
|
+
default=5,
|
|
231
|
+
description="How often to process pending transcripts (minutes)",
|
|
232
|
+
)
|
|
233
|
+
transcript_processing_batch_size: int = Field(
|
|
234
|
+
default=10,
|
|
235
|
+
description="Maximum sessions to process per batch",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
@field_validator(
|
|
239
|
+
"active_session_pause_minutes",
|
|
240
|
+
"stale_session_timeout_hours",
|
|
241
|
+
"expire_check_interval_minutes",
|
|
242
|
+
"transcript_processing_interval_minutes",
|
|
243
|
+
"transcript_processing_batch_size",
|
|
244
|
+
)
|
|
245
|
+
@classmethod
|
|
246
|
+
def validate_positive(cls, v: int) -> int:
|
|
247
|
+
"""Validate value is positive."""
|
|
248
|
+
if v <= 0:
|
|
249
|
+
raise ValueError("Value must be positive")
|
|
250
|
+
return v
|