voidx 1.0.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.
- voidx/__init__.py +3 -0
- voidx/agent/__init__.py +0 -0
- voidx/agent/agents.py +439 -0
- voidx/agent/attachments.py +235 -0
- voidx/agent/graph.py +463 -0
- voidx/agent/graph_components/__init__.py +1 -0
- voidx/agent/graph_components/compaction.py +268 -0
- voidx/agent/graph_components/permissions.py +139 -0
- voidx/agent/graph_components/run_loop.py +532 -0
- voidx/agent/graph_components/runtime.py +14 -0
- voidx/agent/graph_components/streaming.py +351 -0
- voidx/agent/graph_components/subagent.py +278 -0
- voidx/agent/graph_components/tool_execution.py +208 -0
- voidx/agent/runtime_context.py +368 -0
- voidx/agent/slash.py +466 -0
- voidx/agent/slash_components/__init__.py +1 -0
- voidx/agent/slash_components/code_ide.py +68 -0
- voidx/agent/slash_components/lsp.py +105 -0
- voidx/agent/slash_components/mcp.py +332 -0
- voidx/agent/slash_components/model.py +419 -0
- voidx/agent/slash_components/runtime.py +55 -0
- voidx/agent/slash_components/skills.py +94 -0
- voidx/agent/state.py +32 -0
- voidx/agent/task_state.py +278 -0
- voidx/agent/tool_filters.py +27 -0
- voidx/config.py +707 -0
- voidx/llm/__init__.py +0 -0
- voidx/llm/catalog.py +188 -0
- voidx/llm/compaction.py +267 -0
- voidx/llm/context.py +43 -0
- voidx/llm/instruction.py +220 -0
- voidx/llm/provider.py +312 -0
- voidx/llm/usage.py +341 -0
- voidx/lsp/__init__.py +30 -0
- voidx/lsp/client.py +259 -0
- voidx/lsp/config.py +172 -0
- voidx/lsp/detector.py +512 -0
- voidx/lsp/errors.py +19 -0
- voidx/lsp/manager.py +280 -0
- voidx/lsp/schema.py +179 -0
- voidx/lsp/service.py +103 -0
- voidx/main.py +154 -0
- voidx/mcp/__init__.py +33 -0
- voidx/mcp/client.py +458 -0
- voidx/mcp/manager.py +267 -0
- voidx/mcp/schema.py +112 -0
- voidx/mcp/tool.py +122 -0
- voidx/mcp_servers/__init__.py +1 -0
- voidx/mcp_servers/web.py +104 -0
- voidx/memory/__init__.py +0 -0
- voidx/memory/context_frames.py +188 -0
- voidx/memory/model_profiles.py +98 -0
- voidx/memory/runtime_state.py +240 -0
- voidx/memory/session.py +272 -0
- voidx/memory/store.py +245 -0
- voidx/memory/transcript.py +137 -0
- voidx/permission/__init__.py +28 -0
- voidx/permission/engine.py +430 -0
- voidx/permission/evaluate.py +114 -0
- voidx/permission/sandbox.py +280 -0
- voidx/permission/schema.py +24 -0
- voidx/permission/service.py +314 -0
- voidx/permission/wildcard.py +34 -0
- voidx/skills/__init__.py +18 -0
- voidx/skills/bundled/superpowers/receiving-code-review/SKILL.md +30 -0
- voidx/skills/bundled/superpowers/requesting-code-review/SKILL.md +27 -0
- voidx/skills/bundled/superpowers/systematic-debugging/SKILL.md +36 -0
- voidx/skills/bundled/superpowers/test-driven-development/SKILL.md +33 -0
- voidx/skills/bundled/superpowers/verification-before-completion/SKILL.md +31 -0
- voidx/skills/bundled/superpowers/writing-plans/SKILL.md +27 -0
- voidx/skills/policy.py +97 -0
- voidx/skills/registry.py +162 -0
- voidx/skills/schema.py +47 -0
- voidx/skills/service.py +199 -0
- voidx/tools/__init__.py +0 -0
- voidx/tools/agent.py +81 -0
- voidx/tools/base.py +86 -0
- voidx/tools/bash.py +105 -0
- voidx/tools/file_ops.py +193 -0
- voidx/tools/lsp.py +155 -0
- voidx/tools/registry.py +104 -0
- voidx/tools/repomap.py +238 -0
- voidx/tools/search.py +162 -0
- voidx/tools/task_status.py +57 -0
- voidx/tools/task_tracker.py +81 -0
- voidx/tools/todo.py +82 -0
- voidx/tools/web_content.py +357 -0
- voidx/tools/web_mcp.py +107 -0
- voidx/tools/webfetch.py +155 -0
- voidx/tools/websearch.py +276 -0
- voidx/ui/__init__.py +0 -0
- voidx/ui/app.py +1033 -0
- voidx/ui/app_components/__init__.py +1 -0
- voidx/ui/app_components/clipboard_image.py +245 -0
- voidx/ui/app_components/commands.py +18 -0
- voidx/ui/app_components/controls.py +29 -0
- voidx/ui/app_components/file_picker.py +115 -0
- voidx/ui/app_components/formatting.py +187 -0
- voidx/ui/app_components/git_changes.py +51 -0
- voidx/ui/app_components/rendering.py +1169 -0
- voidx/ui/browse.py +160 -0
- voidx/ui/capture.py +169 -0
- voidx/ui/code_ide.py +251 -0
- voidx/ui/commands.py +83 -0
- voidx/ui/console.py +381 -0
- voidx/ui/console_components/__init__.py +1 -0
- voidx/ui/console_components/formatting.py +96 -0
- voidx/ui/console_components/streaming.py +253 -0
- voidx/ui/diff.py +331 -0
- voidx/ui/dock.py +372 -0
- voidx/ui/dock_components/__init__.py +1 -0
- voidx/ui/dock_components/formatting.py +123 -0
- voidx/ui/dock_components/nodes.py +401 -0
- voidx/ui/dock_components/state.py +51 -0
- voidx/ui/event_components/__init__.py +1 -0
- voidx/ui/event_components/schema.py +249 -0
- voidx/ui/events.py +341 -0
- voidx/ui/session_changes.py +163 -0
- voidx/ui/startup.py +161 -0
- voidx/ui/transcript.py +148 -0
- voidx/ui/tree.py +316 -0
- voidx-1.0.0.dist-info/METADATA +59 -0
- voidx-1.0.0.dist-info/RECORD +126 -0
- voidx-1.0.0.dist-info/WHEEL +5 -0
- voidx-1.0.0.dist-info/entry_points.txt +2 -0
- voidx-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Compiled LLM context frame snapshots."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage, ToolMessage
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
from voidx.memory.session import _now
|
|
13
|
+
from voidx.memory.store import _execute_commit, _fetch_all
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ContextFrameRecord(BaseModel):
|
|
17
|
+
id: int | None = None
|
|
18
|
+
session_id: str
|
|
19
|
+
user_message_id: int | None = None
|
|
20
|
+
frame_kind: str = "main"
|
|
21
|
+
agent_role: str = "orchestrator"
|
|
22
|
+
provider: str
|
|
23
|
+
model: str
|
|
24
|
+
prefix_hash: str
|
|
25
|
+
frame_hash: str
|
|
26
|
+
message_count: int
|
|
27
|
+
token_estimate: int = 0
|
|
28
|
+
messages: list[dict[str, Any]]
|
|
29
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
30
|
+
created_at: str = Field(default_factory=_now)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build_context_frame(
|
|
34
|
+
*,
|
|
35
|
+
session_id: str,
|
|
36
|
+
messages: list[BaseMessage],
|
|
37
|
+
provider: str,
|
|
38
|
+
model: str,
|
|
39
|
+
frame_kind: str = "main",
|
|
40
|
+
agent_role: str = "orchestrator",
|
|
41
|
+
user_message_id: int | None = None,
|
|
42
|
+
token_estimate: int = 0,
|
|
43
|
+
metadata: dict[str, Any] | None = None,
|
|
44
|
+
) -> ContextFrameRecord:
|
|
45
|
+
serialized = [_serialize_message(message) for message in messages]
|
|
46
|
+
return ContextFrameRecord(
|
|
47
|
+
session_id=session_id,
|
|
48
|
+
user_message_id=user_message_id,
|
|
49
|
+
frame_kind=frame_kind,
|
|
50
|
+
agent_role=agent_role,
|
|
51
|
+
provider=provider,
|
|
52
|
+
model=model,
|
|
53
|
+
prefix_hash=_hash_payload(_stable_prefix_payload(serialized)),
|
|
54
|
+
frame_hash=_hash_payload(serialized),
|
|
55
|
+
message_count=len(serialized),
|
|
56
|
+
token_estimate=token_estimate,
|
|
57
|
+
messages=serialized,
|
|
58
|
+
metadata=metadata or {},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
async def save_context_frame(record: ContextFrameRecord) -> int:
|
|
63
|
+
cur = await _execute_commit(
|
|
64
|
+
"""INSERT INTO context_frames (
|
|
65
|
+
session_id, user_message_id, frame_kind, agent_role, provider,
|
|
66
|
+
model, prefix_hash, frame_hash, message_count, token_estimate,
|
|
67
|
+
messages_json, metadata_json, created_at
|
|
68
|
+
)
|
|
69
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
70
|
+
(
|
|
71
|
+
record.session_id,
|
|
72
|
+
record.user_message_id,
|
|
73
|
+
record.frame_kind,
|
|
74
|
+
record.agent_role,
|
|
75
|
+
record.provider,
|
|
76
|
+
record.model,
|
|
77
|
+
record.prefix_hash,
|
|
78
|
+
record.frame_hash,
|
|
79
|
+
record.message_count,
|
|
80
|
+
record.token_estimate,
|
|
81
|
+
json.dumps(record.messages, ensure_ascii=False, sort_keys=True),
|
|
82
|
+
json.dumps(record.metadata, ensure_ascii=False, sort_keys=True),
|
|
83
|
+
record.created_at,
|
|
84
|
+
),
|
|
85
|
+
)
|
|
86
|
+
return cur.lastrowid
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def save_context_frame_from_messages(
|
|
90
|
+
*,
|
|
91
|
+
session_id: str,
|
|
92
|
+
messages: list[BaseMessage],
|
|
93
|
+
provider: str,
|
|
94
|
+
model: str,
|
|
95
|
+
frame_kind: str = "main",
|
|
96
|
+
agent_role: str = "orchestrator",
|
|
97
|
+
user_message_id: int | None = None,
|
|
98
|
+
token_estimate: int = 0,
|
|
99
|
+
metadata: dict[str, Any] | None = None,
|
|
100
|
+
) -> int:
|
|
101
|
+
return await save_context_frame(build_context_frame(
|
|
102
|
+
session_id=session_id,
|
|
103
|
+
messages=messages,
|
|
104
|
+
provider=provider,
|
|
105
|
+
model=model,
|
|
106
|
+
frame_kind=frame_kind,
|
|
107
|
+
agent_role=agent_role,
|
|
108
|
+
user_message_id=user_message_id,
|
|
109
|
+
token_estimate=token_estimate,
|
|
110
|
+
metadata=metadata,
|
|
111
|
+
))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def load_context_frames(session_id: str, limit: int = 50) -> list[ContextFrameRecord]:
|
|
115
|
+
rows = await _fetch_all(
|
|
116
|
+
"""SELECT * FROM context_frames
|
|
117
|
+
WHERE session_id = ?
|
|
118
|
+
ORDER BY id DESC
|
|
119
|
+
LIMIT ?""",
|
|
120
|
+
(session_id, limit),
|
|
121
|
+
)
|
|
122
|
+
records = [
|
|
123
|
+
ContextFrameRecord(
|
|
124
|
+
id=row["id"],
|
|
125
|
+
session_id=row["session_id"],
|
|
126
|
+
user_message_id=row["user_message_id"],
|
|
127
|
+
frame_kind=row["frame_kind"],
|
|
128
|
+
agent_role=row["agent_role"],
|
|
129
|
+
provider=row["provider"],
|
|
130
|
+
model=row["model"],
|
|
131
|
+
prefix_hash=row["prefix_hash"],
|
|
132
|
+
frame_hash=row["frame_hash"],
|
|
133
|
+
message_count=row["message_count"],
|
|
134
|
+
token_estimate=row["token_estimate"],
|
|
135
|
+
messages=json.loads(row["messages_json"]),
|
|
136
|
+
metadata=json.loads(row["metadata_json"]),
|
|
137
|
+
created_at=row["created_at"],
|
|
138
|
+
)
|
|
139
|
+
for row in rows
|
|
140
|
+
]
|
|
141
|
+
return list(reversed(records))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _serialize_message(message: BaseMessage) -> dict[str, Any]:
|
|
145
|
+
payload: dict[str, Any] = {
|
|
146
|
+
"role": _message_role(message),
|
|
147
|
+
"content": _json_safe(getattr(message, "content", "")),
|
|
148
|
+
}
|
|
149
|
+
if isinstance(message, AIMessage) and getattr(message, "tool_calls", None):
|
|
150
|
+
payload["tool_calls"] = _json_safe(message.tool_calls)
|
|
151
|
+
if isinstance(message, ToolMessage):
|
|
152
|
+
payload["tool_call_id"] = getattr(message, "tool_call_id", "")
|
|
153
|
+
return payload
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _message_role(message: BaseMessage) -> str:
|
|
157
|
+
if isinstance(message, SystemMessage):
|
|
158
|
+
return "system"
|
|
159
|
+
if isinstance(message, HumanMessage):
|
|
160
|
+
return "user"
|
|
161
|
+
if isinstance(message, AIMessage):
|
|
162
|
+
return "assistant"
|
|
163
|
+
if isinstance(message, ToolMessage):
|
|
164
|
+
return "tool"
|
|
165
|
+
return getattr(message, "type", message.__class__.__name__)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _stable_prefix_payload(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
169
|
+
if not messages or messages[0].get("role") != "system":
|
|
170
|
+
return []
|
|
171
|
+
content = str(messages[0].get("content", ""))
|
|
172
|
+
stable_content = content.split("\n\n## Current Date", 1)[0]
|
|
173
|
+
return [{"role": "system", "content": stable_content}]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _hash_payload(payload: Any) -> str:
|
|
177
|
+
encoded = json.dumps(
|
|
178
|
+
payload,
|
|
179
|
+
ensure_ascii=False,
|
|
180
|
+
sort_keys=True,
|
|
181
|
+
separators=(",", ":"),
|
|
182
|
+
default=str,
|
|
183
|
+
).encode("utf-8")
|
|
184
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _json_safe(value: Any) -> Any:
|
|
188
|
+
return json.loads(json.dumps(value, ensure_ascii=False, sort_keys=True, default=str))
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""User-level model profile persistence."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from voidx.memory.session import _now
|
|
8
|
+
from voidx.memory.store import _get_db, _write_lock
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class ModelProfileRow:
|
|
13
|
+
name: str
|
|
14
|
+
provider: str
|
|
15
|
+
model: str
|
|
16
|
+
api_key: str = ""
|
|
17
|
+
base_url: str | None = None
|
|
18
|
+
protocol: str | None = None
|
|
19
|
+
|
|
20
|
+
def list_model_profiles() -> list[ModelProfileRow]:
|
|
21
|
+
conn = _get_db()
|
|
22
|
+
rows = conn.execute(
|
|
23
|
+
"""SELECT name, provider, model, api_key, base_url, protocol
|
|
24
|
+
FROM model_profiles
|
|
25
|
+
ORDER BY updated_at DESC, name ASC"""
|
|
26
|
+
).fetchall()
|
|
27
|
+
return [_row_to_profile(row) for row in rows]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_model_profile(name: str) -> ModelProfileRow | None:
|
|
31
|
+
conn = _get_db()
|
|
32
|
+
row = conn.execute(
|
|
33
|
+
"""SELECT name, provider, model, api_key, base_url, protocol
|
|
34
|
+
FROM model_profiles
|
|
35
|
+
WHERE name = ?""",
|
|
36
|
+
(name,),
|
|
37
|
+
).fetchone()
|
|
38
|
+
return _row_to_profile(row) if row else None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def first_model_profile_for_provider(provider: str) -> ModelProfileRow | None:
|
|
42
|
+
conn = _get_db()
|
|
43
|
+
row = conn.execute(
|
|
44
|
+
"""SELECT name, provider, model, api_key, base_url, protocol
|
|
45
|
+
FROM model_profiles
|
|
46
|
+
WHERE provider = ?
|
|
47
|
+
ORDER BY updated_at DESC, name ASC
|
|
48
|
+
LIMIT 1""",
|
|
49
|
+
(provider,),
|
|
50
|
+
).fetchone()
|
|
51
|
+
return _row_to_profile(row) if row else None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def save_model_profile(profile: ModelProfileRow) -> None:
|
|
55
|
+
now = _now()
|
|
56
|
+
conn = _get_db()
|
|
57
|
+
with _write_lock:
|
|
58
|
+
conn.execute(
|
|
59
|
+
"""INSERT INTO model_profiles
|
|
60
|
+
(name, provider, model, api_key, base_url, protocol, created_at, updated_at)
|
|
61
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
62
|
+
ON CONFLICT(name) DO UPDATE SET
|
|
63
|
+
provider = excluded.provider,
|
|
64
|
+
model = excluded.model,
|
|
65
|
+
api_key = excluded.api_key,
|
|
66
|
+
base_url = excluded.base_url,
|
|
67
|
+
protocol = excluded.protocol,
|
|
68
|
+
updated_at = excluded.updated_at""",
|
|
69
|
+
(
|
|
70
|
+
profile.name,
|
|
71
|
+
profile.provider,
|
|
72
|
+
profile.model,
|
|
73
|
+
profile.api_key,
|
|
74
|
+
profile.base_url,
|
|
75
|
+
profile.protocol,
|
|
76
|
+
now,
|
|
77
|
+
now,
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
conn.commit()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def delete_model_profile(name: str) -> None:
|
|
84
|
+
conn = _get_db()
|
|
85
|
+
with _write_lock:
|
|
86
|
+
conn.execute("DELETE FROM model_profiles WHERE name = ?", (name,))
|
|
87
|
+
conn.commit()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _row_to_profile(row) -> ModelProfileRow:
|
|
91
|
+
return ModelProfileRow(
|
|
92
|
+
name=row["name"],
|
|
93
|
+
provider=row["provider"],
|
|
94
|
+
model=row["model"],
|
|
95
|
+
api_key=row["api_key"],
|
|
96
|
+
base_url=row["base_url"],
|
|
97
|
+
protocol=row["protocol"],
|
|
98
|
+
)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""Structured runtime state persistence for session resume."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from voidx.agent.runtime_context import InteractionMode, TaskIntent
|
|
8
|
+
from voidx.agent.task_state import TaskPhase, TaskRun, TaskRunStatus, TaskState
|
|
9
|
+
from voidx.memory.session import _now
|
|
10
|
+
from voidx.memory.store import _execute_commit, _fetch_one
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RuntimeStateSnapshot(BaseModel):
|
|
14
|
+
interaction_mode: InteractionMode = InteractionMode.AUTO
|
|
15
|
+
task_state: TaskState = Field(default_factory=TaskState)
|
|
16
|
+
task_run: TaskRun = Field(default_factory=TaskRun)
|
|
17
|
+
compaction_summary: str = ""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class MessageRuntimeSnapshot(BaseModel):
|
|
21
|
+
message_id: int
|
|
22
|
+
session_id: str
|
|
23
|
+
interaction_mode: InteractionMode = InteractionMode.AUTO
|
|
24
|
+
task_intent: TaskIntent = TaskIntent.CHAT
|
|
25
|
+
implementation_allowed: bool = False
|
|
26
|
+
intent_resolution_reason: str = ""
|
|
27
|
+
goal: str = ""
|
|
28
|
+
goal_phase: str = TaskPhase.CLARIFY.value
|
|
29
|
+
goal_status: str = TaskRunStatus.IDLE.value
|
|
30
|
+
goal_turn_count: int = 0
|
|
31
|
+
awaiting_implementation_approval: bool = False
|
|
32
|
+
approved_scope: str = ""
|
|
33
|
+
created_at: str = Field(default_factory=_now)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def save_runtime_state(session_id: str, snapshot: RuntimeStateSnapshot) -> None:
|
|
37
|
+
await save_session_runtime_state(
|
|
38
|
+
session_id,
|
|
39
|
+
snapshot.interaction_mode,
|
|
40
|
+
snapshot.task_state,
|
|
41
|
+
snapshot.compaction_summary,
|
|
42
|
+
)
|
|
43
|
+
await save_session_task_run(session_id, snapshot.task_run)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
async def load_runtime_state(session_id: str) -> RuntimeStateSnapshot:
|
|
47
|
+
return RuntimeStateSnapshot(
|
|
48
|
+
interaction_mode=await load_interaction_mode(session_id),
|
|
49
|
+
task_state=await load_task_state(session_id),
|
|
50
|
+
task_run=await load_task_run(session_id),
|
|
51
|
+
compaction_summary=await load_compaction_summary(session_id),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def save_session_runtime_state(
|
|
56
|
+
session_id: str,
|
|
57
|
+
interaction_mode: InteractionMode,
|
|
58
|
+
task_state: TaskState,
|
|
59
|
+
compaction_summary: str = "",
|
|
60
|
+
) -> None:
|
|
61
|
+
await _execute_commit(
|
|
62
|
+
"""INSERT INTO session_runtime_state (
|
|
63
|
+
session_id, interaction_mode, current_intent, previous_intent,
|
|
64
|
+
current_goal, awaiting_implementation_approval, approved_scope,
|
|
65
|
+
last_plan_summary, compaction_summary, updated_at
|
|
66
|
+
)
|
|
67
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
68
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
69
|
+
interaction_mode = excluded.interaction_mode,
|
|
70
|
+
current_intent = excluded.current_intent,
|
|
71
|
+
previous_intent = excluded.previous_intent,
|
|
72
|
+
current_goal = excluded.current_goal,
|
|
73
|
+
awaiting_implementation_approval = excluded.awaiting_implementation_approval,
|
|
74
|
+
approved_scope = excluded.approved_scope,
|
|
75
|
+
last_plan_summary = excluded.last_plan_summary,
|
|
76
|
+
compaction_summary = excluded.compaction_summary,
|
|
77
|
+
updated_at = excluded.updated_at""",
|
|
78
|
+
(
|
|
79
|
+
session_id,
|
|
80
|
+
interaction_mode.value,
|
|
81
|
+
task_state.current_intent.value,
|
|
82
|
+
task_state.previous_intent.value if task_state.previous_intent else None,
|
|
83
|
+
task_state.current_goal,
|
|
84
|
+
1 if task_state.awaiting_implementation_approval else 0,
|
|
85
|
+
task_state.approved_scope,
|
|
86
|
+
task_state.last_plan_summary,
|
|
87
|
+
compaction_summary,
|
|
88
|
+
_now(),
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def load_interaction_mode(session_id: str) -> InteractionMode:
|
|
94
|
+
row = await _fetch_one(
|
|
95
|
+
"SELECT interaction_mode FROM session_runtime_state WHERE session_id = ?",
|
|
96
|
+
(session_id,),
|
|
97
|
+
)
|
|
98
|
+
if not row:
|
|
99
|
+
return InteractionMode.AUTO
|
|
100
|
+
return InteractionMode.parse(row["interaction_mode"])
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
async def load_task_state(session_id: str) -> TaskState:
|
|
104
|
+
row = await _fetch_one(
|
|
105
|
+
"SELECT * FROM session_runtime_state WHERE session_id = ?",
|
|
106
|
+
(session_id,),
|
|
107
|
+
)
|
|
108
|
+
if not row:
|
|
109
|
+
return TaskState()
|
|
110
|
+
return TaskState(
|
|
111
|
+
current_intent=TaskIntent(row["current_intent"]),
|
|
112
|
+
previous_intent=TaskIntent(row["previous_intent"]) if row["previous_intent"] else None,
|
|
113
|
+
current_goal=row["current_goal"],
|
|
114
|
+
awaiting_implementation_approval=bool(row["awaiting_implementation_approval"]),
|
|
115
|
+
approved_scope=row["approved_scope"],
|
|
116
|
+
last_plan_summary=row["last_plan_summary"],
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
async def load_compaction_summary(session_id: str) -> str:
|
|
121
|
+
row = await _fetch_one(
|
|
122
|
+
"SELECT compaction_summary FROM session_runtime_state WHERE session_id = ?",
|
|
123
|
+
(session_id,),
|
|
124
|
+
)
|
|
125
|
+
if not row:
|
|
126
|
+
return ""
|
|
127
|
+
return row["compaction_summary"] if "compaction_summary" in row.keys() else ""
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
async def save_session_task_run(session_id: str, task_run: TaskRun) -> None:
|
|
131
|
+
await _execute_commit(
|
|
132
|
+
"""INSERT INTO session_task_runs (
|
|
133
|
+
session_id, goal, phase, status, approved_scope,
|
|
134
|
+
awaiting_implementation_approval, turn_count, updated_at
|
|
135
|
+
)
|
|
136
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
137
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
138
|
+
goal = excluded.goal,
|
|
139
|
+
phase = excluded.phase,
|
|
140
|
+
status = excluded.status,
|
|
141
|
+
approved_scope = excluded.approved_scope,
|
|
142
|
+
awaiting_implementation_approval = excluded.awaiting_implementation_approval,
|
|
143
|
+
turn_count = excluded.turn_count,
|
|
144
|
+
updated_at = excluded.updated_at""",
|
|
145
|
+
(
|
|
146
|
+
session_id,
|
|
147
|
+
task_run.goal,
|
|
148
|
+
task_run.phase.value,
|
|
149
|
+
task_run.status.value,
|
|
150
|
+
task_run.approved_scope,
|
|
151
|
+
1 if task_run.awaiting_implementation_approval else 0,
|
|
152
|
+
task_run.turn_count,
|
|
153
|
+
_now(),
|
|
154
|
+
),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
async def load_task_run(session_id: str) -> TaskRun:
|
|
159
|
+
row = await _fetch_one(
|
|
160
|
+
"SELECT * FROM session_task_runs WHERE session_id = ?",
|
|
161
|
+
(session_id,),
|
|
162
|
+
)
|
|
163
|
+
if not row:
|
|
164
|
+
return TaskRun()
|
|
165
|
+
return TaskRun(
|
|
166
|
+
goal=row["goal"],
|
|
167
|
+
phase=TaskPhase(row["phase"]),
|
|
168
|
+
status=TaskRunStatus(row["status"]),
|
|
169
|
+
approved_scope=row["approved_scope"],
|
|
170
|
+
awaiting_implementation_approval=bool(row["awaiting_implementation_approval"]),
|
|
171
|
+
turn_count=row["turn_count"],
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
async def save_message_runtime_snapshot(snapshot: MessageRuntimeSnapshot) -> None:
|
|
176
|
+
await _execute_commit(
|
|
177
|
+
"""INSERT INTO message_runtime_snapshots (
|
|
178
|
+
message_id, session_id, interaction_mode, task_intent,
|
|
179
|
+
implementation_allowed, intent_resolution_reason, goal, goal_phase,
|
|
180
|
+
goal_status, goal_turn_count, awaiting_implementation_approval,
|
|
181
|
+
approved_scope, created_at
|
|
182
|
+
)
|
|
183
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
184
|
+
ON CONFLICT(message_id) DO UPDATE SET
|
|
185
|
+
interaction_mode = excluded.interaction_mode,
|
|
186
|
+
task_intent = excluded.task_intent,
|
|
187
|
+
implementation_allowed = excluded.implementation_allowed,
|
|
188
|
+
intent_resolution_reason = excluded.intent_resolution_reason,
|
|
189
|
+
goal = excluded.goal,
|
|
190
|
+
goal_phase = excluded.goal_phase,
|
|
191
|
+
goal_status = excluded.goal_status,
|
|
192
|
+
goal_turn_count = excluded.goal_turn_count,
|
|
193
|
+
awaiting_implementation_approval = excluded.awaiting_implementation_approval,
|
|
194
|
+
approved_scope = excluded.approved_scope""",
|
|
195
|
+
(
|
|
196
|
+
snapshot.message_id,
|
|
197
|
+
snapshot.session_id,
|
|
198
|
+
snapshot.interaction_mode.value,
|
|
199
|
+
snapshot.task_intent.value,
|
|
200
|
+
1 if snapshot.implementation_allowed else 0,
|
|
201
|
+
snapshot.intent_resolution_reason,
|
|
202
|
+
snapshot.goal,
|
|
203
|
+
snapshot.goal_phase,
|
|
204
|
+
snapshot.goal_status,
|
|
205
|
+
snapshot.goal_turn_count,
|
|
206
|
+
1 if snapshot.awaiting_implementation_approval else 0,
|
|
207
|
+
snapshot.approved_scope,
|
|
208
|
+
snapshot.created_at,
|
|
209
|
+
),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
async def load_message_runtime_snapshot(message_id: int) -> MessageRuntimeSnapshot | None:
|
|
214
|
+
row = await _fetch_one(
|
|
215
|
+
"SELECT * FROM message_runtime_snapshots WHERE message_id = ?",
|
|
216
|
+
(message_id,),
|
|
217
|
+
)
|
|
218
|
+
if not row:
|
|
219
|
+
return None
|
|
220
|
+
return MessageRuntimeSnapshot(
|
|
221
|
+
message_id=row["message_id"],
|
|
222
|
+
session_id=row["session_id"],
|
|
223
|
+
interaction_mode=InteractionMode(row["interaction_mode"]),
|
|
224
|
+
task_intent=TaskIntent(row["task_intent"]),
|
|
225
|
+
implementation_allowed=bool(row["implementation_allowed"]),
|
|
226
|
+
intent_resolution_reason=row["intent_resolution_reason"],
|
|
227
|
+
goal=row["goal"],
|
|
228
|
+
goal_phase=row["goal_phase"],
|
|
229
|
+
goal_status=row["goal_status"],
|
|
230
|
+
goal_turn_count=row["goal_turn_count"],
|
|
231
|
+
awaiting_implementation_approval=bool(row["awaiting_implementation_approval"]),
|
|
232
|
+
approved_scope=row["approved_scope"],
|
|
233
|
+
created_at=row["created_at"],
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
async def clear_runtime_state(session_id: str) -> None:
|
|
238
|
+
await _execute_commit("DELETE FROM session_runtime_state WHERE session_id = ?", (session_id,))
|
|
239
|
+
await _execute_commit("DELETE FROM session_task_runs WHERE session_id = ?", (session_id,))
|
|
240
|
+
await _execute_commit("DELETE FROM message_runtime_snapshots WHERE session_id = ?", (session_id,))
|