import json
import os
import sqlite3
HERE = os.path.dirname(os.path.abspath(__file__))
FIXTURE_DIR = os.path.join(HERE, "opencode_fixture")
DB_PATH = os.path.join(FIXTURE_DIR, "opencode.db")
STORAGE_DIR = os.path.join(FIXTURE_DIR, "storage")
PROJECT_ID = "prj_fixture000000000000001"
SESSION_A = "ses_fixtureAAAAAAAAAAAAAAA1" SESSION_B = "ses_fixtureBBBBBBBBBBBBBBB1" MSG_USER_A1 = "msg_fixtureUser0000000001"
MSG_ASST_A1 = "msg_fixtureAsst0000000001"
MSG_USER_B1 = "msg_fixtureUser0000000002"
MSG_ASST_B1 = "msg_fixtureAsst0000000002"
T0 = 1750000000000
DDL = [
"""
CREATE TABLE `project` (
`id` text PRIMARY KEY,
`worktree` text NOT NULL,
`vcs` text,
`name` text,
`icon_url` text,
`icon_url_override` text,
`icon_color` text,
`time_created` integer NOT NULL,
`time_updated` integer NOT NULL,
`time_initialized` integer,
`sandboxes` text NOT NULL,
`commands` text
);
""",
"""
CREATE TABLE `message` (
`id` text PRIMARY KEY,
`session_id` text NOT NULL,
`time_created` integer NOT NULL,
`time_updated` integer NOT NULL,
`data` text NOT NULL,
CONSTRAINT `fk_message_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
);
""",
"""
CREATE TABLE `part` (
`id` text PRIMARY KEY,
`message_id` text NOT NULL,
`session_id` text NOT NULL,
`time_created` integer NOT NULL,
`time_updated` integer NOT NULL,
`data` text NOT NULL,
CONSTRAINT `fk_part_message_id_message_id_fk` FOREIGN KEY (`message_id`) REFERENCES `message`(`id`) ON DELETE CASCADE
);
""",
"""
CREATE TABLE `session` (
`id` text PRIMARY KEY,
`project_id` text NOT NULL,
`workspace_id` text,
`parent_id` text,
`slug` text NOT NULL,
`directory` text NOT NULL,
`path` text,
`title` text NOT NULL,
`version` text NOT NULL,
`share_url` text,
`summary_additions` integer,
`summary_deletions` integer,
`summary_files` integer,
`summary_diffs` text,
`metadata` text,
`cost` real DEFAULT 0 NOT NULL,
`tokens_input` integer DEFAULT 0 NOT NULL,
`tokens_output` integer DEFAULT 0 NOT NULL,
`tokens_reasoning` integer DEFAULT 0 NOT NULL,
`tokens_cache_read` integer DEFAULT 0 NOT NULL,
`tokens_cache_write` integer DEFAULT 0 NOT NULL,
`revert` text,
`permission` text,
`agent` text,
`model` text,
`time_created` integer NOT NULL,
`time_updated` integer NOT NULL,
`time_compacting` integer,
`time_archived` integer,
CONSTRAINT `fk_session_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
);
""",
"""
CREATE TABLE `todo` (
`session_id` text NOT NULL,
`content` text NOT NULL,
`status` text NOT NULL,
`priority` text NOT NULL,
`position` integer NOT NULL,
`time_created` integer NOT NULL,
`time_updated` integer NOT NULL,
CONSTRAINT `todo_pk` PRIMARY KEY(`session_id`, `position`),
CONSTRAINT `fk_todo_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
);
""",
]
def user_message(msg_id, session_id, t_created, system=None):
data = {
"role": "user",
"time": {"created": t_created},
"agent": "build",
"model": {"providerID": "anthropic", "modelID": "claude-fixture"},
}
if system is not None:
data["system"] = system
return data
def assistant_message(msg_id, session_id, parent_id, t_created, t_completed):
return {
"role": "assistant",
"time": {"created": t_created, "completed": t_completed},
"parentID": parent_id,
"modelID": "claude-fixture",
"providerID": "anthropic",
"mode": "build",
"agent": "build",
"path": {"cwd": "/tmp/fixture-project", "root": "/tmp/fixture-project"},
"cost": 0.0123,
"tokens": {
"input": 100,
"output": 200,
"reasoning": 0,
"cache": {"read": 10, "write": 5},
},
"finish": "stop",
}
def text_part(part_id, text, ignored=None):
d = {"type": "text", "text": text}
if ignored is not None:
d["ignored"] = ignored
return d
def file_part(part_id, mime, url, filename=None):
d = {"type": "file", "mime": mime, "url": url}
if filename:
d["filename"] = filename
return d
def tool_part_completed(part_id, call_id, tool, t_start, t_end):
return {
"type": "tool",
"callID": call_id,
"tool": tool,
"state": {
"status": "completed",
"input": {"command": "cat file.txt"},
"output": "file contents: hello world\n",
"title": tool,
"metadata": {},
"time": {"start": t_start, "end": t_end},
},
}
def tool_part_error(part_id, call_id, tool, t_start, t_end):
return {
"type": "tool",
"callID": call_id,
"tool": tool,
"state": {
"status": "error",
"input": {"path": "file.txt"},
"error": "permission denied",
"time": {"start": t_start, "end": t_end},
},
}
TINY_PNG_DATA_URI = (
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4"
"2mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
)
def main():
os.makedirs(FIXTURE_DIR, exist_ok=True)
os.makedirs(os.path.join(STORAGE_DIR, "session_diff"), exist_ok=True)
if os.path.exists(DB_PATH):
os.remove(DB_PATH)
conn = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA foreign_keys = ON")
for stmt in DDL:
conn.execute(stmt)
conn.execute(
"INSERT INTO project (id, worktree, vcs, name, icon_url, icon_url_override, "
"icon_color, time_created, time_updated, time_initialized, sandboxes, commands) "
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
(
PROJECT_ID,
"/tmp/fixture-project",
"git",
"fixture-project",
None,
None,
None,
T0,
T0,
None,
"[]",
None,
),
)
revert_with_files = {
"messageID": MSG_ASST_A1,
"snapshot": "snap_fixture0001",
"diff": "@@ -1 +1 @@\n-old\n+new\n",
"files": [
{"path": "src/a.txt", "additions": 3, "deletions": 1, "patch": "@@ -1,1 +1,3 @@\n"}
],
}
conn.execute(
"INSERT INTO session (id, project_id, workspace_id, parent_id, slug, directory, "
"path, title, version, share_url, summary_additions, summary_deletions, "
"summary_files, summary_diffs, metadata, cost, tokens_input, tokens_output, "
"tokens_reasoning, tokens_cache_read, tokens_cache_write, revert, permission, "
"agent, model, time_created, time_updated, time_compacting, time_archived) "
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(
SESSION_A,
PROJECT_ID,
None, None, "fixture-session-a",
"/tmp/fixture-project",
None, "Fixture Session A (hard cases)",
"1.0.0",
None, None,
None,
None, None, None, 0.0421, 120,
340,
0,
50,
10,
json.dumps(revert_with_files),
None, None, None, T0 + 60_000,
T0 + 120_000,
None, None, ),
)
u1 = user_message(MSG_USER_A1, SESSION_A, T0 + 60_000, system="You are a helpful coding agent.")
conn.execute(
"INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?,?,?,?,?)",
(MSG_USER_A1, SESSION_A, T0 + 60_000, T0 + 60_500, json.dumps(u1)),
)
user_parts = [
("prt_userA_text1", text_part("prt_userA_text1", "Please check this file and fix the bug.")),
(
"prt_userA_ignored",
text_part("prt_userA_ignored", "internal scratch note — not shown to the model", ignored=True),
),
(
"prt_userA_pdf",
file_part("prt_userA_pdf", "application/pdf", "https://example.com/report.pdf", "report.pdf"),
),
(
"prt_userA_img",
file_part("prt_userA_img", "image/png", TINY_PNG_DATA_URI, "screenshot.png"),
),
]
for i, (pid, pdata) in enumerate(user_parts):
conn.execute(
"INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?,?,?,?,?,?)",
(pid, MSG_USER_A1, SESSION_A, T0 + 60_000 + i, T0 + 60_000 + i, json.dumps(pdata)),
)
a1 = assistant_message(MSG_ASST_A1, SESSION_A, MSG_USER_A1, T0 + 61_000, T0 + 90_000)
conn.execute(
"INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?,?,?,?,?)",
(MSG_ASST_A1, SESSION_A, T0 + 61_000, T0 + 90_000, json.dumps(a1)),
)
asst_parts = [
("prt_asstA_text1", text_part("prt_asstA_text1", "I'll check the file now.")),
(
"prt_asstA_tool_ok",
tool_part_completed("prt_asstA_tool_ok", "call_1", "bash", T0 + 62_000, T0 + 63_000),
),
(
"prt_asstA_tool_err",
tool_part_error("prt_asstA_tool_err", "call_2", "edit", T0 + 64_000, T0 + 65_000),
),
]
for i, (pid, pdata) in enumerate(asst_parts):
conn.execute(
"INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?,?,?,?,?,?)",
(pid, MSG_ASST_A1, SESSION_A, T0 + 62_000 + i, T0 + 62_000 + i, json.dumps(pdata)),
)
conn.execute(
"INSERT INTO todo (session_id, content, status, priority, position, time_created, time_updated) "
"VALUES (?,?,?,?,?,?,?)",
(SESSION_A, "Fix the bug in file.txt", "in_progress", "high", 0, T0 + 61_500, T0 + 61_500),
)
conn.execute(
"INSERT INTO session (id, project_id, workspace_id, parent_id, slug, directory, "
"path, title, version, share_url, summary_additions, summary_deletions, "
"summary_files, summary_diffs, metadata, cost, tokens_input, tokens_output, "
"tokens_reasoning, tokens_cache_read, tokens_cache_write, revert, permission, "
"agent, model, time_created, time_updated, time_compacting, time_archived) "
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(
SESSION_B,
PROJECT_ID,
None,
None,
"fixture-session-b",
"/tmp/fixture-project",
None,
"Fixture Session B (plain, older)",
"1.0.0",
None,
None,
None,
None,
None,
None,
0.0,
0,
0,
0,
0,
0,
None,
None,
"build",
json.dumps({"id": "claude-fixture", "providerID": "anthropic"}),
T0, T0 + 1_000,
None,
None,
),
)
ub1 = user_message(MSG_USER_B1, SESSION_B, T0)
conn.execute(
"INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?,?,?,?,?)",
(MSG_USER_B1, SESSION_B, T0, T0 + 100, json.dumps(ub1)),
)
conn.execute(
"INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?,?,?,?,?,?)",
("prt_userB_text1", MSG_USER_B1, SESSION_B, T0, T0, json.dumps(text_part("prt_userB_text1", "hi"))),
)
ab1 = assistant_message(MSG_ASST_B1, SESSION_B, MSG_USER_B1, T0 + 200, T0 + 500)
conn.execute(
"INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?,?,?,?,?)",
(MSG_ASST_B1, SESSION_B, T0 + 200, T0 + 500, json.dumps(ab1)),
)
conn.execute(
"INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?,?,?,?,?,?)",
(
"prt_asstB_text1",
MSG_ASST_B1,
SESSION_B,
T0 + 200,
T0 + 200,
json.dumps(text_part("prt_asstB_text1", "hello back")),
),
)
conn.commit()
conn.close()
diffs = [
{"file": "src/a.txt", "additions": 3, "deletions": 1, "patch": "@@ -1,1 +1,3 @@\n"}
]
with open(os.path.join(STORAGE_DIR, "session_diff", f"{SESSION_A}.json"), "w") as f:
json.dump(diffs, f, indent=2)
print(f"wrote {DB_PATH}")
print(f"wrote {os.path.join(STORAGE_DIR, 'session_diff', SESSION_A + '.json')}")
print(f"SESSION_A (primary) = {SESSION_A}")
print(f"SESSION_B (secondary) = {SESSION_B}")
if __name__ == "__main__":
main()