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.
- gdmcode-0.1.0.dist-info/METADATA +240 -0
- gdmcode-0.1.0.dist-info/RECORD +131 -0
- gdmcode-0.1.0.dist-info/WHEEL +4 -0
- gdmcode-0.1.0.dist-info/entry_points.txt +2 -0
- src/__init__.py +1 -0
- src/_internal/__init__.py +0 -0
- src/_internal/constants.py +244 -0
- src/_internal/domain_skills.py +339 -0
- src/agent/__init__.py +0 -0
- src/agent/commit_classifier.py +91 -0
- src/agent/context_budget.py +391 -0
- src/agent/daemon.py +681 -0
- src/agent/dag_validator.py +153 -0
- src/agent/debug_loop.py +473 -0
- src/agent/impact_analyzer.py +149 -0
- src/agent/impact_graph.py +117 -0
- src/agent/loop.py +1410 -0
- src/agent/orchestrator.py +141 -0
- src/agent/regression_guard.py +251 -0
- src/agent/review_gate.py +648 -0
- src/agent/risk_scorer.py +169 -0
- src/agent/self_healing.py +145 -0
- src/agent/smart_test_selector.py +89 -0
- src/agent/system_prompt.py +226 -0
- src/agent/task_tracker.py +320 -0
- src/agent/test_validator.py +210 -0
- src/agent/tool_orchestrator.py +402 -0
- src/agent/transcript.py +230 -0
- src/agent/verification_loop.py +133 -0
- src/agent/work_director.py +136 -0
- src/agent/worktree_manager.py +53 -0
- src/artifacts/__init__.py +16 -0
- src/artifacts/artifact_store.py +456 -0
- src/artifacts/verification_graph.py +75 -0
- src/auth.py +411 -0
- src/cli.py +1290 -0
- src/commands.py +1398 -0
- src/config.py +762 -0
- src/cost_tracker.py +348 -0
- src/db/__init__.py +4 -0
- src/db/migrations.py +337 -0
- src/enterprise/__init__.py +3 -0
- src/enterprise/audit_log.py +182 -0
- src/enterprise/identity.py +90 -0
- src/enterprise/rbac.py +100 -0
- src/enterprise/team_config.py +125 -0
- src/enterprise/usage_analytics.py +261 -0
- src/exceptions.py +207 -0
- src/git_workflow.py +651 -0
- src/integrations/__init__.py +6 -0
- src/integrations/github_actions.py +106 -0
- src/integrations/mcp_server.py +333 -0
- src/integrations/sentry_integration.py +100 -0
- src/integrations/sentry_server.py +82 -0
- src/integrations/webhook_security.py +19 -0
- src/main.py +27 -0
- src/memory/__init__.py +0 -0
- src/memory/code_index.py +376 -0
- src/memory/compressor.py +378 -0
- src/memory/context_memory.py +135 -0
- src/memory/continuous_memory.py +234 -0
- src/memory/conventions.py +495 -0
- src/memory/db.py +1119 -0
- src/memory/document_index.py +205 -0
- src/memory/file_cache.py +128 -0
- src/memory/project_scanner.py +178 -0
- src/memory/session_store.py +201 -0
- src/models/__init__.py +0 -0
- src/models/client.py +715 -0
- src/models/definitions.py +459 -0
- src/models/router.py +418 -0
- src/models/schemas.py +389 -0
- src/permissions.py +294 -0
- src/remote/__init__.py +5 -0
- src/remote/command_filter.py +33 -0
- src/remote/models.py +31 -0
- src/remote/permission_handler.py +79 -0
- src/remote/phone_ui.py +48 -0
- src/remote/protocol.py +59 -0
- src/remote/qr.py +65 -0
- src/remote/server.py +586 -0
- src/remote/token_manager.py +61 -0
- src/remote/tunnel.py +212 -0
- src/repl.py +475 -0
- src/runtime/__init__.py +1 -0
- src/runtime/branch_farm.py +372 -0
- src/runtime/replay.py +351 -0
- src/sandbox/__init__.py +2 -0
- src/sandbox/hermetic.py +214 -0
- src/sandbox/policy.py +44 -0
- src/sdk/__init__.py +3 -0
- src/sdk/plugin_base.py +39 -0
- src/sdk/plugin_host.py +100 -0
- src/sdk/plugin_loader.py +101 -0
- src/security.py +409 -0
- src/server/__init__.py +7 -0
- src/server/bridge.py +427 -0
- src/server/bridge_cli.py +103 -0
- src/server/bridge_client.py +170 -0
- src/server/protocol_version.py +103 -0
- src/session/__init__.py +10 -0
- src/session/event_fanout.py +46 -0
- src/session/input_broker.py +38 -0
- src/session/permission_bridge.py +100 -0
- src/tools/__init__.py +160 -0
- src/tools/_atomic.py +72 -0
- src/tools/agent_tools.py +423 -0
- src/tools/ask_user_tool.py +83 -0
- src/tools/bash_tool.py +384 -0
- src/tools/browser_tool.py +352 -0
- src/tools/browser_tools.py +179 -0
- src/tools/dep_tools.py +210 -0
- src/tools/document_reader.py +167 -0
- src/tools/document_tool.py +240 -0
- src/tools/document_writer.py +171 -0
- src/tools/impact_tools.py +240 -0
- src/tools/playwright_tool.py +172 -0
- src/tools/quality_tools.py +366 -0
- src/tools/read_tools.py +318 -0
- src/tools/result_cache.py +157 -0
- src/tools/search_tools.py +310 -0
- src/tools/shell_tools.py +311 -0
- src/tools/write_tools.py +337 -0
- src/voice/__init__.py +25 -0
- src/voice/audio_capture.py +92 -0
- src/voice/audio_playback.py +68 -0
- src/voice/errors.py +14 -0
- src/voice/models.py +35 -0
- src/voice/providers.py +143 -0
- src/voice/vad.py +55 -0
- 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
|