Repository navigation
Conversation
Mirrors chats into Hermes' SQLite store ($HERMES_HOME/state.db) so they show up in Hermes' own /resume, and reads Hermes chats into every other agent. Writes sessions + messages rows directly (the schema is stable and already maintained by AFTER-INSERT triggers), marked via origin_json so hop never re-adopts its own mirror. model is left NULL on purpose: --resume reuses the stored model, and a placeholder like 'hop-import' makes the mirror unresumable. Also fixes two pre-existing bugs found while wiring this up: - kimi: updated was the raw ISO string from state.json, so sync's sort-by-updated crashed with str/int comparison (TypeError). - kimi: list() now converts it with core.iso_ms. Tests: 3 new (Hermes mirror round-trip + title collision, reading a real session, WAL store without -wal/-shm sidecars; Kimi ISO updatedAt).
…ision, delete)
1. write() on a store that does not exist yet (Hermes installed but never run)
raised a raw OperationalError, which is not a HopError, so sync queued the
chat for retry on every turn forever. It now raises HopError('run Hermes
once so its state.db exists') before touching anything, like Gemini/Kimi.
2. A title collision retried with no title at all, leaving mirrors untitled
(Hermes requires unique titles). It now retries with ' · <last 6 of id>'.
3. delete() (hop forget) and retitle() crashed when ~/.hermes was gone. The
existence check moved into connect() so every rw caller treats a missing
store as a no-op.
Also from the same review:
- started_at is the first message's time, not the last.
- The delete in write() moved inside the transaction block.
- list() uses one grouped query for message counts instead of two per session.
- Tests: per-test store reset (no test depends on another having run), the WAL
test seeds its own session, HOP renamed to HERMES_HOME_DIR.
- Docs: pyproject description/keywords and the README 'Tested on' line mention
Hermes. Hero image left for the maintainer's make_hero.py.
- Added 3 tests: missing store -> HopError + delete no-op, title collision
keeps a suffixed title, WAL store without sidecars.
agy owns a protobuf execution trajectory (conversations/<cid>.db) that hop does not write, but a conversation's readable history is a JSONL transcript under brain/<cid>/ and its picker index is the conversation_summaries table. Writing those two, with app_data_dir != 'antigravity-cli', puts a mirror under agy's resume-picker 'Other' tab (verified: rows with app_data_dir='antigravity-cli' are the 'CLI' tab; every other app_data_dir is 'Other'). - write(): emit the transcript JSONL (the exact inverse of read()) + a summary row, so a mirror is readable in agy and continues as text. # ponytail: no protobuf trajectory, so no agy-native tool state; upgrade via agy's importConversationCmd if bit-identical resume matters. - delete()/retitle(): remove the transcript + row / rename the label, so hop forget is reversible. - Mirror detection: rows are tagged samethread and list() sets is_hop, so sync never re-imports its own copy (no loop). - title == preview == the picker label, matching real agy rows. - write() raises HopError (not a raw OperationalError) if agy has never run. Tests: write/read round-trip, 'Other'-tab shape (app_data_dir != cli, empty project/parent, depth 0), is_hop no-loop, delete removes both, missing store -> HopError. 19 tests green on py3.9 and py3.12 with the exact CI commands. Docs updated where they claimed agy was unwritable.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
A critical sync registration failure and multiple correctness and cleanup issues remain unresolved.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (6)
What changed in this PR
Makes the Antigravity (agy) adapter writable by mirroring chats into its Other tab using JSONL transcripts and summary rows.
Changes:
- Adds agy write, retitle, delete, and mirror-detection support.
- Adds persistence and cleanup tests.
- Updates documentation for agy mirroring and limitations.
| File | Summary and final review comments |
|---|---|
tests/test_core.py |
Adds agy persistence, round-trip, metadata, missing-store, and deletion coverage. |
src/samethread/agents/antigravity.py |
Implements writable mirrors. Critical (2 votes): materialize() reads missing res['seen'], preventing index registration. Moderate: update both title and preview on retitle (3 votes); construct valid Windows file URIs (2 votes); preserve user text containing closing delimiters (2 votes); guard incomplete stores before writing (3 votes); raise HopError when deletion cannot clean up (1 vote). |
README.md |
Nit (3 votes): update outdated agy seeding guidance to describe population through hop sync in the Other tab. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+129
to
+130
| return self.session(cid, path=path, title=title, sig=file_sig(path), cwd=cwd, | ||
| updated=msgs[-1]['ts'] if msgs else 0, is_hop=True) |
Comment on lines
+112
to
+113
| if not os.path.isdir(ROOT): | ||
| raise HopError('run agy once so its store exists') |
| if m['role'] == 'user': | ||
| rec = {'step_index': i, 'source': 'USER_EXPLICIT', 'type': 'USER_INPUT', | ||
| 'status': 'DONE', 'created_at': ms_iso(m['ts']), | ||
| 'content': '<USER_REQUEST>\n' + (m['text'] or '') + '\n</USER_REQUEST>'} |
| 'values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', | ||
| (cid, label, label, len(msgs), | ||
| stamp(msgs[-1]['ts'] if msgs else 0), | ||
| json.dumps(['file://' + urllib.parse.quote(cwd or '', safe='/')]), |
Comment on lines
+160
to
+161
| c.execute('update conversation_summaries set title=? where conversation_id=?', | ||
| (title or '', (cp.get('id') or '').split(':', 1)[-1])) |
| | `hop sync` | Mirror new and changed chats. The hooks run this for you. | | ||
| | `hop list [-a]` | Chats for this folder (or all folders), with the exact resume command for every copy. | | ||
| | `hop resume <agent> [n]` | Open chat *n* in any agent: `cc`, `oc`, `codex`, `gemini`, `qwen`, `pi`, `kimi`, `agy`, `mmx`. For Antigravity and MiniMax it seeds a new conversation, and its turns flow back to the others. | | ||
| | `hop resume <agent> [n]` | Open chat *n* in any agent: `cc`, `oc`, `codex`, `gemini`, `qwen`, `pi`, `kimi`, `agy`, `mmx`. For Antigravity and MiniMax it seeds a new conversation; `hop sync` also writes mirrors into Antigravity's `"Other"` tab. Turns flow back to the others. | |
Author
Hermes audit — changes requestedThis PR does not currently connect to agy correctly. Blocking
High/medium
Verification
|
# Conflicts: # README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Combines the Hermes adapter and writable Antigravity adapter work into one pull request.
updatedAttimestamp crash during sync.agy) writable by mirroring foreign chats into its nativeOtherstore.hop agychat selection instead of automatically opening the newest conversation.hop agy <number|title>.hop sync-agyrunning after an individual seed failure.HopErrorinstead of an endless raw-error retry loop.hop refreshas an alias forhop syncwhen manually catching up a live chat.Verified
python -m unittest discover -s tests -v: 31 tests pass.git diff --checkpass.hopwrapper and a fakeagyexecutable, confirming the selected thread is passed to the handoff.hop refresh --dry-run --days 1detected the live "Fix hop agy auto-resuming handoff" chat.hop syncindexed that live chat, and it is now visible inhop agy.action_requiredpending maintainer approval, not failed.Honest limits
Agy mirrors are readable and continue as text, but do not preserve agy-native protobuf tool state. The current implementation uses the readable JSONL transcript and summary store rather than reconstructing agy's proprietary execution trajectory.