-
Notifications
You must be signed in to change notification settings - Fork 0
Make Swift and Rust peer implementation lanes #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,9 @@ | ||
| [workspace] | ||
| resolver = "2" | ||
| members = ["crates/agent-session-core"] | ||
| members = ["implementations/rust/crates/agent-session-core"] | ||
|
|
||
| # Rust implementation lane of agent-session-kit. The Swift package rooted at | ||
| # Package.swift and this Cargo workspace are peer implementations of the same | ||
| # session-reading semantics; contracts and fixtures are shared. The Swift | ||
| # sources are untouched by this workspace. | ||
| # Rust implementation lane of agent-session-kit. This workspace and the | ||
| # SwiftPM package rooted at Package.swift are peer implementations of the | ||
| # same session-reading semantics, living side by side under | ||
| # implementations/. Both manifests stay at the repository root so neither | ||
| # language's tooling needs a non-default working directory. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,32 +1,72 @@ | ||
| # agent-session-kit | ||
|
|
||
| A Swift package that reads the session stores AI coding agents leave on your | ||
| Mac, and serves them over MCP. | ||
| Reads the session stores AI coding agents leave on your machine, and serves | ||
| them over MCP. **Two implementations, one set of semantics:** a Swift package | ||
| for macOS hosts and a Rust crate for cross-platform ones. | ||
|
|
||
| Every coding agent keeps a local record of what you asked it — Codex writes | ||
| rollout JSONL, Claude Code writes transcript JSONL, Cursor and AntiGravity | ||
| write SQLite databases another process holds open. Each one has its own | ||
| layout, its own idea of a "session id", and its own way of spelling a | ||
| timestamp. This package hides all of that behind one adapter protocol, a | ||
| timestamp. This repository hides all of that behind one adapter protocol, a | ||
| searchable index, and a local MCP transport. | ||
|
|
||
| No third-party dependencies. Foundation, Darwin, and the system SQLite. | ||
| No third-party dependencies in either lane beyond the system SQLite. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This claim is false for the newly documented Rust lane: its manifest depends on Useful? React with 👍 / 👎. |
||
|
|
||
| ```swift | ||
| .package(url: "https://github.com/AstroQore/agent-session-kit", exact: "0.3.0") | ||
| // Swift — macOS 26, Swift 6.2 | ||
| .package(url: "https://github.com/AstroQore/agent-session-kit", exact: "0.7.0") | ||
| ``` | ||
|
|
||
| ```toml | ||
| # Rust — macOS, Linux, Windows | ||
| agent-session-core = { git = "https://github.com/AstroQore/agent-session-kit", tag = "0.7.0" } | ||
| ``` | ||
|
|
||
| `exact:` while the package is `0.x` — see | ||
| [Versioning and releases](#versioning-and-releases). | ||
|
|
||
| ## Targets | ||
| ## Layout | ||
|
|
||
| ```text | ||
| Package.swift SwiftPM entry point | ||
| Cargo.toml Cargo workspace entry point | ||
| contracts/ Semantics both lanes are checked against | ||
| implementations/swift/ Sources/ + Tests/ | ||
| implementations/rust/ crates/ | ||
| ``` | ||
|
|
||
| Both manifests stay at the repository root, so a consumer's git URL, pin, and | ||
| `import AgentSessionKit` are unaffected by the layout — and neither language's | ||
| tooling needs a non-default working directory. | ||
|
|
||
| The two lanes are peers, not a primary and a port. Neither is generated from | ||
| the other, which is why anything they must agree on lives in `contracts/` with | ||
| a test on each side. Today that is the session index schema | ||
| (`contracts/storage/session-index-v5.sql`): the Swift writer is checked to | ||
| create exactly those objects, and the Rust reader is checked to open a database | ||
| built from that file. Add to `contracts/` whenever a new fact has to hold in | ||
| both languages; a change there is a coordinated change plus a kit minor bump. | ||
|
|
||
| ## What each lane provides | ||
|
|
||
| **Swift** (`implementations/swift`, macOS 26, Swift 6.2) | ||
|
|
||
| | Target | What it is | | ||
| | ------ | ---------- | | ||
| | `AgentSessionKit` | Discovery, parsing, deletion planning, the FTS5 session index, and the MCP Unix-socket / stdio transport. | | ||
| | `AgentSessionLive` | Live views over the same stores — the unified event model, the state reducer, and the tailing protocols. | | ||
|
|
||
| Requires macOS 26 and Swift 6.2. | ||
| **Rust** (`implementations/rust`, macOS / Linux / Windows) | ||
|
|
||
| | Crate | What it is | | ||
| | ----- | ---------- | | ||
| | `agent-session-core` | Read-only session index access, lightweight Codex and Claude Code discovery, tolerant JSONL transcript paging, and the resume command builder. | | ||
|
|
||
| The Rust lane is deliberately the smaller of the two: it covers what a | ||
| cross-platform host needs to render sessions without the Swift runtime. It | ||
| never writes the index — schema mismatch is refused, never rebuilt — because | ||
| the writer is the host that owns the file. | ||
|
|
||
| ## Architecture | ||
|
|
||
|
|
@@ -544,16 +584,16 @@ package is `0.x`, a **minor** bump carries anything a caller could trip over | |
| storage key) and a **patch** carries fixes and internals only. Pin `exact:`. | ||
|
|
||
| ```swift | ||
| .package(url: "https://github.com/AstroQore/agent-session-kit", exact: "0.3.0") | ||
| .package(url: "https://github.com/AstroQore/agent-session-kit", exact: "0.7.0") | ||
| ``` | ||
|
|
||
| Every build knows which version it is: | ||
|
|
||
| ```swift | ||
| import AgentSessionKit | ||
|
|
||
| AgentSessionKitInfo.version // "0.3.0" | ||
| AgentSessionKitInfo.bundledReleaseNotesURL // .../releases/tag/0.3.0 | ||
| AgentSessionKitInfo.version // "0.7.0" | ||
| AgentSessionKitInfo.bundledReleaseNotesURL // .../releases/tag/0.7.0 | ||
| AgentSessionKitInfo.repositoryURL | ||
| ``` | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| -- Session index, schema version 5. | ||
| -- | ||
| -- Canonical DDL for `session_index.sqlite3`. Both implementation lanes are | ||
| -- checked against this file: the Swift writer creates exactly these objects, | ||
| -- and the Rust reader refuses any database whose `PRAGMA user_version` is not | ||
| -- 5 rather than rebuilding it. | ||
| -- | ||
| -- The index is derived data. A host may delete the file and pay a re-scan, so | ||
| -- a version bump is a re-index, never a migration. Changing anything here is | ||
| -- therefore a coordinated change across both lanes plus a kit minor version. | ||
|
|
||
| PRAGMA user_version = 5; | ||
|
|
||
| CREATE TABLE IF NOT EXISTS session_index_meta ( | ||
| key TEXT PRIMARY KEY, | ||
| value TEXT NOT NULL | ||
| ); | ||
| CREATE TABLE IF NOT EXISTS sessions ( | ||
| id INTEGER PRIMARY KEY, | ||
| provider TEXT NOT NULL, | ||
| session_id TEXT NOT NULL, | ||
| provider_variant TEXT, | ||
| harness TEXT, | ||
| model TEXT, | ||
| title TEXT, | ||
| summary TEXT, | ||
| project_dir TEXT, | ||
| created_at INTEGER, | ||
| last_active_at INTEGER, | ||
| source_path TEXT NOT NULL, | ||
| size_bytes INTEGER NOT NULL DEFAULT 0, | ||
| message_count INTEGER NOT NULL DEFAULT -1, | ||
| UNIQUE(provider, session_id, source_path) | ||
| ); | ||
| CREATE INDEX IF NOT EXISTS sessions_last_active_idx | ||
| ON sessions(last_active_at DESC); | ||
| CREATE INDEX IF NOT EXISTS sessions_effective_active_idx | ||
| ON sessions(COALESCE(last_active_at, created_at) DESC, id DESC); | ||
| CREATE INDEX IF NOT EXISTS sessions_provider_effective_active_idx | ||
| ON sessions(provider, COALESCE(last_active_at, created_at) DESC, id DESC); | ||
| CREATE INDEX IF NOT EXISTS sessions_provider_project_idx | ||
| ON sessions(provider, project_dir); | ||
| CREATE INDEX IF NOT EXISTS sessions_harness_effective_active_idx | ||
| ON sessions(harness, COALESCE(last_active_at, created_at) DESC, id DESC); | ||
| CREATE TABLE IF NOT EXISTS session_files ( | ||
| path_hash TEXT PRIMARY KEY, | ||
| path TEXT NOT NULL, | ||
| provider TEXT NOT NULL, | ||
| mtime_ns INTEGER NOT NULL, | ||
| size INTEGER NOT NULL, | ||
| session_row INTEGER REFERENCES sessions(id) ON DELETE CASCADE | ||
| ); | ||
| CREATE TABLE IF NOT EXISTS session_messages ( | ||
| id INTEGER PRIMARY KEY, | ||
| session_row INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE, | ||
| seq INTEGER NOT NULL, | ||
| role TEXT NOT NULL, | ||
| excerpt TEXT NOT NULL | ||
| ); | ||
| CREATE INDEX IF NOT EXISTS session_messages_row_idx | ||
| ON session_messages(session_row, seq); | ||
| CREATE VIRTUAL TABLE IF NOT EXISTS session_fts USING fts5( | ||
| excerpt, | ||
| content='session_messages', | ||
| content_rowid='id', | ||
| tokenize='trigram' | ||
| ); | ||
| CREATE TRIGGER IF NOT EXISTS session_messages_ai | ||
| AFTER INSERT ON session_messages BEGIN | ||
| INSERT INTO session_fts(rowid, excerpt) VALUES (new.id, new.excerpt); | ||
| END; | ||
| CREATE TRIGGER IF NOT EXISTS session_messages_ad | ||
| AFTER DELETE ON session_messages BEGIN | ||
| INSERT INTO session_fts(session_fts, rowid, excerpt) | ||
| VALUES('delete', old.id, old.excerpt); | ||
| END; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a later shared release updates the Swift constant and changelog but accidentally leaves the Rust manifest behind, this job still publishes because it validates only
AgentSessionKitInfo.versionand runs only Swift commands. Rust consumers fetching that tag then receive a crate whose package version disagrees with the release, so the release check should also compareimplementations/rust/crates/agent-session-core/Cargo.tomlwithTAGbefore publishing.AGENTS.md reference: AGENTS.md:L74-L79
Useful? React with 👍 / 👎.