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
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
"""Codex spawner implementation."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import subprocess
|
|
6
|
+
import time
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from .base import AIResult, BaseSpawner
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from htmlgraph.sdk import SDK
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CodexSpawner(BaseSpawner):
|
|
18
|
+
"""Spawner for OpenAI Codex CLI."""
|
|
19
|
+
|
|
20
|
+
def _parse_and_track_events(self, jsonl_output: str, sdk: "SDK") -> list[dict]:
|
|
21
|
+
"""
|
|
22
|
+
Parse Codex JSONL events and track in HtmlGraph.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
jsonl_output: JSONL output from Codex CLI
|
|
26
|
+
sdk: HtmlGraph SDK instance for tracking
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Parsed events list
|
|
30
|
+
"""
|
|
31
|
+
events = []
|
|
32
|
+
parse_errors = []
|
|
33
|
+
|
|
34
|
+
for line_num, line in enumerate(jsonl_output.splitlines(), start=1):
|
|
35
|
+
if not line.strip():
|
|
36
|
+
continue
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
event = json.loads(line)
|
|
40
|
+
events.append(event)
|
|
41
|
+
|
|
42
|
+
event_type = event.get("type")
|
|
43
|
+
|
|
44
|
+
# Track item.started events
|
|
45
|
+
if event_type == "item.started":
|
|
46
|
+
item = event.get("item", {})
|
|
47
|
+
item_type = item.get("type")
|
|
48
|
+
|
|
49
|
+
if item_type == "command_execution":
|
|
50
|
+
command = item.get("command", "")
|
|
51
|
+
self._track_activity(
|
|
52
|
+
sdk,
|
|
53
|
+
tool="codex_command",
|
|
54
|
+
summary=f"Codex executing: {command[:80]}",
|
|
55
|
+
payload={"command": command},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Track item.completed events
|
|
59
|
+
elif event_type == "item.completed":
|
|
60
|
+
item = event.get("item", {})
|
|
61
|
+
item_type = item.get("type")
|
|
62
|
+
|
|
63
|
+
if item_type == "file_change":
|
|
64
|
+
path = item.get("path", "unknown")
|
|
65
|
+
self._track_activity(
|
|
66
|
+
sdk,
|
|
67
|
+
tool="codex_file_change",
|
|
68
|
+
summary=f"Codex modified: {path}",
|
|
69
|
+
file_paths=[path],
|
|
70
|
+
payload={"path": path},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
elif item_type == "agent_message":
|
|
74
|
+
text = item.get("text", "")
|
|
75
|
+
summary = text[:100] + "..." if len(text) > 100 else text
|
|
76
|
+
self._track_activity(
|
|
77
|
+
sdk,
|
|
78
|
+
tool="codex_message",
|
|
79
|
+
summary=f"Codex: {summary}",
|
|
80
|
+
payload={"text_length": len(text)},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Track turn.completed for token usage
|
|
84
|
+
elif event_type == "turn.completed":
|
|
85
|
+
usage = event.get("usage", {})
|
|
86
|
+
total_tokens = sum(usage.values())
|
|
87
|
+
self._track_activity(
|
|
88
|
+
sdk,
|
|
89
|
+
tool="codex_completion",
|
|
90
|
+
summary=f"Codex turn completed ({total_tokens} tokens)",
|
|
91
|
+
payload={"usage": usage},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
except json.JSONDecodeError as e:
|
|
95
|
+
parse_errors.append(
|
|
96
|
+
{
|
|
97
|
+
"line_number": line_num,
|
|
98
|
+
"error": str(e),
|
|
99
|
+
"content": line[:100],
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
return events
|
|
105
|
+
|
|
106
|
+
def spawn(
|
|
107
|
+
self,
|
|
108
|
+
prompt: str,
|
|
109
|
+
output_json: bool = True,
|
|
110
|
+
model: str | None = None,
|
|
111
|
+
sandbox: str | None = None,
|
|
112
|
+
full_auto: bool = True,
|
|
113
|
+
images: list[str] | None = None,
|
|
114
|
+
output_last_message: str | None = None,
|
|
115
|
+
output_schema: str | None = None,
|
|
116
|
+
skip_git_check: bool = False,
|
|
117
|
+
working_directory: str | None = None,
|
|
118
|
+
use_oss: bool = False,
|
|
119
|
+
bypass_approvals: bool = False,
|
|
120
|
+
track_in_htmlgraph: bool = True,
|
|
121
|
+
timeout: int = 120,
|
|
122
|
+
tracker: Any = None,
|
|
123
|
+
parent_event_id: str | None = None,
|
|
124
|
+
) -> AIResult:
|
|
125
|
+
"""
|
|
126
|
+
Spawn Codex in headless mode.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
prompt: Task description for Codex
|
|
130
|
+
output_json: JSONL output flag (enables real-time tracking)
|
|
131
|
+
model: Model selection (e.g., "gpt-4-turbo"). Default: None
|
|
132
|
+
sandbox: Sandbox mode ("read-only", "workspace-write", or full)
|
|
133
|
+
full_auto: Enable full auto mode. Default: True (required headless)
|
|
134
|
+
images: List of image paths (--image). Default: None
|
|
135
|
+
output_last_message: Write last message to file. Default: None
|
|
136
|
+
output_schema: JSON schema for validation. Default: None
|
|
137
|
+
skip_git_check: Skip git repo check. Default: False
|
|
138
|
+
working_directory: Workspace directory (--cd). Default: None
|
|
139
|
+
use_oss: Use local Ollama provider (--oss). Default: False
|
|
140
|
+
bypass_approvals: Bypass approval checks. Default: False
|
|
141
|
+
track_in_htmlgraph: Enable HtmlGraph activity tracking. Default: True
|
|
142
|
+
timeout: Max seconds to wait
|
|
143
|
+
tracker: Optional SpawnerEventTracker for recording subprocess invocation
|
|
144
|
+
parent_event_id: Optional parent event ID for event hierarchy
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
AIResult with response, error, and tracked events if tracking enabled
|
|
148
|
+
"""
|
|
149
|
+
# Initialize tracking if enabled
|
|
150
|
+
sdk: SDK | None = None
|
|
151
|
+
tracked_events: list[dict] = []
|
|
152
|
+
if track_in_htmlgraph and output_json:
|
|
153
|
+
sdk = self._get_sdk()
|
|
154
|
+
|
|
155
|
+
# Publish live event: spawner starting
|
|
156
|
+
self._publish_live_event(
|
|
157
|
+
"spawner_start",
|
|
158
|
+
"codex",
|
|
159
|
+
prompt=prompt,
|
|
160
|
+
model=model,
|
|
161
|
+
)
|
|
162
|
+
start_time = time.time()
|
|
163
|
+
|
|
164
|
+
cmd = ["codex", "exec"]
|
|
165
|
+
|
|
166
|
+
if output_json:
|
|
167
|
+
cmd.append("--json")
|
|
168
|
+
|
|
169
|
+
# Add model if specified
|
|
170
|
+
if model:
|
|
171
|
+
cmd.extend(["--model", model])
|
|
172
|
+
|
|
173
|
+
# Add sandbox mode if specified
|
|
174
|
+
if sandbox:
|
|
175
|
+
cmd.extend(["--sandbox", sandbox])
|
|
176
|
+
|
|
177
|
+
# Add full auto flag
|
|
178
|
+
if full_auto:
|
|
179
|
+
cmd.append("--full-auto")
|
|
180
|
+
|
|
181
|
+
# Add images
|
|
182
|
+
if images:
|
|
183
|
+
for image in images:
|
|
184
|
+
cmd.extend(["--image", image])
|
|
185
|
+
|
|
186
|
+
# Add output last message file if specified
|
|
187
|
+
if output_last_message:
|
|
188
|
+
cmd.extend(["--output-last-message", output_last_message])
|
|
189
|
+
|
|
190
|
+
# Add output schema if specified
|
|
191
|
+
if output_schema:
|
|
192
|
+
cmd.extend(["--output-schema", output_schema])
|
|
193
|
+
|
|
194
|
+
# Add skip git check flag
|
|
195
|
+
if skip_git_check:
|
|
196
|
+
cmd.append("--skip-git-repo-check")
|
|
197
|
+
|
|
198
|
+
# Add working directory if specified
|
|
199
|
+
if working_directory:
|
|
200
|
+
cmd.extend(["--cd", working_directory])
|
|
201
|
+
|
|
202
|
+
# Add OSS flag
|
|
203
|
+
if use_oss:
|
|
204
|
+
cmd.append("--oss")
|
|
205
|
+
|
|
206
|
+
# Add bypass approvals flag
|
|
207
|
+
if bypass_approvals:
|
|
208
|
+
cmd.append("--dangerously-bypass-approvals-and-sandbox")
|
|
209
|
+
|
|
210
|
+
# Add prompt as final argument
|
|
211
|
+
cmd.append(prompt)
|
|
212
|
+
|
|
213
|
+
# Track spawner start if SDK available
|
|
214
|
+
if sdk:
|
|
215
|
+
self._track_activity(
|
|
216
|
+
sdk,
|
|
217
|
+
tool="codex_spawn_start",
|
|
218
|
+
summary=f"Spawning Codex: {prompt[:80]}",
|
|
219
|
+
payload={
|
|
220
|
+
"prompt_length": len(prompt),
|
|
221
|
+
"model": model,
|
|
222
|
+
"sandbox": sandbox,
|
|
223
|
+
},
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
# Publish live event: executing
|
|
228
|
+
self._publish_live_event(
|
|
229
|
+
"spawner_phase",
|
|
230
|
+
"codex",
|
|
231
|
+
phase="executing",
|
|
232
|
+
details="Running Codex CLI",
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Record subprocess invocation if tracker is available
|
|
236
|
+
subprocess_event_id = None
|
|
237
|
+
logger.warning(
|
|
238
|
+
f"DEBUG: tracker={tracker is not None}, parent_event_id={parent_event_id}"
|
|
239
|
+
)
|
|
240
|
+
if tracker and parent_event_id:
|
|
241
|
+
logger.debug("Recording subprocess invocation for Codex...")
|
|
242
|
+
try:
|
|
243
|
+
subprocess_event = tracker.record_tool_call(
|
|
244
|
+
tool_name="subprocess.codex",
|
|
245
|
+
tool_input={"cmd": cmd},
|
|
246
|
+
phase_event_id=parent_event_id,
|
|
247
|
+
spawned_agent="gpt-4",
|
|
248
|
+
)
|
|
249
|
+
if subprocess_event:
|
|
250
|
+
subprocess_event_id = subprocess_event.get("event_id")
|
|
251
|
+
logger.warning(
|
|
252
|
+
f"DEBUG: Subprocess event created for Codex: {subprocess_event_id}"
|
|
253
|
+
)
|
|
254
|
+
else:
|
|
255
|
+
logger.debug("subprocess_event was None")
|
|
256
|
+
except Exception as e:
|
|
257
|
+
# Tracking failure should not break execution
|
|
258
|
+
logger.warning(f"DEBUG: Exception recording Codex subprocess: {e}")
|
|
259
|
+
pass
|
|
260
|
+
else:
|
|
261
|
+
logger.warning(
|
|
262
|
+
f"DEBUG: Skipping Codex subprocess tracking - tracker={tracker is not None}, parent_event_id={parent_event_id}"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
result = subprocess.run(
|
|
266
|
+
cmd,
|
|
267
|
+
stdout=subprocess.PIPE,
|
|
268
|
+
stderr=subprocess.DEVNULL,
|
|
269
|
+
text=True,
|
|
270
|
+
timeout=timeout,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Complete subprocess invocation tracking
|
|
274
|
+
if tracker and subprocess_event_id:
|
|
275
|
+
try:
|
|
276
|
+
tracker.complete_tool_call(
|
|
277
|
+
event_id=subprocess_event_id,
|
|
278
|
+
output_summary=result.stdout[:500] if result.stdout else "",
|
|
279
|
+
success=result.returncode == 0,
|
|
280
|
+
)
|
|
281
|
+
except Exception:
|
|
282
|
+
# Tracking failure should not break execution
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
# Publish live event: processing
|
|
286
|
+
self._publish_live_event(
|
|
287
|
+
"spawner_phase",
|
|
288
|
+
"codex",
|
|
289
|
+
phase="processing",
|
|
290
|
+
details="Parsing Codex response",
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
if not output_json:
|
|
294
|
+
# Plain text mode - return as-is
|
|
295
|
+
duration = time.time() - start_time
|
|
296
|
+
success = result.returncode == 0
|
|
297
|
+
self._publish_live_event(
|
|
298
|
+
"spawner_complete",
|
|
299
|
+
"codex",
|
|
300
|
+
success=success,
|
|
301
|
+
duration=duration,
|
|
302
|
+
response=result.stdout.strip()[:200] if success else None,
|
|
303
|
+
error="Command failed" if not success else None,
|
|
304
|
+
)
|
|
305
|
+
return AIResult(
|
|
306
|
+
success=success,
|
|
307
|
+
response=result.stdout.strip(),
|
|
308
|
+
tokens_used=None,
|
|
309
|
+
error=None if success else "Command failed",
|
|
310
|
+
raw_output=result.stdout,
|
|
311
|
+
tracked_events=tracked_events,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Parse JSONL output
|
|
315
|
+
events = []
|
|
316
|
+
parse_errors = []
|
|
317
|
+
|
|
318
|
+
# Use tracking parser if SDK is available
|
|
319
|
+
if sdk:
|
|
320
|
+
tracked_events = self._parse_and_track_events(result.stdout, sdk)
|
|
321
|
+
events = tracked_events
|
|
322
|
+
else:
|
|
323
|
+
# Fallback to regular parsing without tracking
|
|
324
|
+
for line_num, line in enumerate(result.stdout.splitlines(), start=1):
|
|
325
|
+
if line.strip():
|
|
326
|
+
try:
|
|
327
|
+
events.append(json.loads(line))
|
|
328
|
+
except json.JSONDecodeError as e:
|
|
329
|
+
parse_errors.append(
|
|
330
|
+
{
|
|
331
|
+
"line_number": line_num,
|
|
332
|
+
"error": str(e),
|
|
333
|
+
"content": line[
|
|
334
|
+
:100
|
|
335
|
+
], # First 100 chars for debugging
|
|
336
|
+
}
|
|
337
|
+
)
|
|
338
|
+
continue
|
|
339
|
+
|
|
340
|
+
# Extract agent message
|
|
341
|
+
response = None
|
|
342
|
+
for event in events:
|
|
343
|
+
if event.get("type") == "item.completed":
|
|
344
|
+
item = event.get("item", {})
|
|
345
|
+
if item.get("type") == "agent_message":
|
|
346
|
+
response = item.get("text")
|
|
347
|
+
|
|
348
|
+
# Extract token usage from turn.completed event
|
|
349
|
+
tokens = None
|
|
350
|
+
for event in events:
|
|
351
|
+
if event.get("type") == "turn.completed":
|
|
352
|
+
usage = event.get("usage", {})
|
|
353
|
+
# Sum all token types
|
|
354
|
+
tokens = sum(usage.values())
|
|
355
|
+
|
|
356
|
+
# Publish live event: complete
|
|
357
|
+
duration = time.time() - start_time
|
|
358
|
+
success = result.returncode == 0
|
|
359
|
+
self._publish_live_event(
|
|
360
|
+
"spawner_complete",
|
|
361
|
+
"codex",
|
|
362
|
+
success=success,
|
|
363
|
+
duration=duration,
|
|
364
|
+
response=response[:200] if response else None,
|
|
365
|
+
tokens=tokens,
|
|
366
|
+
error="Command failed" if not success else None,
|
|
367
|
+
)
|
|
368
|
+
return AIResult(
|
|
369
|
+
success=success,
|
|
370
|
+
response=response or "",
|
|
371
|
+
tokens_used=tokens,
|
|
372
|
+
error=None if success else "Command failed",
|
|
373
|
+
raw_output={
|
|
374
|
+
"events": events,
|
|
375
|
+
"parse_errors": parse_errors if parse_errors else None,
|
|
376
|
+
},
|
|
377
|
+
tracked_events=tracked_events,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
except FileNotFoundError:
|
|
381
|
+
duration = time.time() - start_time
|
|
382
|
+
self._publish_live_event(
|
|
383
|
+
"spawner_complete",
|
|
384
|
+
"codex",
|
|
385
|
+
success=False,
|
|
386
|
+
duration=duration,
|
|
387
|
+
error="CLI not found",
|
|
388
|
+
)
|
|
389
|
+
return AIResult(
|
|
390
|
+
success=False,
|
|
391
|
+
response="",
|
|
392
|
+
tokens_used=None,
|
|
393
|
+
error="Codex CLI not found. Install from: https://github.com/openai/codex",
|
|
394
|
+
raw_output=None,
|
|
395
|
+
tracked_events=tracked_events,
|
|
396
|
+
)
|
|
397
|
+
except subprocess.TimeoutExpired as e:
|
|
398
|
+
duration = time.time() - start_time
|
|
399
|
+
self._publish_live_event(
|
|
400
|
+
"spawner_complete",
|
|
401
|
+
"codex",
|
|
402
|
+
success=False,
|
|
403
|
+
duration=duration,
|
|
404
|
+
error=f"Timed out after {timeout} seconds",
|
|
405
|
+
)
|
|
406
|
+
return AIResult(
|
|
407
|
+
success=False,
|
|
408
|
+
response="",
|
|
409
|
+
tokens_used=None,
|
|
410
|
+
error=f"Timed out after {timeout} seconds",
|
|
411
|
+
raw_output={
|
|
412
|
+
"partial_stdout": e.stdout.decode() if e.stdout else None,
|
|
413
|
+
"partial_stderr": e.stderr.decode() if e.stderr else None,
|
|
414
|
+
}
|
|
415
|
+
if e.stdout or e.stderr
|
|
416
|
+
else None,
|
|
417
|
+
tracked_events=tracked_events,
|
|
418
|
+
)
|
|
419
|
+
except Exception as e:
|
|
420
|
+
duration = time.time() - start_time
|
|
421
|
+
self._publish_live_event(
|
|
422
|
+
"spawner_complete",
|
|
423
|
+
"codex",
|
|
424
|
+
success=False,
|
|
425
|
+
duration=duration,
|
|
426
|
+
error=str(e),
|
|
427
|
+
)
|
|
428
|
+
return AIResult(
|
|
429
|
+
success=False,
|
|
430
|
+
response="",
|
|
431
|
+
tokens_used=None,
|
|
432
|
+
error=f"Unexpected error: {type(e).__name__}: {e}",
|
|
433
|
+
raw_output=None,
|
|
434
|
+
tracked_events=tracked_events,
|
|
435
|
+
)
|