htmlgraph 0.9.3__py3-none-any.whl → 0.27.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.
- htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/.htmlgraph/agents.json +72 -0
- htmlgraph/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/__init__.py +173 -17
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +127 -0
- htmlgraph/agent_registry.py +45 -30
- htmlgraph/agents.py +160 -107
- htmlgraph/analytics/__init__.py +9 -2
- htmlgraph/analytics/cli.py +190 -51
- htmlgraph/analytics/cost_analyzer.py +391 -0
- htmlgraph/analytics/cost_monitor.py +664 -0
- htmlgraph/analytics/cost_reporter.py +675 -0
- htmlgraph/analytics/cross_session.py +617 -0
- htmlgraph/analytics/dependency.py +192 -100
- htmlgraph/analytics/pattern_learning.py +771 -0
- htmlgraph/analytics/session_graph.py +707 -0
- htmlgraph/analytics/strategic/__init__.py +80 -0
- htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
- htmlgraph/analytics/strategic/pattern_detector.py +876 -0
- htmlgraph/analytics/strategic/preference_manager.py +709 -0
- htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
- htmlgraph/analytics/work_type.py +190 -14
- htmlgraph/analytics_index.py +135 -51
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/cost_alerts_websocket.py +416 -0
- htmlgraph/api/main.py +2498 -0
- htmlgraph/api/static/htmx.min.js +1 -0
- htmlgraph/api/static/style-redesign.css +1344 -0
- htmlgraph/api/static/style.css +1079 -0
- htmlgraph/api/templates/dashboard-redesign.html +1366 -0
- htmlgraph/api/templates/dashboard.html +794 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +1100 -0
- htmlgraph/api/templates/partials/agents-redesign.html +317 -0
- htmlgraph/api/templates/partials/agents.html +317 -0
- htmlgraph/api/templates/partials/event-traces.html +373 -0
- htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
- htmlgraph/api/templates/partials/features.html +578 -0
- htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
- htmlgraph/api/templates/partials/metrics.html +346 -0
- htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
- htmlgraph/api/templates/partials/orchestration.html +198 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/api/templates/partials/work-items.html +613 -0
- htmlgraph/api/websocket.py +538 -0
- htmlgraph/archive/__init__.py +24 -0
- htmlgraph/archive/bloom.py +234 -0
- htmlgraph/archive/fts.py +297 -0
- htmlgraph/archive/manager.py +583 -0
- htmlgraph/archive/search.py +244 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/attribute_index.py +208 -0
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/__init__.py +14 -0
- htmlgraph/builders/base.py +118 -29
- htmlgraph/builders/bug.py +150 -0
- htmlgraph/builders/chore.py +119 -0
- htmlgraph/builders/epic.py +150 -0
- htmlgraph/builders/feature.py +31 -6
- htmlgraph/builders/insight.py +195 -0
- htmlgraph/builders/metric.py +217 -0
- htmlgraph/builders/pattern.py +202 -0
- htmlgraph/builders/phase.py +162 -0
- htmlgraph/builders/spike.py +52 -19
- htmlgraph/builders/track.py +148 -72
- htmlgraph/cigs/__init__.py +81 -0
- htmlgraph/cigs/autonomy.py +385 -0
- htmlgraph/cigs/cost.py +475 -0
- htmlgraph/cigs/messages_basic.py +472 -0
- htmlgraph/cigs/messaging.py +365 -0
- htmlgraph/cigs/models.py +771 -0
- htmlgraph/cigs/pattern_storage.py +427 -0
- htmlgraph/cigs/patterns.py +503 -0
- htmlgraph/cigs/posttool_analyzer.py +234 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cigs/tracker.py +317 -0
- htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/cli/.htmlgraph/agents.json +72 -0
- htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/cli/__init__.py +42 -0
- htmlgraph/cli/__main__.py +6 -0
- htmlgraph/cli/analytics.py +1424 -0
- htmlgraph/cli/base.py +685 -0
- htmlgraph/cli/constants.py +206 -0
- htmlgraph/cli/core.py +954 -0
- htmlgraph/cli/main.py +147 -0
- htmlgraph/cli/models.py +475 -0
- htmlgraph/cli/templates/__init__.py +1 -0
- htmlgraph/cli/templates/cost_dashboard.py +399 -0
- htmlgraph/cli/work/__init__.py +239 -0
- htmlgraph/cli/work/browse.py +115 -0
- htmlgraph/cli/work/features.py +568 -0
- htmlgraph/cli/work/orchestration.py +676 -0
- htmlgraph/cli/work/report.py +728 -0
- htmlgraph/cli/work/sessions.py +466 -0
- htmlgraph/cli/work/snapshot.py +559 -0
- htmlgraph/cli/work/tracks.py +486 -0
- htmlgraph/cli_commands/__init__.py +1 -0
- htmlgraph/cli_commands/feature.py +195 -0
- htmlgraph/cli_framework.py +115 -0
- htmlgraph/collections/__init__.py +18 -0
- htmlgraph/collections/base.py +415 -98
- htmlgraph/collections/bug.py +53 -0
- htmlgraph/collections/chore.py +53 -0
- htmlgraph/collections/epic.py +53 -0
- htmlgraph/collections/feature.py +12 -26
- htmlgraph/collections/insight.py +100 -0
- htmlgraph/collections/metric.py +92 -0
- htmlgraph/collections/pattern.py +97 -0
- htmlgraph/collections/phase.py +53 -0
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +56 -16
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +511 -0
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +344 -0
- htmlgraph/converter.py +216 -28
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2406 -307
- htmlgraph/dashboard.html.backup +6592 -0
- htmlgraph/dashboard.html.bak +7181 -0
- htmlgraph/dashboard.html.bak2 +7231 -0
- htmlgraph/dashboard.html.bak3 +7232 -0
- htmlgraph/db/__init__.py +38 -0
- htmlgraph/db/queries.py +790 -0
- htmlgraph/db/schema.py +1788 -0
- htmlgraph/decorators.py +317 -0
- htmlgraph/dependency_models.py +19 -2
- htmlgraph/deploy.py +142 -125
- htmlgraph/deployment_models.py +474 -0
- htmlgraph/docs/API_REFERENCE.md +841 -0
- htmlgraph/docs/HTTP_API.md +750 -0
- htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
- htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
- htmlgraph/docs/README.md +532 -0
- htmlgraph/docs/__init__.py +77 -0
- htmlgraph/docs/docs_version.py +55 -0
- htmlgraph/docs/metadata.py +93 -0
- htmlgraph/docs/migrations.py +232 -0
- htmlgraph/docs/template_engine.py +143 -0
- htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
- htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
- htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
- htmlgraph/docs/templates/base_agents.md.j2 +78 -0
- htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
- htmlgraph/docs/version_check.py +163 -0
- htmlgraph/edge_index.py +182 -27
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +100 -52
- htmlgraph/event_migration.py +13 -4
- htmlgraph/exceptions.py +49 -0
- htmlgraph/file_watcher.py +101 -28
- htmlgraph/find_api.py +75 -63
- htmlgraph/git_events.py +145 -63
- htmlgraph/graph.py +1122 -106
- htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/hooks/.htmlgraph/agents.json +72 -0
- htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
- htmlgraph/hooks/__init__.py +45 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
- htmlgraph/hooks/concurrent_sessions.py +208 -0
- htmlgraph/hooks/context.py +350 -0
- htmlgraph/hooks/drift_handler.py +525 -0
- htmlgraph/hooks/event_tracker.py +1314 -0
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/hooks-config.example.json +12 -0
- htmlgraph/hooks/installer.py +343 -0
- htmlgraph/hooks/orchestrator.py +674 -0
- htmlgraph/hooks/orchestrator_reflector.py +223 -0
- htmlgraph/hooks/post-checkout.sh +28 -0
- htmlgraph/hooks/post-commit.sh +24 -0
- htmlgraph/hooks/post-merge.sh +26 -0
- htmlgraph/hooks/post_tool_use_failure.py +273 -0
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +408 -0
- htmlgraph/hooks/pre-commit.sh +94 -0
- htmlgraph/hooks/pre-push.sh +28 -0
- htmlgraph/hooks/pretooluse.py +819 -0
- htmlgraph/hooks/prompt_analyzer.py +637 -0
- htmlgraph/hooks/session_handler.py +668 -0
- htmlgraph/hooks/session_summary.py +395 -0
- htmlgraph/hooks/state_manager.py +504 -0
- htmlgraph/hooks/subagent_detection.py +202 -0
- htmlgraph/hooks/subagent_stop.py +369 -0
- htmlgraph/hooks/task_enforcer.py +255 -0
- htmlgraph/hooks/task_validator.py +177 -0
- htmlgraph/hooks/validator.py +628 -0
- htmlgraph/ids.py +41 -27
- htmlgraph/index.d.ts +286 -0
- htmlgraph/learning.py +767 -0
- htmlgraph/mcp_server.py +69 -23
- htmlgraph/models.py +1586 -87
- htmlgraph/operations/README.md +62 -0
- htmlgraph/operations/__init__.py +79 -0
- htmlgraph/operations/analytics.py +339 -0
- htmlgraph/operations/bootstrap.py +289 -0
- htmlgraph/operations/events.py +244 -0
- htmlgraph/operations/fastapi_server.py +231 -0
- htmlgraph/operations/hooks.py +350 -0
- htmlgraph/operations/initialization.py +597 -0
- htmlgraph/operations/initialization.py.backup +228 -0
- htmlgraph/operations/server.py +303 -0
- htmlgraph/orchestration/__init__.py +58 -0
- htmlgraph/orchestration/claude_launcher.py +179 -0
- htmlgraph/orchestration/command_builder.py +72 -0
- htmlgraph/orchestration/headless_spawner.py +281 -0
- htmlgraph/orchestration/live_events.py +377 -0
- htmlgraph/orchestration/model_selection.py +327 -0
- htmlgraph/orchestration/plugin_manager.py +140 -0
- htmlgraph/orchestration/prompts.py +137 -0
- htmlgraph/orchestration/spawner_event_tracker.py +383 -0
- htmlgraph/orchestration/spawners/__init__.py +16 -0
- htmlgraph/orchestration/spawners/base.py +194 -0
- htmlgraph/orchestration/spawners/claude.py +173 -0
- htmlgraph/orchestration/spawners/codex.py +435 -0
- htmlgraph/orchestration/spawners/copilot.py +294 -0
- htmlgraph/orchestration/spawners/gemini.py +471 -0
- htmlgraph/orchestration/subprocess_runner.py +36 -0
- htmlgraph/orchestration/task_coordination.py +343 -0
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +669 -0
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +328 -0
- htmlgraph/orchestrator_validator.py +133 -0
- htmlgraph/parallel.py +646 -0
- htmlgraph/parser.py +160 -35
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/planning.py +147 -52
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +109 -72
- htmlgraph/query_composer.py +509 -0
- htmlgraph/reflection.py +443 -0
- htmlgraph/refs.py +344 -0
- htmlgraph/repo_hash.py +512 -0
- htmlgraph/repositories/__init__.py +292 -0
- htmlgraph/repositories/analytics_repository.py +455 -0
- htmlgraph/repositories/analytics_repository_standard.py +628 -0
- htmlgraph/repositories/feature_repository.py +581 -0
- htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
- htmlgraph/repositories/feature_repository_memory.py +607 -0
- htmlgraph/repositories/feature_repository_sqlite.py +858 -0
- htmlgraph/repositories/filter_service.py +620 -0
- htmlgraph/repositories/filter_service_standard.py +445 -0
- htmlgraph/repositories/shared_cache.py +621 -0
- htmlgraph/repositories/shared_cache_memory.py +395 -0
- htmlgraph/repositories/track_repository.py +552 -0
- htmlgraph/repositories/track_repository_htmlfile.py +619 -0
- htmlgraph/repositories/track_repository_memory.py +508 -0
- htmlgraph/repositories/track_repository_sqlite.py +711 -0
- htmlgraph/routing.py +8 -19
- htmlgraph/scripts/deploy.py +1 -2
- htmlgraph/sdk/__init__.py +398 -0
- htmlgraph/sdk/__init__.pyi +14 -0
- htmlgraph/sdk/analytics/__init__.py +19 -0
- htmlgraph/sdk/analytics/engine.py +155 -0
- htmlgraph/sdk/analytics/helpers.py +178 -0
- htmlgraph/sdk/analytics/registry.py +109 -0
- htmlgraph/sdk/base.py +484 -0
- htmlgraph/sdk/constants.py +216 -0
- htmlgraph/sdk/core.pyi +308 -0
- htmlgraph/sdk/discovery.py +120 -0
- htmlgraph/sdk/help/__init__.py +12 -0
- htmlgraph/sdk/help/mixin.py +699 -0
- htmlgraph/sdk/mixins/__init__.py +15 -0
- htmlgraph/sdk/mixins/attribution.py +113 -0
- htmlgraph/sdk/mixins/mixin.py +410 -0
- htmlgraph/sdk/operations/__init__.py +12 -0
- htmlgraph/sdk/operations/mixin.py +427 -0
- htmlgraph/sdk/orchestration/__init__.py +17 -0
- htmlgraph/sdk/orchestration/coordinator.py +203 -0
- htmlgraph/sdk/orchestration/spawner.py +204 -0
- htmlgraph/sdk/planning/__init__.py +19 -0
- htmlgraph/sdk/planning/bottlenecks.py +93 -0
- htmlgraph/sdk/planning/mixin.py +211 -0
- htmlgraph/sdk/planning/parallel.py +186 -0
- htmlgraph/sdk/planning/queue.py +210 -0
- htmlgraph/sdk/planning/recommendations.py +87 -0
- htmlgraph/sdk/planning/smart_planning.py +319 -0
- htmlgraph/sdk/session/__init__.py +19 -0
- htmlgraph/sdk/session/continuity.py +57 -0
- htmlgraph/sdk/session/handoff.py +110 -0
- htmlgraph/sdk/session/info.py +309 -0
- htmlgraph/sdk/session/manager.py +103 -0
- htmlgraph/sdk/strategic/__init__.py +26 -0
- htmlgraph/sdk/strategic/mixin.py +563 -0
- htmlgraph/server.py +685 -180
- htmlgraph/services/__init__.py +10 -0
- htmlgraph/services/claiming.py +199 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +1392 -175
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +201 -0
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/setup.py +34 -17
- htmlgraph/spike_index.py +143 -0
- htmlgraph/sync_docs.py +12 -15
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph/templates/GEMINI.md.template +87 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +146 -15
- htmlgraph/track_manager.py +69 -21
- htmlgraph/transcript.py +890 -0
- htmlgraph/transcript_analytics.py +699 -0
- htmlgraph/types.py +323 -0
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +8 -5
- htmlgraph/work_type_utils.py +3 -2
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
- htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -2688
- htmlgraph/sdk.py +0 -709
- htmlgraph-0.9.3.dist-info/RECORD +0 -61
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
htmlgraph/agents.py
CHANGED
|
@@ -1,19 +1,85 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Agent interface for
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
Agent interface for simplified task management.
|
|
3
|
+
|
|
4
|
+
IMPERATIVE USAGE INSTRUCTIONS
|
|
5
|
+
=============================
|
|
6
|
+
|
|
7
|
+
The AgentInterface provides lightweight task claiming and progress tracking.
|
|
8
|
+
|
|
9
|
+
TASK LIFECYCLE
|
|
10
|
+
==============
|
|
11
|
+
|
|
12
|
+
1. CLAIM A TASK
|
|
13
|
+
```python
|
|
14
|
+
from htmlgraph.agents import AgentInterface
|
|
15
|
+
|
|
16
|
+
agent = AgentInterface("features/")
|
|
17
|
+
task = agent.get_next_task(
|
|
18
|
+
agent_id="claude",
|
|
19
|
+
priority="high", # Optional filter
|
|
20
|
+
auto_claim=True # Automatically claim the task
|
|
21
|
+
)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
2. WORK ON TASK
|
|
25
|
+
```python
|
|
26
|
+
# Get lightweight context for LLM
|
|
27
|
+
context = agent.get_context(task.id)
|
|
28
|
+
# Returns: "# feature-001: Title\\nStatus: in-progress..."
|
|
29
|
+
|
|
30
|
+
# Complete steps as you work
|
|
31
|
+
agent.complete_step(task.id, step_index=0, agent_id="claude")
|
|
32
|
+
agent.complete_step(task.id, step_index=1, agent_id="claude")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. COMPLETE TASK
|
|
36
|
+
```python
|
|
37
|
+
agent.complete_task(task.id, agent_id="claude")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
4. IF BLOCKED
|
|
41
|
+
```python
|
|
42
|
+
agent.release_task(task.id, agent_id="claude")
|
|
43
|
+
# Task becomes available for other agents
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
DISCOVERY METHODS
|
|
47
|
+
=================
|
|
48
|
+
|
|
49
|
+
| Method | Purpose |
|
|
50
|
+
|--------|---------|
|
|
51
|
+
| `get_available_tasks()` | Find all matching tasks |
|
|
52
|
+
| `get_next_task()` | Get single next task |
|
|
53
|
+
| `get_blocked_tasks()` | Find blocked work |
|
|
54
|
+
| `get_in_progress_tasks()` | Get agent's active work |
|
|
55
|
+
|
|
56
|
+
ANTI-PATTERNS
|
|
57
|
+
=============
|
|
58
|
+
|
|
59
|
+
NEVER:
|
|
60
|
+
- Work on unclaimed tasks
|
|
61
|
+
- Skip step completion updates
|
|
62
|
+
- Leave tasks in-progress when blocked
|
|
63
|
+
|
|
64
|
+
ALWAYS:
|
|
65
|
+
- Claim before working
|
|
66
|
+
- Update progress incrementally
|
|
67
|
+
- Release if you can't complete
|
|
68
|
+
|
|
69
|
+
Available Classes
|
|
70
|
+
=================
|
|
71
|
+
|
|
72
|
+
AgentInterface: Simplified interface for task management
|
|
73
|
+
AgentRegistry: Capability-based agent routing
|
|
8
74
|
"""
|
|
9
75
|
|
|
10
76
|
from datetime import datetime
|
|
11
77
|
from pathlib import Path
|
|
12
|
-
from typing import Any
|
|
78
|
+
from typing import Any, Literal, cast
|
|
13
79
|
|
|
14
|
-
from htmlgraph.
|
|
80
|
+
from htmlgraph.agent_registry import AgentProfile, AgentRegistry
|
|
15
81
|
from htmlgraph.graph import HtmlGraph
|
|
16
|
-
from htmlgraph.
|
|
82
|
+
from htmlgraph.models import Node, Step
|
|
17
83
|
|
|
18
84
|
|
|
19
85
|
class AgentInterface:
|
|
@@ -34,11 +100,7 @@ class AgentInterface:
|
|
|
34
100
|
agent.complete_task(task.id, agent_id="claude")
|
|
35
101
|
"""
|
|
36
102
|
|
|
37
|
-
def __init__(
|
|
38
|
-
self,
|
|
39
|
-
directory: Path | str,
|
|
40
|
-
agent_id: str | None = None
|
|
41
|
-
):
|
|
103
|
+
def __init__(self, directory: Path | str, agent_id: str | None = None):
|
|
42
104
|
"""
|
|
43
105
|
Initialize agent interface.
|
|
44
106
|
|
|
@@ -51,7 +113,11 @@ class AgentInterface:
|
|
|
51
113
|
|
|
52
114
|
# Initialize agent registry for capability-based routing
|
|
53
115
|
# Assumes .htmlgraph is parent of directory
|
|
54
|
-
htmlgraph_dir =
|
|
116
|
+
htmlgraph_dir = (
|
|
117
|
+
Path(directory).parent
|
|
118
|
+
if Path(directory).name == "features"
|
|
119
|
+
else Path(directory)
|
|
120
|
+
)
|
|
55
121
|
self.registry = AgentRegistry(htmlgraph_dir)
|
|
56
122
|
|
|
57
123
|
def reload(self) -> None:
|
|
@@ -67,7 +133,7 @@ class AgentInterface:
|
|
|
67
133
|
status: str = "todo",
|
|
68
134
|
priority: str | None = None,
|
|
69
135
|
node_type: str | None = None,
|
|
70
|
-
limit: int = 10
|
|
136
|
+
limit: int = 10,
|
|
71
137
|
) -> list[Node]:
|
|
72
138
|
"""
|
|
73
139
|
Get available tasks matching criteria.
|
|
@@ -81,6 +147,7 @@ class AgentInterface:
|
|
|
81
147
|
Returns:
|
|
82
148
|
List of matching nodes, sorted by priority
|
|
83
149
|
"""
|
|
150
|
+
|
|
84
151
|
def matches(node: Node) -> bool:
|
|
85
152
|
if node.status != status:
|
|
86
153
|
return False
|
|
@@ -106,7 +173,7 @@ class AgentInterface:
|
|
|
106
173
|
agent_id: str | None = None,
|
|
107
174
|
priority: str | None = None,
|
|
108
175
|
node_type: str | None = None,
|
|
109
|
-
auto_claim: bool = False
|
|
176
|
+
auto_claim: bool = False,
|
|
110
177
|
) -> Node | None:
|
|
111
178
|
"""
|
|
112
179
|
Get the next available task.
|
|
@@ -122,9 +189,7 @@ class AgentInterface:
|
|
|
122
189
|
"""
|
|
123
190
|
agent_id = agent_id or self.agent_id
|
|
124
191
|
tasks = self.get_available_tasks(
|
|
125
|
-
priority=priority,
|
|
126
|
-
node_type=node_type,
|
|
127
|
-
limit=1
|
|
192
|
+
priority=priority, node_type=node_type, limit=1
|
|
128
193
|
)
|
|
129
194
|
|
|
130
195
|
if not tasks:
|
|
@@ -135,7 +200,9 @@ class AgentInterface:
|
|
|
135
200
|
if auto_claim and agent_id:
|
|
136
201
|
self.claim_task(task.id, agent_id)
|
|
137
202
|
# Reload to get updated state
|
|
138
|
-
|
|
203
|
+
reloaded_task = self.graph.get(task.id)
|
|
204
|
+
if reloaded_task:
|
|
205
|
+
task = reloaded_task
|
|
139
206
|
|
|
140
207
|
return task
|
|
141
208
|
|
|
@@ -158,10 +225,7 @@ class AgentInterface:
|
|
|
158
225
|
return tasks
|
|
159
226
|
|
|
160
227
|
def get_tasks_by_capability(
|
|
161
|
-
self,
|
|
162
|
-
agent_capabilities: list[str],
|
|
163
|
-
status: str = "todo",
|
|
164
|
-
limit: int = 10
|
|
228
|
+
self, agent_capabilities: list[str], status: str = "todo", limit: int = 10
|
|
165
229
|
) -> list[Node]:
|
|
166
230
|
"""
|
|
167
231
|
Get tasks that match agent capabilities.
|
|
@@ -176,6 +240,7 @@ class AgentInterface:
|
|
|
176
240
|
Returns:
|
|
177
241
|
List of matching nodes, prioritized by exact matches first
|
|
178
242
|
"""
|
|
243
|
+
|
|
179
244
|
def matches(node: Node) -> bool:
|
|
180
245
|
if node.status != status:
|
|
181
246
|
return False
|
|
@@ -193,10 +258,11 @@ class AgentInterface:
|
|
|
193
258
|
# Sort by capability match quality
|
|
194
259
|
agent_caps = set(agent_capabilities)
|
|
195
260
|
|
|
196
|
-
def capability_score(node: Node) -> tuple[int,
|
|
197
|
-
"""Return (negative_exact_matches,
|
|
261
|
+
def capability_score(node: Node) -> tuple[int, int]:
|
|
262
|
+
"""Return (negative_exact_matches, priority_order) for sorting."""
|
|
198
263
|
if not node.required_capabilities:
|
|
199
|
-
|
|
264
|
+
priority_order = {"critical": 0, "high": 1, "medium": 2, "low": 3}
|
|
265
|
+
return (0, priority_order.get(node.priority, 99))
|
|
200
266
|
exact_matches = len(set(node.required_capabilities) & agent_caps)
|
|
201
267
|
# Sort by exact matches (descending), then by priority
|
|
202
268
|
priority_order = {"critical": 0, "high": 1, "medium": 2, "low": 3}
|
|
@@ -210,7 +276,7 @@ class AgentInterface:
|
|
|
210
276
|
self,
|
|
211
277
|
agent_capabilities: list[str],
|
|
212
278
|
agent_id: str | None = None,
|
|
213
|
-
auto_claim: bool = False
|
|
279
|
+
auto_claim: bool = False,
|
|
214
280
|
) -> Node | None:
|
|
215
281
|
"""
|
|
216
282
|
Get next task matching agent capabilities.
|
|
@@ -234,7 +300,9 @@ class AgentInterface:
|
|
|
234
300
|
if auto_claim and agent_id:
|
|
235
301
|
self.claim_task(task.id, agent_id)
|
|
236
302
|
# Reload to get updated state
|
|
237
|
-
|
|
303
|
+
reloaded_task = self.graph.get(task.id)
|
|
304
|
+
if reloaded_task:
|
|
305
|
+
task = reloaded_task
|
|
238
306
|
|
|
239
307
|
return task
|
|
240
308
|
|
|
@@ -332,10 +400,7 @@ class AgentInterface:
|
|
|
332
400
|
return True
|
|
333
401
|
|
|
334
402
|
def block_task(
|
|
335
|
-
self,
|
|
336
|
-
node_id: str,
|
|
337
|
-
blocked_by: str,
|
|
338
|
-
reason: str | None = None
|
|
403
|
+
self, node_id: str, blocked_by: str, reason: str | None = None
|
|
339
404
|
) -> bool:
|
|
340
405
|
"""
|
|
341
406
|
Mark a task as blocked.
|
|
@@ -357,11 +422,12 @@ class AgentInterface:
|
|
|
357
422
|
|
|
358
423
|
# Add blocking edge if not present
|
|
359
424
|
from htmlgraph.models import Edge
|
|
425
|
+
|
|
360
426
|
blocking_edge = Edge(
|
|
361
427
|
target_id=blocked_by,
|
|
362
428
|
relationship="blocked_by",
|
|
363
429
|
since=datetime.now(),
|
|
364
|
-
properties={"reason": reason} if reason else {}
|
|
430
|
+
properties={"reason": reason} if reason else {},
|
|
365
431
|
)
|
|
366
432
|
node.add_edge(blocking_edge)
|
|
367
433
|
|
|
@@ -373,10 +439,7 @@ class AgentInterface:
|
|
|
373
439
|
# =========================================================================
|
|
374
440
|
|
|
375
441
|
def complete_step(
|
|
376
|
-
self,
|
|
377
|
-
node_id: str,
|
|
378
|
-
step_index: int,
|
|
379
|
-
agent_id: str | None = None
|
|
442
|
+
self, node_id: str, step_index: int, agent_id: str | None = None
|
|
380
443
|
) -> bool:
|
|
381
444
|
"""
|
|
382
445
|
Mark a step as completed.
|
|
@@ -401,11 +464,7 @@ class AgentInterface:
|
|
|
401
464
|
|
|
402
465
|
return False
|
|
403
466
|
|
|
404
|
-
def add_step(
|
|
405
|
-
self,
|
|
406
|
-
node_id: str,
|
|
407
|
-
description: str
|
|
408
|
-
) -> bool:
|
|
467
|
+
def add_step(self, node_id: str, description: str) -> bool:
|
|
409
468
|
"""
|
|
410
469
|
Add a new step to a task.
|
|
411
470
|
|
|
@@ -532,9 +591,7 @@ class AgentInterface:
|
|
|
532
591
|
# =========================================================================
|
|
533
592
|
|
|
534
593
|
def find_bottlenecks(
|
|
535
|
-
self,
|
|
536
|
-
top_n: int = 5,
|
|
537
|
-
status_filter: list[str] | None = None
|
|
594
|
+
self, top_n: int = 5, status_filter: list[str] | None = None
|
|
538
595
|
) -> list[dict[str, Any]]:
|
|
539
596
|
"""
|
|
540
597
|
Identify tasks blocking the most downstream work.
|
|
@@ -555,8 +612,7 @@ class AgentInterface:
|
|
|
555
612
|
|
|
556
613
|
analytics = DependencyAnalytics(self.graph)
|
|
557
614
|
bottlenecks = analytics.find_bottlenecks(
|
|
558
|
-
status_filter=status_filter,
|
|
559
|
-
top_n=top_n
|
|
615
|
+
status_filter=status_filter, top_n=top_n
|
|
560
616
|
)
|
|
561
617
|
|
|
562
618
|
# Convert to agent-friendly dict format
|
|
@@ -568,15 +624,13 @@ class AgentInterface:
|
|
|
568
624
|
"priority": bn.priority,
|
|
569
625
|
"blocks_count": bn.transitive_blocking,
|
|
570
626
|
"impact_score": bn.weighted_impact,
|
|
571
|
-
"blocked_tasks": bn.blocked_nodes[:5] # Limit for readability
|
|
627
|
+
"blocked_tasks": bn.blocked_nodes[:5], # Limit for readability
|
|
572
628
|
}
|
|
573
629
|
for bn in bottlenecks
|
|
574
630
|
]
|
|
575
631
|
|
|
576
632
|
def get_parallel_work(
|
|
577
|
-
self,
|
|
578
|
-
status: str = "todo",
|
|
579
|
-
max_agents: int = 5
|
|
633
|
+
self, status: str = "todo", max_agents: int = 5
|
|
580
634
|
) -> dict[str, Any]:
|
|
581
635
|
"""
|
|
582
636
|
Find tasks that can be worked on simultaneously.
|
|
@@ -598,20 +652,22 @@ class AgentInterface:
|
|
|
598
652
|
analytics = DependencyAnalytics(self.graph)
|
|
599
653
|
report = analytics.find_parallelizable_work(status=status)
|
|
600
654
|
|
|
601
|
-
ready_now =
|
|
655
|
+
ready_now = (
|
|
656
|
+
report.dependency_levels[0].nodes if report.dependency_levels else []
|
|
657
|
+
)
|
|
602
658
|
|
|
603
659
|
return {
|
|
604
660
|
"max_parallelism": report.max_parallelism,
|
|
605
661
|
"ready_now": ready_now[:max_agents],
|
|
606
662
|
"total_ready": len(ready_now),
|
|
607
663
|
"level_count": len(report.dependency_levels),
|
|
608
|
-
"next_level": report.dependency_levels[1].nodes
|
|
664
|
+
"next_level": report.dependency_levels[1].nodes
|
|
665
|
+
if len(report.dependency_levels) > 1
|
|
666
|
+
else [],
|
|
609
667
|
}
|
|
610
668
|
|
|
611
669
|
def recommend_next_work(
|
|
612
|
-
self,
|
|
613
|
-
agent_count: int = 1,
|
|
614
|
-
lookahead: int = 5
|
|
670
|
+
self, agent_count: int = 1, lookahead: int = 5
|
|
615
671
|
) -> list[dict[str, Any]]:
|
|
616
672
|
"""
|
|
617
673
|
Get smart recommendations for what to work on next.
|
|
@@ -635,8 +691,7 @@ class AgentInterface:
|
|
|
635
691
|
|
|
636
692
|
analytics = DependencyAnalytics(self.graph)
|
|
637
693
|
recommendations = analytics.recommend_next_tasks(
|
|
638
|
-
agent_count=agent_count,
|
|
639
|
-
lookahead=lookahead
|
|
694
|
+
agent_count=agent_count, lookahead=lookahead
|
|
640
695
|
)
|
|
641
696
|
|
|
642
697
|
return [
|
|
@@ -648,7 +703,7 @@ class AgentInterface:
|
|
|
648
703
|
"reasons": rec.reasons,
|
|
649
704
|
"estimated_hours": rec.estimated_effort,
|
|
650
705
|
"unlocks_count": len(rec.unlocks),
|
|
651
|
-
"unlocks": rec.unlocks[:3] # Show first 3
|
|
706
|
+
"unlocks": rec.unlocks[:3], # Show first 3
|
|
652
707
|
}
|
|
653
708
|
for rec in recommendations.recommendations
|
|
654
709
|
]
|
|
@@ -682,14 +737,14 @@ class AgentInterface:
|
|
|
682
737
|
"id": node.id,
|
|
683
738
|
"title": node.title,
|
|
684
739
|
"risk_score": node.risk_score,
|
|
685
|
-
"risk_factors": [f.description for f in node.risk_factors]
|
|
740
|
+
"risk_factors": [f.description for f in node.risk_factors],
|
|
686
741
|
}
|
|
687
742
|
for node in risk.high_risk
|
|
688
743
|
],
|
|
689
744
|
"circular_dependencies": risk.circular_dependencies,
|
|
690
745
|
"orphaned_count": len(risk.orphaned_nodes),
|
|
691
746
|
"orphaned_tasks": risk.orphaned_nodes[:5], # First 5
|
|
692
|
-
"recommendations": risk.recommendations
|
|
747
|
+
"recommendations": risk.recommendations,
|
|
693
748
|
}
|
|
694
749
|
|
|
695
750
|
def analyze_impact(self, node_id: str) -> dict[str, Any]:
|
|
@@ -720,7 +775,7 @@ class AgentInterface:
|
|
|
720
775
|
"total_impact": impact.transitive_dependents,
|
|
721
776
|
"completion_impact": impact.completion_impact,
|
|
722
777
|
"unlocks_count": len(impact.affected_nodes),
|
|
723
|
-
"affected_tasks": impact.affected_nodes[:10] # First 10
|
|
778
|
+
"affected_tasks": impact.affected_nodes[:10], # First 10
|
|
724
779
|
}
|
|
725
780
|
|
|
726
781
|
# =========================================================================
|
|
@@ -734,7 +789,7 @@ class AgentInterface:
|
|
|
734
789
|
description: str = "",
|
|
735
790
|
priority: str = "medium",
|
|
736
791
|
node_type: str = "task",
|
|
737
|
-
steps: list[str] | None = None
|
|
792
|
+
steps: list[str] | None = None,
|
|
738
793
|
) -> Node:
|
|
739
794
|
"""
|
|
740
795
|
Create a new task.
|
|
@@ -754,9 +809,9 @@ class AgentInterface:
|
|
|
754
809
|
id=task_id,
|
|
755
810
|
title=title,
|
|
756
811
|
type=node_type,
|
|
757
|
-
priority=priority,
|
|
812
|
+
priority=cast(Literal["low", "medium", "high", "critical"], priority),
|
|
758
813
|
content=f"<p>{description}</p>" if description else "",
|
|
759
|
-
steps=[Step(description=s) for s in (steps or [])]
|
|
814
|
+
steps=[Step(description=s) for s in (steps or [])],
|
|
760
815
|
)
|
|
761
816
|
|
|
762
817
|
self.graph.add(node)
|
|
@@ -785,7 +840,7 @@ class AgentInterface:
|
|
|
785
840
|
"agent_id": agent_id,
|
|
786
841
|
"in_progress": len(in_progress),
|
|
787
842
|
"completed": len(completed),
|
|
788
|
-
"tasks": [t.id for t in in_progress]
|
|
843
|
+
"tasks": [t.id for t in in_progress],
|
|
789
844
|
}
|
|
790
845
|
|
|
791
846
|
# =========================================================================
|
|
@@ -793,10 +848,7 @@ class AgentInterface:
|
|
|
793
848
|
# =========================================================================
|
|
794
849
|
|
|
795
850
|
def calculate_task_score(
|
|
796
|
-
self,
|
|
797
|
-
task: Node,
|
|
798
|
-
agent: AgentProfile,
|
|
799
|
-
current_workload: int = 0
|
|
851
|
+
self, task: Node, agent: AgentProfile, current_workload: int = 0
|
|
800
852
|
) -> float:
|
|
801
853
|
"""
|
|
802
854
|
Calculate routing score for a task-agent pair.
|
|
@@ -814,12 +866,7 @@ class AgentInterface:
|
|
|
814
866
|
score = 0.0
|
|
815
867
|
|
|
816
868
|
# Priority score (0-30 points)
|
|
817
|
-
priority_scores = {
|
|
818
|
-
"critical": 30,
|
|
819
|
-
"high": 20,
|
|
820
|
-
"medium": 10,
|
|
821
|
-
"low": 5
|
|
822
|
-
}
|
|
869
|
+
priority_scores = {"critical": 30, "high": 20, "medium": 10, "low": 5}
|
|
823
870
|
score += priority_scores.get(task.priority, 5)
|
|
824
871
|
|
|
825
872
|
# Capability match score (0-40 points)
|
|
@@ -828,24 +875,27 @@ class AgentInterface:
|
|
|
828
875
|
# Perfect match gets 40 points
|
|
829
876
|
score += 40
|
|
830
877
|
# Bonus for exact capability match (no extra capabilities)
|
|
831
|
-
matching_caps = sum(
|
|
878
|
+
matching_caps = sum(
|
|
879
|
+
1 for cap in task.required_capabilities if cap in agent.capabilities
|
|
880
|
+
)
|
|
832
881
|
score += (matching_caps / len(task.required_capabilities)) * 5
|
|
833
882
|
else:
|
|
834
883
|
# No match = very low score
|
|
835
884
|
score = max(score - 30, 0)
|
|
836
885
|
|
|
837
886
|
# Complexity match score (0-20 points)
|
|
838
|
-
|
|
839
|
-
|
|
887
|
+
task_complexity = getattr(task, "complexity", None)
|
|
888
|
+
if task_complexity:
|
|
889
|
+
if agent.can_handle_complexity(task_complexity):
|
|
840
890
|
score += 20
|
|
841
891
|
# Bonus for preferred complexity
|
|
842
892
|
complexity_preference = {
|
|
843
893
|
"low": 2,
|
|
844
894
|
"medium": 5,
|
|
845
895
|
"high": 3,
|
|
846
|
-
"very-high": 1
|
|
896
|
+
"very-high": 1,
|
|
847
897
|
}
|
|
848
|
-
score += complexity_preference.get(
|
|
898
|
+
score += complexity_preference.get(task_complexity, 0)
|
|
849
899
|
else:
|
|
850
900
|
score = max(score - 15, 0)
|
|
851
901
|
|
|
@@ -865,9 +915,7 @@ class AgentInterface:
|
|
|
865
915
|
return len(in_progress)
|
|
866
916
|
|
|
867
917
|
def find_best_match(
|
|
868
|
-
self,
|
|
869
|
-
task: Node,
|
|
870
|
-
candidate_agents: list[str] | None = None
|
|
918
|
+
self, task: Node, candidate_agents: list[str] | None = None
|
|
871
919
|
) -> tuple[str, float] | None:
|
|
872
920
|
"""
|
|
873
921
|
Find the best agent for a task using smart routing.
|
|
@@ -880,15 +928,16 @@ class AgentInterface:
|
|
|
880
928
|
Tuple of (agent_id, score) or None if no match
|
|
881
929
|
"""
|
|
882
930
|
# Get candidate agents
|
|
931
|
+
agents: list[AgentProfile]
|
|
883
932
|
if candidate_agents:
|
|
884
|
-
|
|
885
|
-
agents = [a for a in
|
|
933
|
+
agents_maybe = [self.registry.get(aid) for aid in candidate_agents]
|
|
934
|
+
agents = [a for a in agents_maybe if a and a.active]
|
|
886
935
|
else:
|
|
887
936
|
# Find capable agents based on requirements
|
|
888
|
-
|
|
937
|
+
required_caps = getattr(task, "required_capabilities", None)
|
|
938
|
+
if required_caps:
|
|
889
939
|
agents = self.registry.find_capable_agents(
|
|
890
|
-
task
|
|
891
|
-
task.complexity
|
|
940
|
+
required_caps, getattr(task, "complexity", None)
|
|
892
941
|
)
|
|
893
942
|
else:
|
|
894
943
|
# No requirements, all active agents
|
|
@@ -914,10 +963,7 @@ class AgentInterface:
|
|
|
914
963
|
return None
|
|
915
964
|
|
|
916
965
|
def get_work_queue(
|
|
917
|
-
self,
|
|
918
|
-
agent_id: str | None = None,
|
|
919
|
-
limit: int = 10,
|
|
920
|
-
min_score: float = 20.0
|
|
966
|
+
self, agent_id: str | None = None, limit: int = 10, min_score: float = 20.0
|
|
921
967
|
) -> list[dict[str, Any]]:
|
|
922
968
|
"""
|
|
923
969
|
Get prioritized work queue for an agent using smart routing.
|
|
@@ -949,19 +995,26 @@ class AgentInterface:
|
|
|
949
995
|
score = self.calculate_task_score(task, agent, workload)
|
|
950
996
|
|
|
951
997
|
if score >= min_score:
|
|
952
|
-
queue.append(
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
998
|
+
queue.append(
|
|
999
|
+
{
|
|
1000
|
+
"task_id": task.id,
|
|
1001
|
+
"title": task.title,
|
|
1002
|
+
"priority": task.priority,
|
|
1003
|
+
"status": task.status,
|
|
1004
|
+
"score": round(score, 2),
|
|
1005
|
+
"required_capabilities": getattr(
|
|
1006
|
+
task, "required_capabilities", None
|
|
1007
|
+
),
|
|
1008
|
+
"complexity": getattr(task, "complexity", None),
|
|
1009
|
+
"estimated_effort": getattr(task, "estimated_effort", None),
|
|
1010
|
+
}
|
|
1011
|
+
)
|
|
962
1012
|
|
|
963
1013
|
# Sort by score (highest first)
|
|
964
|
-
queue.sort(
|
|
1014
|
+
queue.sort(
|
|
1015
|
+
key=lambda x: float(x["score"]) if x["score"] is not None else 0.0,
|
|
1016
|
+
reverse=True,
|
|
1017
|
+
)
|
|
965
1018
|
|
|
966
1019
|
return queue[:limit]
|
|
967
1020
|
|
|
@@ -969,7 +1022,7 @@ class AgentInterface:
|
|
|
969
1022
|
self,
|
|
970
1023
|
agent_id: str | None = None,
|
|
971
1024
|
auto_claim: bool = False,
|
|
972
|
-
min_score: float = 20.0
|
|
1025
|
+
min_score: float = 20.0,
|
|
973
1026
|
) -> Node | None:
|
|
974
1027
|
"""
|
|
975
1028
|
Get next task using smart routing based on capabilities.
|
htmlgraph/analytics/__init__.py
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Analytics modules for HtmlGraph.
|
|
3
3
|
|
|
4
|
-
Provides work type analysis, dependency analytics,
|
|
4
|
+
Provides work type analysis, dependency analytics, cross-session analytics, CLI analytics,
|
|
5
|
+
and cost attribution analysis for OTEL ROI.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
|
-
from htmlgraph.analytics.
|
|
8
|
+
from htmlgraph.analytics.cost_analyzer import CostAnalyzer
|
|
9
|
+
from htmlgraph.analytics.cost_reporter import CostReporter
|
|
10
|
+
from htmlgraph.analytics.cross_session import CrossSessionAnalytics
|
|
8
11
|
from htmlgraph.analytics.dependency import DependencyAnalytics
|
|
12
|
+
from htmlgraph.analytics.work_type import Analytics
|
|
9
13
|
|
|
10
14
|
__all__ = [
|
|
11
15
|
"Analytics",
|
|
12
16
|
"DependencyAnalytics",
|
|
17
|
+
"CrossSessionAnalytics",
|
|
18
|
+
"CostAnalyzer",
|
|
19
|
+
"CostReporter",
|
|
13
20
|
]
|