Add runtime guidance input queue
This commit is contained in:
@@ -298,6 +298,7 @@ class LocalCodingAgent:
|
||||
team_runtime: TeamRuntime | None = None
|
||||
workflow_runtime: WorkflowRuntime | None = None
|
||||
worktree_runtime: WorktreeRuntime | None = None
|
||||
runtime_guidance_provider: Callable[[], tuple[dict[str, object], ...]] | None = None
|
||||
last_session: AgentSessionState | None = field(default=None, init=False, repr=False)
|
||||
last_run_result: AgentRunResult | None = field(default=None, init=False, repr=False)
|
||||
cumulative_usage: UsageStats = field(default_factory=UsageStats, init=False, repr=False)
|
||||
@@ -734,6 +735,11 @@ class LocalCodingAgent:
|
||||
return result
|
||||
|
||||
for turn_index in range(1, self.runtime_config.max_turns + 1):
|
||||
self._inject_runtime_guidance(
|
||||
session,
|
||||
stream_events,
|
||||
turn_index=turn_index,
|
||||
)
|
||||
self._microcompact_session_if_needed(
|
||||
session,
|
||||
stream_events,
|
||||
@@ -4274,6 +4280,69 @@ class LocalCodingAgent:
|
||||
transcript=session.transcript(),
|
||||
)
|
||||
|
||||
def _inject_runtime_guidance(
|
||||
self,
|
||||
session: AgentSessionState,
|
||||
stream_events: list[dict[str, object]],
|
||||
*,
|
||||
turn_index: int,
|
||||
) -> None:
|
||||
provider = self.runtime_guidance_provider
|
||||
if provider is None:
|
||||
return
|
||||
try:
|
||||
items = tuple(
|
||||
item
|
||||
for item in provider()
|
||||
if isinstance(item, dict)
|
||||
and str(item.get('content') or '').strip()
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
stream_events.append(
|
||||
{
|
||||
'type': 'runtime_guidance_error',
|
||||
'turn_index': turn_index,
|
||||
'error': str(exc)[:500],
|
||||
}
|
||||
)
|
||||
return
|
||||
if not items:
|
||||
return
|
||||
lines = [
|
||||
'<runtime-guidance>',
|
||||
(
|
||||
'用户在本轮任务运行过程中追加了以下引导。请在不丢弃当前进展的前提下'
|
||||
'吸收这些要求;如果它们与当前目标冲突,先说明冲突并选择最合理的继续方式。'
|
||||
),
|
||||
]
|
||||
item_ids: list[int] = []
|
||||
for index, item in enumerate(items, start=1):
|
||||
item_id = item.get('id')
|
||||
if isinstance(item_id, int) and not isinstance(item_id, bool):
|
||||
item_ids.append(item_id)
|
||||
content = str(item.get('content') or '').strip()
|
||||
lines.append(f'{index}. {content}')
|
||||
lines.append('</runtime-guidance>')
|
||||
session.append_user(
|
||||
'\n'.join(lines),
|
||||
metadata={
|
||||
'kind': 'runtime_guidance',
|
||||
'turn_index': turn_index,
|
||||
'queue_item_ids': item_ids,
|
||||
},
|
||||
message_id=f'runtime_guidance_{turn_index}_{uuid4().hex[:8]}',
|
||||
display=False,
|
||||
)
|
||||
stream_events.append(
|
||||
{
|
||||
'type': 'runtime_guidance_injected',
|
||||
'turn_index': turn_index,
|
||||
'input_ids': item_ids,
|
||||
'count': len(items),
|
||||
'message': f'已将 {len(items)} 条用户引导注入当前任务',
|
||||
}
|
||||
)
|
||||
|
||||
def render_system_prompt(self) -> str:
|
||||
prompt_context = self.build_prompt_context()
|
||||
parts = self.build_system_prompt_parts(prompt_context)
|
||||
|
||||
@@ -230,6 +230,69 @@ class RunStateStore:
|
||||
return None
|
||||
return self._snapshot_from_row(conn, row)
|
||||
|
||||
def events_since(
|
||||
self,
|
||||
account_key: str,
|
||||
session_id: str,
|
||||
*,
|
||||
after_event_seq: int = 0,
|
||||
limit: int = 500,
|
||||
) -> dict[str, Any]:
|
||||
with self._connect() as conn:
|
||||
rows = conn.execute(
|
||||
"""
|
||||
select e.id, e.recorded_at, e.event_json, s.run_id, s.status
|
||||
from run_events e
|
||||
join run_states s on s.run_id = e.run_id
|
||||
where s.account_key = ?
|
||||
and s.session_id = ?
|
||||
and e.id > ?
|
||||
order by e.id asc
|
||||
limit ?
|
||||
""",
|
||||
(
|
||||
account_key,
|
||||
session_id,
|
||||
max(0, int(after_event_seq)),
|
||||
max(1, int(limit)),
|
||||
),
|
||||
).fetchall()
|
||||
latest_row = conn.execute(
|
||||
"""
|
||||
select max(e.id) as latest_event_seq
|
||||
from run_events e
|
||||
join run_states s on s.run_id = e.run_id
|
||||
where s.account_key = ?
|
||||
and s.session_id = ?
|
||||
""",
|
||||
(account_key, session_id),
|
||||
).fetchone()
|
||||
events: list[dict[str, Any]] = []
|
||||
for row in rows:
|
||||
try:
|
||||
event = json.loads(row['event_json'])
|
||||
except (TypeError, json.JSONDecodeError):
|
||||
continue
|
||||
if not isinstance(event, dict):
|
||||
continue
|
||||
events.append(
|
||||
{
|
||||
'event_seq': int(row['id']),
|
||||
'run_id': row['run_id'],
|
||||
'run_status': row['status'],
|
||||
'recorded_at': float(row['recorded_at'] or 0.0),
|
||||
'event': event,
|
||||
}
|
||||
)
|
||||
latest_event_seq = 0
|
||||
if latest_row is not None and latest_row['latest_event_seq'] is not None:
|
||||
latest_event_seq = int(latest_row['latest_event_seq'])
|
||||
return {
|
||||
'session_id': session_id,
|
||||
'latest_event_seq': latest_event_seq,
|
||||
'events': events,
|
||||
}
|
||||
|
||||
def mark_interrupted_if_active(
|
||||
self,
|
||||
run_id: str,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import hashlib
|
||||
import sqlite3
|
||||
import time
|
||||
from dataclasses import asdict, dataclass
|
||||
@@ -84,6 +85,12 @@ def save_agent_session(session: StoredAgentSession, directory: Path | None = Non
|
||||
path = session_dir / 'session.json'
|
||||
payload = asdict(session)
|
||||
_write_agent_session_db_payload(target_dir, session.session_id, payload, path)
|
||||
_sync_agent_display_messages(
|
||||
target_dir,
|
||||
session.session_id,
|
||||
tuple(message for message in session.display_messages if isinstance(message, dict))
|
||||
or tuple(message for message in session.messages if isinstance(message, dict)),
|
||||
)
|
||||
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding='utf-8')
|
||||
return path
|
||||
|
||||
@@ -131,6 +138,266 @@ def list_agent_sessions(
|
||||
return sorted(sessions.values(), key=lambda item: item[1], reverse=True)
|
||||
|
||||
|
||||
def agent_session_delta(
|
||||
session_id: str,
|
||||
directory: Path | None = None,
|
||||
*,
|
||||
after_message_seq: int = 0,
|
||||
limit: int = 500,
|
||||
) -> dict[str, Any]:
|
||||
target_dir = directory or DEFAULT_AGENT_SESSION_DIR
|
||||
try:
|
||||
stored = load_agent_session(session_id, directory=target_dir)
|
||||
except (FileNotFoundError, json.JSONDecodeError, KeyError, TypeError, ValueError):
|
||||
stored = None
|
||||
if stored is not None:
|
||||
_sync_agent_display_messages(
|
||||
target_dir,
|
||||
session_id,
|
||||
stored.display_messages or stored.messages,
|
||||
)
|
||||
with _connect_agent_session_db(target_dir) as conn:
|
||||
latest_row = conn.execute(
|
||||
"""
|
||||
select max(seq) as latest_seq
|
||||
from agent_display_messages
|
||||
where session_id = ?
|
||||
""",
|
||||
(session_id,),
|
||||
).fetchone()
|
||||
rows = conn.execute(
|
||||
"""
|
||||
select seq, message_json, updated_at
|
||||
from agent_display_messages
|
||||
where session_id = ? and seq > ?
|
||||
order by seq asc
|
||||
limit ?
|
||||
""",
|
||||
(session_id, max(0, int(after_message_seq)), max(1, int(limit))),
|
||||
).fetchall()
|
||||
messages: list[dict[str, Any]] = []
|
||||
for row in rows:
|
||||
try:
|
||||
message = json.loads(row['message_json'])
|
||||
except (TypeError, json.JSONDecodeError):
|
||||
continue
|
||||
if isinstance(message, dict):
|
||||
messages.append(
|
||||
{
|
||||
'seq': int(row['seq']),
|
||||
'updated_at': float(row['updated_at'] or 0.0),
|
||||
'message': message,
|
||||
}
|
||||
)
|
||||
latest_seq = 0
|
||||
if latest_row is not None and latest_row['latest_seq'] is not None:
|
||||
latest_seq = int(latest_row['latest_seq'])
|
||||
return {
|
||||
'session_id': session_id,
|
||||
'latest_message_seq': latest_seq,
|
||||
'messages': messages,
|
||||
'session': (
|
||||
{
|
||||
'session_id': stored.session_id,
|
||||
'turns': stored.turns,
|
||||
'tool_calls': stored.tool_calls,
|
||||
'usage': stored.usage,
|
||||
'total_cost_usd': stored.total_cost_usd,
|
||||
'model': stored.model_config.get('model'),
|
||||
'is_training': stored.is_training,
|
||||
'session_metadata': stored.session_metadata or {},
|
||||
}
|
||||
if stored is not None
|
||||
else {'session_id': session_id}
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def enqueue_session_input(
|
||||
session_id: str,
|
||||
*,
|
||||
content: str,
|
||||
directory: Path | None = None,
|
||||
run_id: str | None = None,
|
||||
kind: str = 'next_turn',
|
||||
status: str = 'pending',
|
||||
metadata: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
target_dir = directory or DEFAULT_AGENT_SESSION_DIR
|
||||
now = time.time()
|
||||
normalized_kind = kind if kind in {'next_turn', 'guidance'} else 'next_turn'
|
||||
with _connect_agent_session_db(target_dir) as conn:
|
||||
cursor = conn.execute(
|
||||
"""
|
||||
insert into agent_input_queue (
|
||||
session_id, run_id, kind, status, content,
|
||||
created_at, updated_at, metadata_json
|
||||
)
|
||||
values (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
session_id,
|
||||
run_id or '',
|
||||
normalized_kind,
|
||||
status,
|
||||
content,
|
||||
now,
|
||||
now,
|
||||
json.dumps(metadata or {}, ensure_ascii=False, sort_keys=True),
|
||||
),
|
||||
)
|
||||
item_id = int(cursor.lastrowid)
|
||||
return {
|
||||
'id': item_id,
|
||||
'session_id': session_id,
|
||||
'run_id': run_id or '',
|
||||
'kind': normalized_kind,
|
||||
'status': status,
|
||||
'content': content,
|
||||
'created_at': now,
|
||||
'updated_at': now,
|
||||
'metadata': metadata or {},
|
||||
}
|
||||
|
||||
|
||||
def list_session_input_queue(
|
||||
session_id: str,
|
||||
directory: Path | None = None,
|
||||
*,
|
||||
statuses: tuple[str, ...] = ('pending',),
|
||||
limit: int = 100,
|
||||
) -> list[dict[str, Any]]:
|
||||
target_dir = directory or DEFAULT_AGENT_SESSION_DIR
|
||||
placeholders = ','.join('?' for _ in statuses)
|
||||
with _connect_agent_session_db(target_dir) as conn:
|
||||
rows = conn.execute(
|
||||
f"""
|
||||
select *
|
||||
from agent_input_queue
|
||||
where session_id = ?
|
||||
and status in ({placeholders})
|
||||
order by id asc
|
||||
limit ?
|
||||
""",
|
||||
(session_id, *statuses, max(1, int(limit))),
|
||||
).fetchall()
|
||||
return [_queue_row_to_dict(row) for row in rows]
|
||||
|
||||
|
||||
def update_session_input_queue_item(
|
||||
session_id: str,
|
||||
item_id: int,
|
||||
directory: Path | None = None,
|
||||
*,
|
||||
content: str | None = None,
|
||||
kind: str | None = None,
|
||||
status: str | None = None,
|
||||
run_id: str | None = None,
|
||||
) -> dict[str, Any] | None:
|
||||
target_dir = directory or DEFAULT_AGENT_SESSION_DIR
|
||||
assignments: list[str] = ['updated_at = ?']
|
||||
values: list[Any] = [time.time()]
|
||||
if content is not None:
|
||||
assignments.append('content = ?')
|
||||
values.append(content)
|
||||
if kind is not None:
|
||||
assignments.append('kind = ?')
|
||||
values.append(kind if kind in {'next_turn', 'guidance'} else 'next_turn')
|
||||
if status is not None:
|
||||
assignments.append('status = ?')
|
||||
values.append(status)
|
||||
if status in {'consumed', 'cancelled'}:
|
||||
assignments.append('consumed_at = coalesce(consumed_at, ?)')
|
||||
values.append(time.time())
|
||||
if run_id is not None:
|
||||
assignments.append('run_id = ?')
|
||||
values.append(run_id)
|
||||
values.extend([session_id, int(item_id)])
|
||||
with _connect_agent_session_db(target_dir) as conn:
|
||||
conn.execute(
|
||||
f"""
|
||||
update agent_input_queue
|
||||
set {', '.join(assignments)}
|
||||
where session_id = ? and id = ?
|
||||
""",
|
||||
values,
|
||||
)
|
||||
row = conn.execute(
|
||||
"""
|
||||
select *
|
||||
from agent_input_queue
|
||||
where session_id = ? and id = ?
|
||||
""",
|
||||
(session_id, int(item_id)),
|
||||
).fetchone()
|
||||
return _queue_row_to_dict(row) if row is not None else None
|
||||
|
||||
|
||||
def cancel_session_input_queue(
|
||||
session_id: str,
|
||||
directory: Path | None = None,
|
||||
*,
|
||||
run_id: str | None = None,
|
||||
) -> int:
|
||||
target_dir = directory or DEFAULT_AGENT_SESSION_DIR
|
||||
now = time.time()
|
||||
where = 'session_id = ? and status = ?'
|
||||
values: list[Any] = [session_id, 'pending']
|
||||
if run_id:
|
||||
where += ' and (run_id = ? or run_id = ?)'
|
||||
values.extend([run_id, ''])
|
||||
with _connect_agent_session_db(target_dir) as conn:
|
||||
cursor = conn.execute(
|
||||
f"""
|
||||
update agent_input_queue
|
||||
set status = 'cancelled',
|
||||
updated_at = ?,
|
||||
consumed_at = coalesce(consumed_at, ?)
|
||||
where {where}
|
||||
""",
|
||||
(now, now, *values),
|
||||
)
|
||||
return int(cursor.rowcount or 0)
|
||||
|
||||
|
||||
def consume_session_guidance(
|
||||
session_id: str,
|
||||
run_id: str,
|
||||
directory: Path | None = None,
|
||||
*,
|
||||
limit: int = 10,
|
||||
) -> list[dict[str, Any]]:
|
||||
target_dir = directory or DEFAULT_AGENT_SESSION_DIR
|
||||
now = time.time()
|
||||
with _connect_agent_session_db(target_dir) as conn:
|
||||
rows = conn.execute(
|
||||
"""
|
||||
select *
|
||||
from agent_input_queue
|
||||
where session_id = ?
|
||||
and status = 'pending'
|
||||
and kind = 'guidance'
|
||||
and (run_id = ? or run_id = '')
|
||||
order by id asc
|
||||
limit ?
|
||||
""",
|
||||
(session_id, run_id, max(1, int(limit))),
|
||||
).fetchall()
|
||||
ids = [int(row['id']) for row in rows]
|
||||
if ids:
|
||||
conn.execute(
|
||||
f"""
|
||||
update agent_input_queue
|
||||
set status = 'consumed',
|
||||
updated_at = ?,
|
||||
consumed_at = ?
|
||||
where id in ({','.join('?' for _ in ids)})
|
||||
""",
|
||||
(now, now, *ids),
|
||||
)
|
||||
return [_queue_row_to_dict(row) for row in rows]
|
||||
|
||||
|
||||
def delete_agent_session(session_id: str, directory: Path | None = None) -> bool:
|
||||
target_dir = directory or DEFAULT_AGENT_SESSION_DIR
|
||||
deleted = False
|
||||
@@ -140,6 +407,14 @@ def delete_agent_session(session_id: str, directory: Path | None = None) -> bool
|
||||
(session_id,),
|
||||
)
|
||||
deleted = cursor.rowcount > 0
|
||||
conn.execute(
|
||||
'delete from agent_display_messages where session_id = ?',
|
||||
(session_id,),
|
||||
)
|
||||
conn.execute(
|
||||
'delete from agent_input_queue where session_id = ?',
|
||||
(session_id,),
|
||||
)
|
||||
nested = target_dir / session_id
|
||||
if nested.exists():
|
||||
import shutil
|
||||
@@ -288,6 +563,195 @@ def _write_agent_session_revision(
|
||||
)
|
||||
|
||||
|
||||
def _sync_agent_display_messages(
|
||||
directory: Path,
|
||||
session_id: str,
|
||||
display_messages: tuple[JSONDict, ...],
|
||||
) -> None:
|
||||
if not display_messages:
|
||||
return
|
||||
now = time.time()
|
||||
with _connect_agent_session_db(directory) as conn:
|
||||
existing_rows = conn.execute(
|
||||
"""
|
||||
select seq, message_key, message_json
|
||||
from agent_display_messages
|
||||
where session_id = ?
|
||||
""",
|
||||
(session_id,),
|
||||
).fetchall()
|
||||
existing: dict[int, sqlite3.Row] = {
|
||||
int(row['seq']): row for row in existing_rows
|
||||
}
|
||||
existing_by_key: dict[str, sqlite3.Row] = {
|
||||
str(row['message_key'] or ''): row
|
||||
for row in existing_rows
|
||||
if str(row['message_key'] or '')
|
||||
}
|
||||
next_seq = max(existing.keys(), default=0) + 1
|
||||
for index, message in enumerate(display_messages, start=1):
|
||||
if not isinstance(message, dict):
|
||||
continue
|
||||
message_json = json.dumps(message, ensure_ascii=False, sort_keys=True)
|
||||
message_key = _display_message_key(message)
|
||||
row = existing.get(index)
|
||||
keyed_row = existing_by_key.get(message_key)
|
||||
if keyed_row is not None and keyed_row['message_json'] == message_json:
|
||||
continue
|
||||
if keyed_row is not None:
|
||||
try:
|
||||
keyed_old_message = json.loads(keyed_row['message_json'])
|
||||
except (TypeError, json.JSONDecodeError):
|
||||
keyed_old_message = {}
|
||||
if not isinstance(keyed_old_message, dict):
|
||||
keyed_old_message = {}
|
||||
if _is_replaceable_display_message(keyed_old_message):
|
||||
conn.execute(
|
||||
"""
|
||||
update agent_display_messages
|
||||
set role = ?,
|
||||
updated_at = ?,
|
||||
message_json = ?
|
||||
where session_id = ? and seq = ?
|
||||
""",
|
||||
(
|
||||
str(message.get('role') or ''),
|
||||
now,
|
||||
message_json,
|
||||
session_id,
|
||||
int(keyed_row['seq']),
|
||||
),
|
||||
)
|
||||
continue
|
||||
if row is None:
|
||||
conn.execute(
|
||||
"""
|
||||
insert into agent_display_messages (
|
||||
session_id, seq, message_key, role, updated_at, message_json
|
||||
)
|
||||
values (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
session_id,
|
||||
index,
|
||||
message_key,
|
||||
str(message.get('role') or ''),
|
||||
now,
|
||||
message_json,
|
||||
),
|
||||
)
|
||||
continue
|
||||
if row['message_json'] == message_json:
|
||||
continue
|
||||
try:
|
||||
old_message = json.loads(row['message_json'])
|
||||
except (TypeError, json.JSONDecodeError):
|
||||
old_message = {}
|
||||
if not isinstance(old_message, dict):
|
||||
old_message = {}
|
||||
old_key = str(row['message_key'] or '')
|
||||
should_update = (
|
||||
old_key == message_key
|
||||
or _is_replaceable_display_message(old_message)
|
||||
or _same_role_and_content(old_message, message)
|
||||
)
|
||||
if not should_update:
|
||||
append_seq = next_seq
|
||||
next_seq += 1
|
||||
conn.execute(
|
||||
"""
|
||||
insert into agent_display_messages (
|
||||
session_id, seq, message_key, role, updated_at, message_json
|
||||
)
|
||||
values (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
session_id,
|
||||
append_seq,
|
||||
message_key,
|
||||
str(message.get('role') or ''),
|
||||
now,
|
||||
message_json,
|
||||
),
|
||||
)
|
||||
existing_by_key[message_key] = {
|
||||
'seq': append_seq,
|
||||
'message_key': message_key,
|
||||
'message_json': message_json,
|
||||
} # type: ignore[assignment]
|
||||
continue
|
||||
conn.execute(
|
||||
"""
|
||||
update agent_display_messages
|
||||
set message_key = ?,
|
||||
role = ?,
|
||||
updated_at = ?,
|
||||
message_json = ?
|
||||
where session_id = ? and seq = ?
|
||||
""",
|
||||
(
|
||||
message_key,
|
||||
str(message.get('role') or ''),
|
||||
now,
|
||||
message_json,
|
||||
session_id,
|
||||
index,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _display_message_key(message: JSONDict) -> str:
|
||||
message_id = message.get('message_id')
|
||||
if isinstance(message_id, str) and message_id:
|
||||
return f'id:{message_id}'
|
||||
tool_call_id = message.get('tool_call_id')
|
||||
if isinstance(tool_call_id, str) and tool_call_id:
|
||||
return f'tool:{tool_call_id}'
|
||||
role = str(message.get('role') or '')
|
||||
content = '' if message.get('content') is None else str(message.get('content') or '')
|
||||
digest = hashlib.sha1(f'{role}\0{content}'.encode('utf-8')).hexdigest()
|
||||
return f'hash:{digest}'
|
||||
|
||||
|
||||
def _is_replaceable_display_message(message: JSONDict) -> bool:
|
||||
metadata = message.get('metadata')
|
||||
if not isinstance(metadata, dict):
|
||||
return False
|
||||
if metadata.get('placeholder') is True:
|
||||
return True
|
||||
return metadata.get('kind') in {'run_status'}
|
||||
|
||||
|
||||
def _same_role_and_content(left: JSONDict, right: JSONDict) -> bool:
|
||||
return (
|
||||
str(left.get('role') or '') == str(right.get('role') or '')
|
||||
and str(left.get('content') or '') == str(right.get('content') or '')
|
||||
)
|
||||
|
||||
|
||||
def _queue_row_to_dict(row: sqlite3.Row) -> dict[str, Any]:
|
||||
try:
|
||||
metadata = json.loads(row['metadata_json'] or '{}')
|
||||
except (TypeError, json.JSONDecodeError):
|
||||
metadata = {}
|
||||
return {
|
||||
'id': int(row['id']),
|
||||
'session_id': str(row['session_id'] or ''),
|
||||
'run_id': str(row['run_id'] or ''),
|
||||
'kind': str(row['kind'] or ''),
|
||||
'status': str(row['status'] or ''),
|
||||
'content': str(row['content'] or ''),
|
||||
'created_at': float(row['created_at'] or 0.0),
|
||||
'updated_at': float(row['updated_at'] or 0.0),
|
||||
'consumed_at': (
|
||||
float(row['consumed_at'])
|
||||
if row['consumed_at'] is not None
|
||||
else None
|
||||
),
|
||||
'metadata': metadata if isinstance(metadata, dict) else {},
|
||||
}
|
||||
|
||||
|
||||
def _read_agent_session_db_payload(
|
||||
directory: Path,
|
||||
session_id: str,
|
||||
@@ -365,6 +829,53 @@ def _connect_agent_session_db(directory: Path) -> sqlite3.Connection:
|
||||
on agent_session_revisions(session_id, revision_id)
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
create table if not exists agent_display_messages (
|
||||
session_id text not null,
|
||||
seq integer not null,
|
||||
message_key text not null,
|
||||
role text default '',
|
||||
updated_at real not null,
|
||||
message_json text not null,
|
||||
primary key(session_id, seq)
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
create index if not exists idx_agent_display_messages_session
|
||||
on agent_display_messages(session_id, seq)
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
create table if not exists agent_input_queue (
|
||||
id integer primary key autoincrement,
|
||||
session_id text not null,
|
||||
run_id text default '',
|
||||
kind text not null,
|
||||
status text not null,
|
||||
content text not null,
|
||||
created_at real not null,
|
||||
updated_at real not null,
|
||||
consumed_at real,
|
||||
metadata_json text default '{}'
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
create index if not exists idx_agent_input_queue_session
|
||||
on agent_input_queue(session_id, status, id)
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
create index if not exists idx_agent_input_queue_run
|
||||
on agent_input_queue(session_id, run_id, status, id)
|
||||
"""
|
||||
)
|
||||
return conn
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user