gdmcode 0.1.0__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.
Files changed (131) hide show
  1. gdmcode-0.1.0.dist-info/METADATA +240 -0
  2. gdmcode-0.1.0.dist-info/RECORD +131 -0
  3. gdmcode-0.1.0.dist-info/WHEEL +4 -0
  4. gdmcode-0.1.0.dist-info/entry_points.txt +2 -0
  5. src/__init__.py +1 -0
  6. src/_internal/__init__.py +0 -0
  7. src/_internal/constants.py +244 -0
  8. src/_internal/domain_skills.py +339 -0
  9. src/agent/__init__.py +0 -0
  10. src/agent/commit_classifier.py +91 -0
  11. src/agent/context_budget.py +391 -0
  12. src/agent/daemon.py +681 -0
  13. src/agent/dag_validator.py +153 -0
  14. src/agent/debug_loop.py +473 -0
  15. src/agent/impact_analyzer.py +149 -0
  16. src/agent/impact_graph.py +117 -0
  17. src/agent/loop.py +1410 -0
  18. src/agent/orchestrator.py +141 -0
  19. src/agent/regression_guard.py +251 -0
  20. src/agent/review_gate.py +648 -0
  21. src/agent/risk_scorer.py +169 -0
  22. src/agent/self_healing.py +145 -0
  23. src/agent/smart_test_selector.py +89 -0
  24. src/agent/system_prompt.py +226 -0
  25. src/agent/task_tracker.py +320 -0
  26. src/agent/test_validator.py +210 -0
  27. src/agent/tool_orchestrator.py +402 -0
  28. src/agent/transcript.py +230 -0
  29. src/agent/verification_loop.py +133 -0
  30. src/agent/work_director.py +136 -0
  31. src/agent/worktree_manager.py +53 -0
  32. src/artifacts/__init__.py +16 -0
  33. src/artifacts/artifact_store.py +456 -0
  34. src/artifacts/verification_graph.py +75 -0
  35. src/auth.py +411 -0
  36. src/cli.py +1290 -0
  37. src/commands.py +1398 -0
  38. src/config.py +762 -0
  39. src/cost_tracker.py +348 -0
  40. src/db/__init__.py +4 -0
  41. src/db/migrations.py +337 -0
  42. src/enterprise/__init__.py +3 -0
  43. src/enterprise/audit_log.py +182 -0
  44. src/enterprise/identity.py +90 -0
  45. src/enterprise/rbac.py +100 -0
  46. src/enterprise/team_config.py +125 -0
  47. src/enterprise/usage_analytics.py +261 -0
  48. src/exceptions.py +207 -0
  49. src/git_workflow.py +651 -0
  50. src/integrations/__init__.py +6 -0
  51. src/integrations/github_actions.py +106 -0
  52. src/integrations/mcp_server.py +333 -0
  53. src/integrations/sentry_integration.py +100 -0
  54. src/integrations/sentry_server.py +82 -0
  55. src/integrations/webhook_security.py +19 -0
  56. src/main.py +27 -0
  57. src/memory/__init__.py +0 -0
  58. src/memory/code_index.py +376 -0
  59. src/memory/compressor.py +378 -0
  60. src/memory/context_memory.py +135 -0
  61. src/memory/continuous_memory.py +234 -0
  62. src/memory/conventions.py +495 -0
  63. src/memory/db.py +1119 -0
  64. src/memory/document_index.py +205 -0
  65. src/memory/file_cache.py +128 -0
  66. src/memory/project_scanner.py +178 -0
  67. src/memory/session_store.py +201 -0
  68. src/models/__init__.py +0 -0
  69. src/models/client.py +715 -0
  70. src/models/definitions.py +459 -0
  71. src/models/router.py +418 -0
  72. src/models/schemas.py +389 -0
  73. src/permissions.py +294 -0
  74. src/remote/__init__.py +5 -0
  75. src/remote/command_filter.py +33 -0
  76. src/remote/models.py +31 -0
  77. src/remote/permission_handler.py +79 -0
  78. src/remote/phone_ui.py +48 -0
  79. src/remote/protocol.py +59 -0
  80. src/remote/qr.py +65 -0
  81. src/remote/server.py +586 -0
  82. src/remote/token_manager.py +61 -0
  83. src/remote/tunnel.py +212 -0
  84. src/repl.py +475 -0
  85. src/runtime/__init__.py +1 -0
  86. src/runtime/branch_farm.py +372 -0
  87. src/runtime/replay.py +351 -0
  88. src/sandbox/__init__.py +2 -0
  89. src/sandbox/hermetic.py +214 -0
  90. src/sandbox/policy.py +44 -0
  91. src/sdk/__init__.py +3 -0
  92. src/sdk/plugin_base.py +39 -0
  93. src/sdk/plugin_host.py +100 -0
  94. src/sdk/plugin_loader.py +101 -0
  95. src/security.py +409 -0
  96. src/server/__init__.py +7 -0
  97. src/server/bridge.py +427 -0
  98. src/server/bridge_cli.py +103 -0
  99. src/server/bridge_client.py +170 -0
  100. src/server/protocol_version.py +103 -0
  101. src/session/__init__.py +10 -0
  102. src/session/event_fanout.py +46 -0
  103. src/session/input_broker.py +38 -0
  104. src/session/permission_bridge.py +100 -0
  105. src/tools/__init__.py +160 -0
  106. src/tools/_atomic.py +72 -0
  107. src/tools/agent_tools.py +423 -0
  108. src/tools/ask_user_tool.py +83 -0
  109. src/tools/bash_tool.py +384 -0
  110. src/tools/browser_tool.py +352 -0
  111. src/tools/browser_tools.py +179 -0
  112. src/tools/dep_tools.py +210 -0
  113. src/tools/document_reader.py +167 -0
  114. src/tools/document_tool.py +240 -0
  115. src/tools/document_writer.py +171 -0
  116. src/tools/impact_tools.py +240 -0
  117. src/tools/playwright_tool.py +172 -0
  118. src/tools/quality_tools.py +366 -0
  119. src/tools/read_tools.py +318 -0
  120. src/tools/result_cache.py +157 -0
  121. src/tools/search_tools.py +310 -0
  122. src/tools/shell_tools.py +311 -0
  123. src/tools/write_tools.py +337 -0
  124. src/voice/__init__.py +25 -0
  125. src/voice/audio_capture.py +92 -0
  126. src/voice/audio_playback.py +68 -0
  127. src/voice/errors.py +14 -0
  128. src/voice/models.py +35 -0
  129. src/voice/providers.py +143 -0
  130. src/voice/vad.py +55 -0
  131. src/voice/voice_loop.py +156 -0
@@ -0,0 +1,201 @@
1
+ """SessionStore — persists and resumes conversation sessions via GdmDatabase.
2
+
3
+ Handles all session lifecycle: create, resume, save checkpoint, close.
4
+ Works in tandem with TranscriptStore (in-memory) — SessionStore is the
5
+ durable layer.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ from dataclasses import dataclass
12
+ from datetime import datetime, timezone
13
+ from pathlib import Path
14
+ from typing import TYPE_CHECKING
15
+
16
+ from src.agent.transcript import Turn, TranscriptStore
17
+ from src.exceptions import DatabaseError
18
+
19
+ if TYPE_CHECKING:
20
+ from src.memory.db import GdmDatabase
21
+
22
+ __all__ = ["Session", "SessionStore"]
23
+
24
+ log = logging.getLogger(__name__)
25
+
26
+
27
+ @dataclass
28
+ class Session:
29
+ """Lightweight descriptor for a persisted session."""
30
+
31
+ session_id: str
32
+ project_id: str
33
+ created_at: datetime
34
+ updated_at: datetime
35
+ turn_count: int
36
+ cost_usd: float
37
+
38
+
39
+ class SessionStore:
40
+ """Creates, persists, and resumes gdm sessions.
41
+
42
+ Usage::
43
+
44
+ store = SessionStore(db, project_root=Path.cwd())
45
+ session_id = store.create()
46
+ transcript = store.load_transcript(session_id, max_tokens=120_000)
47
+ # ... agent loop ...
48
+ store.save_transcript(session_id, transcript)
49
+ store.close(session_id, cost_usd=0.04)
50
+ """
51
+
52
+ def __init__(self, db: GdmDatabase, project_root: Path) -> None:
53
+ self._db = db
54
+ self._project_root = project_root
55
+ self._project_id = self._ensure_project()
56
+
57
+ # ------------------------------------------------------------------
58
+ # Project helpers
59
+ # ------------------------------------------------------------------
60
+
61
+ def _ensure_project(self) -> str:
62
+ """Return existing project_id or create a new row."""
63
+ root_str = str(self._project_root)
64
+ row = self._db.execute_one(
65
+ "SELECT project_id FROM projects WHERE root_path = ?", (root_str,)
66
+ )
67
+ if row:
68
+ return row["project_id"]
69
+
70
+ import uuid
71
+
72
+ pid = str(uuid.uuid4())
73
+ self._db.execute(
74
+ "INSERT INTO projects (project_id, root_path, name) VALUES (?, ?, ?)",
75
+ (pid, root_str, self._project_root.name),
76
+ )
77
+ return pid
78
+
79
+ # ------------------------------------------------------------------
80
+ # Session lifecycle
81
+ # ------------------------------------------------------------------
82
+
83
+ def create(self) -> str:
84
+ """Create a new session row. Returns session_id (UUID)."""
85
+ import uuid
86
+
87
+ sid = str(uuid.uuid4())
88
+ now = datetime.now(timezone.utc).isoformat()
89
+ try:
90
+ self._db.execute(
91
+ """INSERT INTO sessions
92
+ (session_id, project_id, created_at, updated_at, turn_count, cost_usd)
93
+ VALUES (?, ?, ?, ?, 0, 0.0)""",
94
+ (sid, self._project_id, now, now),
95
+ )
96
+ except Exception as exc:
97
+ raise DatabaseError(f"Failed to create session: {exc}") from exc
98
+ log.debug("Created session %s", sid)
99
+ return sid
100
+
101
+ def load_transcript(self, session_id: str, max_tokens: int = 120_000) -> TranscriptStore:
102
+ """Load a persisted transcript from the database.
103
+
104
+ Returns an empty TranscriptStore if no turns exist.
105
+ """
106
+ rows = self._db.execute_all(
107
+ "SELECT role, content, tokens, tool_name, tool_call_id, tool_calls_json "
108
+ "FROM memory WHERE session_id = ? ORDER BY id ASC",
109
+ (session_id,),
110
+ )
111
+ turns: list[Turn] = []
112
+ for row in rows:
113
+ tool_calls = None
114
+ if row["tool_calls_json"]:
115
+ try:
116
+ tool_calls = json.loads(row["tool_calls_json"])
117
+ except (json.JSONDecodeError, TypeError):
118
+ log.warning("Corrupt tool_calls_json for session %s — skipping", session_id)
119
+ turns.append(
120
+ Turn(
121
+ role=row["role"],
122
+ content=row["content"],
123
+ tokens=row["tokens"] or 0,
124
+ tool_name=row["tool_name"],
125
+ tool_call_id=row["tool_call_id"],
126
+ tool_calls=tool_calls,
127
+ )
128
+ )
129
+ return TranscriptStore.from_turns(turns, max_tokens=max_tokens)
130
+
131
+ def save_transcript(self, session_id: str, store: TranscriptStore) -> None:
132
+ """Overwrite the persisted transcript with the current in-memory state.
133
+
134
+ Routes through memory_save_turns() so turn_index is assigned correctly
135
+ and the UNIQUE(session_id, turn_index) constraint is satisfied on every call.
136
+ """
137
+ now = datetime.now(timezone.utc).isoformat()
138
+ try:
139
+ turns = [
140
+ {
141
+ "role": t.role,
142
+ "content": t.content,
143
+ "tokens": t.tokens,
144
+ "tool_name": t.tool_name,
145
+ "tool_call_id": t.tool_call_id,
146
+ "tool_calls": t.tool_calls,
147
+ }
148
+ for t in store
149
+ ]
150
+ self._db.memory_save_turns(session_id, turns)
151
+ self._db.execute(
152
+ "UPDATE sessions SET updated_at = ?, turn_count = ? WHERE session_id = ?",
153
+ (now, store.turn_count, session_id),
154
+ )
155
+ except Exception as exc:
156
+ raise DatabaseError(f"Failed to save transcript for {session_id}: {exc}") from exc
157
+
158
+ def close(self, session_id: str, cost_tracker: object = None) -> None:
159
+ """Mark session closed. Delegates final cost write to cost_tracker if provided.
160
+
161
+ Args:
162
+ session_id: The session to close.
163
+ cost_tracker: CostTracker instance — calls flush_to_db() to persist
164
+ the authoritative total. If None, cost_usd is left as-is.
165
+ """
166
+ now = datetime.now(timezone.utc).isoformat()
167
+ self._db.execute(
168
+ "UPDATE sessions SET updated_at = ? WHERE session_id = ?",
169
+ (now, session_id),
170
+ )
171
+ if cost_tracker is not None:
172
+ cost_tracker.flush_to_db(self._db) # type: ignore[union-attr]
173
+ log.info(
174
+ "Session %s closed. Total cost: $%.4f",
175
+ session_id,
176
+ getattr(cost_tracker, "session_total_usd", 0.0),
177
+ )
178
+ else:
179
+ log.info("Session %s closed.", session_id)
180
+
181
+ def list_recent(self, limit: int = 10) -> list[Session]:
182
+ """Return the N most recent sessions for this project."""
183
+ rows = self._db.execute_all(
184
+ """SELECT session_id, project_id, created_at, updated_at, turn_count, cost_usd
185
+ FROM sessions WHERE project_id = ?
186
+ ORDER BY updated_at DESC LIMIT ?""",
187
+ (self._project_id, limit),
188
+ )
189
+ sessions: list[Session] = []
190
+ for row in rows:
191
+ sessions.append(
192
+ Session(
193
+ session_id=row["session_id"],
194
+ project_id=row["project_id"],
195
+ created_at=datetime.fromisoformat(row["created_at"]),
196
+ updated_at=datetime.fromisoformat(row["updated_at"]),
197
+ turn_count=row["turn_count"],
198
+ cost_usd=row["cost_usd"],
199
+ )
200
+ )
201
+ return sessions
src/models/__init__.py ADDED
File without changes