agent-session-bridge 0.2.1__tar.gz → 0.2.3__tar.gz
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.
- {agent_session_bridge-0.2.1/src/agent_session_bridge.egg-info → agent_session_bridge-0.2.3}/PKG-INFO +2 -2
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/README.md +1 -1
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/pyproject.toml +1 -1
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3/src/agent_session_bridge.egg-info}/PKG-INFO +2 -2
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/codex.py +51 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/codex_db.py +51 -2
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_codex_db.py +80 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/LICENSE +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/setup.cfg +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/agent_session_bridge.egg-info/SOURCES.txt +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/agent_session_bridge.egg-info/dependency_links.txt +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/agent_session_bridge.egg-info/entry_points.txt +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/agent_session_bridge.egg-info/requires.txt +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/agent_session_bridge.egg-info/top_level.txt +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/__init__.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/_ids.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/cli.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/convert.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/handshake.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/ir.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/place.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/__init__.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/_content.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/_jsonl.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/_pending.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/claude_code.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/codex.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/hermes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/skill_install.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/skills/session-handoff/SKILL.md +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/__init__.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/actions.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/app.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/discovery.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/options.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/register.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/screens.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/summary.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/__init__.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/_common.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/claude_code.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/hermes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/hermes_db.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_claude_reader.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_cli_tui.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_codex_reader.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_handshake_convert_cli.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_hermes_db.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_hermes_reader.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_ir.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_place.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round11_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round12_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round13_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round14_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round16_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round17_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round18_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round18b_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round1_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round21_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round22_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round25_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round27_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round28_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round2_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round3_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round4_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round5_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round6_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round7_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round8_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round9_fixes.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_skill_install.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_stub_open_calls.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_tui_actions.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_tui_discovery.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_tui_options.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_tui_pilot.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_tui_register.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_tui_summary.py +0 -0
- {agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_writers.py +0 -0
{agent_session_bridge-0.2.1/src/agent_session_bridge.egg-info → agent_session_bridge-0.2.3}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-session-bridge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Local-first cross-harness agent-session portability: export a live coding-agent session from one harness and resume it in another.
|
|
5
5
|
Author: Prakash Chaudhary
|
|
6
6
|
License: MIT
|
|
@@ -60,7 +60,7 @@ and fail closed (no write) when it doesn't match.
|
|
|
60
60
|
| Harness | Last verified against | Verification |
|
|
61
61
|
|---|---|---|
|
|
62
62
|
| Claude Code | 2.1.x transcripts | Round-trip + live `claude --resume` recall of a converted-only fact |
|
|
63
|
-
| Codex | Codex CLI 0.145.0 (`state_5.sqlite`) | Live `codex resume` recall of a converted-only sentinel |
|
|
63
|
+
| Codex | Codex CLI 0.145.0 (`state_5.sqlite`) | Live `codex resume` recall of a converted-only sentinel, and the registered session listed in the resume picker |
|
|
64
64
|
| Hermes | `state.db` schema as of 2026-07 | Live `hermes --resume` replay of a registered session |
|
|
65
65
|
| Python | 3.11 – 3.13 | CI test matrix |
|
|
66
66
|
|
|
@@ -29,7 +29,7 @@ and fail closed (no write) when it doesn't match.
|
|
|
29
29
|
| Harness | Last verified against | Verification |
|
|
30
30
|
|---|---|---|
|
|
31
31
|
| Claude Code | 2.1.x transcripts | Round-trip + live `claude --resume` recall of a converted-only fact |
|
|
32
|
-
| Codex | Codex CLI 0.145.0 (`state_5.sqlite`) | Live `codex resume` recall of a converted-only sentinel |
|
|
32
|
+
| Codex | Codex CLI 0.145.0 (`state_5.sqlite`) | Live `codex resume` recall of a converted-only sentinel, and the registered session listed in the resume picker |
|
|
33
33
|
| Hermes | `state.db` schema as of 2026-07 | Live `hermes --resume` replay of a registered session |
|
|
34
34
|
| Python | 3.11 – 3.13 | CI test matrix |
|
|
35
35
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agent-session-bridge"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Local-first cross-harness agent-session portability: export a live coding-agent session from one harness and resume it in another."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3/src/agent_session_bridge.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-session-bridge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Local-first cross-harness agent-session portability: export a live coding-agent session from one harness and resume it in another.
|
|
5
5
|
Author: Prakash Chaudhary
|
|
6
6
|
License: MIT
|
|
@@ -60,7 +60,7 @@ and fail closed (no write) when it doesn't match.
|
|
|
60
60
|
| Harness | Last verified against | Verification |
|
|
61
61
|
|---|---|---|
|
|
62
62
|
| Claude Code | 2.1.x transcripts | Round-trip + live `claude --resume` recall of a converted-only fact |
|
|
63
|
-
| Codex | Codex CLI 0.145.0 (`state_5.sqlite`) | Live `codex resume` recall of a converted-only sentinel |
|
|
63
|
+
| Codex | Codex CLI 0.145.0 (`state_5.sqlite`) | Live `codex resume` recall of a converted-only sentinel, and the registered session listed in the resume picker |
|
|
64
64
|
| Hermes | `state.db` schema as of 2026-07 | Live `hermes --resume` replay of a registered session |
|
|
65
65
|
| Python | 3.11 – 3.13 | CI test matrix |
|
|
66
66
|
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/codex.py
RENAMED
|
@@ -23,6 +23,16 @@ def _codex_role(role: Role) -> str:
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def _msg_payload(role: str, text: str) -> dict[str, Any]:
|
|
26
|
+
if role == "assistant":
|
|
27
|
+
# Codex's history reconstruction keeps assistant messages by channel
|
|
28
|
+
# phase; without this the resumed model sees only the user turns.
|
|
29
|
+
block_type = "output_text"
|
|
30
|
+
return {
|
|
31
|
+
"type": "message",
|
|
32
|
+
"role": role,
|
|
33
|
+
"content": [{"type": block_type, "text": text}],
|
|
34
|
+
"phase": "final_answer",
|
|
35
|
+
}
|
|
26
36
|
# Assistant emits output_text; user/system emit input_text.
|
|
27
37
|
block_type = "output_text" if role == "assistant" else "input_text"
|
|
28
38
|
return {"type": "message", "role": role, "content": [{"type": block_type, "text": text}]}
|
|
@@ -150,5 +160,46 @@ def write_codex(
|
|
|
150
160
|
# Preserve an otherwise-empty message so message count survives the round trip.
|
|
151
161
|
if not emitted and msg.role in (Role.USER, Role.ASSISTANT, Role.SYSTEM):
|
|
152
162
|
add(_msg_payload(role, ""), ts)
|
|
163
|
+
# Codex writes an event_msg twin for every user turn, and its resume
|
|
164
|
+
# picker only lists sessions whose rollout contains at least one
|
|
165
|
+
# user_message event — without this the imported session resumes by
|
|
166
|
+
# id but never appears in the picker. The reader ignores event_msg
|
|
167
|
+
# as a response_item duplicate, so round-trips are unaffected.
|
|
168
|
+
if msg.role is Role.USER:
|
|
169
|
+
user_text = msg.text()
|
|
170
|
+
if user_text.strip():
|
|
171
|
+
records.append(
|
|
172
|
+
{
|
|
173
|
+
"timestamp": ts,
|
|
174
|
+
"type": "event_msg",
|
|
175
|
+
"payload": {
|
|
176
|
+
"type": "user_message",
|
|
177
|
+
"message": user_text,
|
|
178
|
+
"images": [],
|
|
179
|
+
"local_images": [],
|
|
180
|
+
"audio": [],
|
|
181
|
+
"local_audio": [],
|
|
182
|
+
"text_elements": [],
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
elif msg.role is Role.ASSISTANT:
|
|
187
|
+
# Same twin discipline for assistant turns: the transcript view
|
|
188
|
+
# renders from event_msg records, so without agent_message events
|
|
189
|
+
# a resumed session displays as user-messages-only.
|
|
190
|
+
assistant_text = msg.text()
|
|
191
|
+
if assistant_text.strip():
|
|
192
|
+
records.append(
|
|
193
|
+
{
|
|
194
|
+
"timestamp": ts,
|
|
195
|
+
"type": "event_msg",
|
|
196
|
+
"payload": {
|
|
197
|
+
"type": "agent_message",
|
|
198
|
+
"message": assistant_text,
|
|
199
|
+
"phase": "final_answer",
|
|
200
|
+
"memory_citation": None,
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
)
|
|
153
204
|
|
|
154
205
|
return records, report
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/codex_db.py
RENAMED
|
@@ -16,7 +16,9 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
|
+
import shutil
|
|
19
20
|
import sqlite3
|
|
21
|
+
import subprocess
|
|
20
22
|
import tempfile
|
|
21
23
|
import time
|
|
22
24
|
import uuid
|
|
@@ -81,6 +83,43 @@ def validate_codex_store(codex_home: str | Path) -> Path:
|
|
|
81
83
|
return db_path
|
|
82
84
|
|
|
83
85
|
|
|
86
|
+
def infer_codex_cli_version(codex_home: str | Path) -> str:
|
|
87
|
+
"""The installed Codex CLI's version, for stamping imported sessions.
|
|
88
|
+
|
|
89
|
+
Asks the binary first (`codex --version` -> "codex-cli 0.145.0"): the
|
|
90
|
+
store's rows are not a trustworthy source because previously imported
|
|
91
|
+
sessions land there too, so inferring from rows can echo an earlier
|
|
92
|
+
import's value back. The newest store row is only the fallback when no
|
|
93
|
+
codex binary is on PATH; empty string for a fresh store, which Codex
|
|
94
|
+
tolerates (the column defaults to '' for pre-column rows).
|
|
95
|
+
"""
|
|
96
|
+
exe = shutil.which("codex")
|
|
97
|
+
if exe:
|
|
98
|
+
try:
|
|
99
|
+
out = subprocess.run(
|
|
100
|
+
[exe, "--version"], capture_output=True, text=True, timeout=10
|
|
101
|
+
).stdout.strip()
|
|
102
|
+
if out:
|
|
103
|
+
return out.split()[-1]
|
|
104
|
+
except (OSError, subprocess.SubprocessError):
|
|
105
|
+
pass
|
|
106
|
+
db_path = validate_codex_store(codex_home)
|
|
107
|
+
conn = sqlite3.connect(db_path.resolve().as_uri() + "?mode=ro", uri=True)
|
|
108
|
+
try:
|
|
109
|
+
row = conn.execute(
|
|
110
|
+
"""
|
|
111
|
+
SELECT cli_version
|
|
112
|
+
FROM threads
|
|
113
|
+
WHERE cli_version IS NOT NULL AND cli_version != ''
|
|
114
|
+
ORDER BY recency_at_ms DESC, updated_at_ms DESC, updated_at DESC
|
|
115
|
+
LIMIT 1
|
|
116
|
+
"""
|
|
117
|
+
).fetchone()
|
|
118
|
+
finally:
|
|
119
|
+
conn.close()
|
|
120
|
+
return row[0] if row else ""
|
|
121
|
+
|
|
122
|
+
|
|
84
123
|
def infer_codex_model(codex_home: str | Path, model_provider: str = "openai") -> str | None:
|
|
85
124
|
"""Return the most recently used model for a configured Codex provider."""
|
|
86
125
|
db_path = validate_codex_store(codex_home)
|
|
@@ -161,6 +200,8 @@ def _thread_values(
|
|
|
161
200
|
"tokens_used": 0,
|
|
162
201
|
"has_user_event": int(bool(first_user_message)),
|
|
163
202
|
"archived": 0,
|
|
203
|
+
# meta.version here is the TARGET Codex version (the registrar
|
|
204
|
+
# replaces the source harness's with the installed Codex's).
|
|
164
205
|
"cli_version": session.meta.version or "",
|
|
165
206
|
"first_user_message": first_user_message,
|
|
166
207
|
"memory_mode": "enabled",
|
|
@@ -221,8 +262,13 @@ def register_codex_session(
|
|
|
221
262
|
|
|
222
263
|
timestamp = started_at if started_at is not None else time.time()
|
|
223
264
|
iso_timestamp = datetime.fromtimestamp(timestamp, UTC).strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
|
224
|
-
|
|
225
|
-
|
|
265
|
+
# The resume picker parses rollout filenames strictly: local time,
|
|
266
|
+
# second precision, no timezone suffix — matching what Codex itself
|
|
267
|
+
# writes. Record timestamps inside the file stay UTC.
|
|
268
|
+
local = datetime.fromtimestamp(timestamp)
|
|
269
|
+
day_path = local.strftime("%Y/%m/%d")
|
|
270
|
+
file_stamp = local.strftime("%Y-%m-%dT%H-%M-%S")
|
|
271
|
+
rollout_path = home / "sessions" / day_path / f"rollout-{file_stamp}-{session_id}.jsonl"
|
|
226
272
|
if rollout_path.exists():
|
|
227
273
|
raise CodexRegistrationError(f"Codex rollout already exists: {rollout_path}")
|
|
228
274
|
|
|
@@ -232,6 +278,9 @@ def register_codex_session(
|
|
|
232
278
|
cwd=resolved_cwd,
|
|
233
279
|
model=model,
|
|
234
280
|
model_provider=model_provider,
|
|
281
|
+
# The source harness's version must not masquerade as Codex's;
|
|
282
|
+
# stamp the installed Codex's version observed from its own store.
|
|
283
|
+
version=infer_codex_cli_version(home),
|
|
235
284
|
)
|
|
236
285
|
target_session = replace(session, meta=target_meta)
|
|
237
286
|
transcript_sandbox, index_sandbox = workspace_write_policies(resolved_cwd)
|
|
@@ -315,3 +315,83 @@ def test_cli_register_codex_backs_up_and_adds_handshake(tmp_path):
|
|
|
315
315
|
]
|
|
316
316
|
) == 0
|
|
317
317
|
assert len(list(home.glob("state_5.sqlite.session-bridge-backup-*"))) == 2
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def test_registration_matches_native_picker_requirements(tmp_path, monkeypatch):
|
|
321
|
+
"""The resume picker only lists sessions whose rollout carries an
|
|
322
|
+
event_msg user_message and whose filename matches the native pattern."""
|
|
323
|
+
import re
|
|
324
|
+
|
|
325
|
+
monkeypatch.setattr('session_bridge.writers.codex_db.shutil.which', lambda _: None)
|
|
326
|
+
|
|
327
|
+
home = tmp_path / "codex"
|
|
328
|
+
_make_codex_home(home)
|
|
329
|
+
session = _sample_session()
|
|
330
|
+
rollout = register_codex_session(
|
|
331
|
+
session, home, "1e8400e2-9b1d-4716-a4f6-426655440000",
|
|
332
|
+
cwd=str(tmp_path), title="picker check", model="gpt-5", model_provider="openai",
|
|
333
|
+
)
|
|
334
|
+
# Native filename: local time, no milliseconds/Z suffix.
|
|
335
|
+
assert re.fullmatch(
|
|
336
|
+
r"rollout-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-[0-9a-f-]{36}\.jsonl",
|
|
337
|
+
rollout.name,
|
|
338
|
+
), rollout.name
|
|
339
|
+
records = [json.loads(line) for line in rollout.read_text().splitlines()]
|
|
340
|
+
user_events = [
|
|
341
|
+
r for r in records
|
|
342
|
+
if r.get("type") == "event_msg" and r["payload"].get("type") == "user_message"
|
|
343
|
+
]
|
|
344
|
+
assert user_events, "picker requires an event_msg user_message in the rollout"
|
|
345
|
+
assert user_events[0]["payload"]["message"].strip()
|
|
346
|
+
agent_events = [
|
|
347
|
+
r for r in records
|
|
348
|
+
if r.get("type") == "event_msg" and r["payload"].get("type") == "agent_message"
|
|
349
|
+
]
|
|
350
|
+
assert agent_events, "transcript rendering requires agent_message events"
|
|
351
|
+
assistant_items = [
|
|
352
|
+
r for r in records
|
|
353
|
+
if r.get("type") == "response_item" and r["payload"].get("type") == "message"
|
|
354
|
+
and r["payload"].get("role") == "assistant"
|
|
355
|
+
]
|
|
356
|
+
assert assistant_items and all(
|
|
357
|
+
i["payload"].get("phase") == "final_answer" for i in assistant_items
|
|
358
|
+
), "history reconstruction drops assistant messages without a phase"
|
|
359
|
+
conn = sqlite3.connect(home / "state_5.sqlite")
|
|
360
|
+
row = dict(zip([c[0] for c in conn.execute("SELECT model, first_user_message, preview, cli_version FROM threads").description],
|
|
361
|
+
conn.execute("SELECT model, first_user_message, preview, cli_version FROM threads").fetchone()))
|
|
362
|
+
conn.close()
|
|
363
|
+
assert row["model"] == "gpt-5"
|
|
364
|
+
assert row["first_user_message"].strip()
|
|
365
|
+
assert row["preview"].strip()
|
|
366
|
+
# The source harness's version must not masquerade as Codex's
|
|
367
|
+
# cli_version; a fresh isolated store has no native row to infer from.
|
|
368
|
+
assert row["cli_version"] == ""
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def test_cli_version_inferred_from_native_rows(tmp_path, monkeypatch):
|
|
372
|
+
monkeypatch.setattr('session_bridge.writers.codex_db.shutil.which', lambda _: None)
|
|
373
|
+
home = tmp_path / "codex"
|
|
374
|
+
_make_codex_home(home)
|
|
375
|
+
conn = sqlite3.connect(home / "state_5.sqlite")
|
|
376
|
+
conn.execute(
|
|
377
|
+
"INSERT INTO threads (id, rollout_path, created_at, updated_at, source, "
|
|
378
|
+
"model_provider, cwd, title, sandbox_policy, approval_mode, cli_version, "
|
|
379
|
+
"recency_at_ms, updated_at_ms) "
|
|
380
|
+
"VALUES (?, ?, 1, 1, 'cli', 'openai', '/tmp', 'native', '{}', 'on-request', "
|
|
381
|
+
"'0.145.0', 1, 1)",
|
|
382
|
+
(str(uuid.uuid4()), "/tmp/native.jsonl"),
|
|
383
|
+
)
|
|
384
|
+
conn.commit()
|
|
385
|
+
conn.close()
|
|
386
|
+
rollout = register_codex_session(
|
|
387
|
+
_sample_session(), home, "2e8400e2-9b1d-4716-a4f6-426655440000",
|
|
388
|
+
cwd=str(tmp_path), title="version check", model="gpt-5", model_provider="openai",
|
|
389
|
+
)
|
|
390
|
+
conn = sqlite3.connect(home / "state_5.sqlite")
|
|
391
|
+
got = conn.execute(
|
|
392
|
+
"SELECT cli_version FROM threads WHERE id='2e8400e2-9b1d-4716-a4f6-426655440000'"
|
|
393
|
+
).fetchone()[0]
|
|
394
|
+
conn.close()
|
|
395
|
+
assert got == "0.145.0"
|
|
396
|
+
meta = json.loads(rollout.read_text().splitlines()[0])
|
|
397
|
+
assert meta["payload"]["cli_version"] == "0.145.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/__init__.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/_content.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/_jsonl.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/_pending.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/claude_code.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/codex.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/readers/hermes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/skill_install.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/discovery.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/tui/register.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/__init__.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/_common.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/claude_code.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/hermes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/src/session_bridge/writers/hermes_db.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_handshake_convert_cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round11_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round12_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round13_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round14_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round16_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round17_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round18_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round18b_fixes.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round21_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round22_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round25_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round27_fixes.py
RENAMED
|
File without changes
|
{agent_session_bridge-0.2.1 → agent_session_bridge-0.2.3}/tests/test_review_round28_fixes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|