From 8bbef3314ad9b8ae9d26bfc797a59afbe1ed7708 Mon Sep 17 00:00:00 2001 From: AstroQore <69107895+AstroQore@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:52:03 +0800 Subject: [PATCH 1/2] Make Swift and Rust peer implementation lanes The Rust crate arrived as crates/ beside the Swift Sources/, which read as a primary implementation with an attachment. It is not one: the two lanes implement the same session-reading semantics for different hosts, and neither is generated from the other. Sources/ and Tests/ move to implementations/swift/, crates/ moves to implementations/rust/. Both manifests stay at the repository root and reach in with explicit paths, so no consumer of either language changes anything. Peers that are not generated from each other need somewhere to agree, so contracts/ arrives with the first fact that qualifies: the session index DDL and its user_version, which until now existed only inside the Swift writer while the Rust reader asserted a matching number by hand. Each lane now has a test that reads that file -- Swift checks it creates exactly those objects, Rust builds a database from it and opens it -- so a schema change on one side alone fails CI instead of silently diverging. The move also caught a real break: AgentSessionKitInfoTests walked three directories up from #filePath to find CHANGELOG.md, which is five now. It had been failing as an XCTest case while the swift-testing summary line still said everything passed. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci-rust.yml | 6 +- .github/workflows/{ci.yml => ci-swift.yml} | 12 ++- AGENTS.md | 28 ++++-- CHANGELOG.md | 32 +++++++ Cargo.lock | 2 +- Cargo.toml | 11 +-- Package.swift | 10 ++- README.md | 60 ++++++++++--- contracts/storage/session-index-v5.sql | 76 +++++++++++++++++ .../crates}/agent-session-core/Cargo.toml | 2 +- .../rust/crates}/agent-session-core/README.md | 0 .../agent-session-core/examples/smoke.rs | 0 .../agent-session-core/src/discovery.rs | 0 .../crates}/agent-session-core/src/error.rs | 0 .../crates}/agent-session-core/src/index.rs | 52 ++++++++++++ .../crates}/agent-session-core/src/jsonl.rs | 0 .../crates}/agent-session-core/src/lib.rs | 0 .../agent-session-core/src/provider.rs | 0 .../crates}/agent-session-core/src/resume.rs | 0 .../agent-session-core/src/transcript.rs | 0 .../AgentSessionKit/AgentSessionKitInfo.swift | 2 +- .../AntigravityGenMetadataReader.swift | 0 .../AgentSessionKit/Harness/Harness.swift | 0 .../AgentSessionKit/MCP/MCPArguments.swift | 0 .../AgentSessionKit/MCP/MCPDescriptors.swift | 0 .../AgentSessionKit/MCP/MCPJSONRPC.swift | 0 .../AgentSessionKit/MCP/MCPSocketServer.swift | 0 .../AgentSessionKit/MCP/MCPStdioBridge.swift | 0 .../AntigravityConversationIndex.swift | 0 .../Sessions/AntigravitySessionAdapter.swift | 0 .../Sessions/ClaudeCoworkSessionAdapter.swift | 0 .../Sessions/ClaudeSessionAdapter.swift | 0 .../Sessions/CodexSessionAdapter.swift | 0 .../Sessions/CodexTitleHydrator.swift | 0 .../Sessions/CursorSessionAdapter.swift | 0 .../Sessions/GeminiSessionAdapter.swift | 0 .../Sessions/GrokBotSessionAdapter.swift | 0 .../Sessions/GrokSessionAdapter.swift | 0 .../Sessions/LiveSQLiteReader.swift | 0 .../Sessions/ProtobufWireReader.swift | 0 .../Sessions/SessionDeleter.swift | 0 .../Sessions/SessionIndexService.swift | 0 .../Sessions/SessionIndexStore.swift | 4 + .../Sessions/SessionModels.swift | 0 .../Sessions/SessionParsing.swift | 0 .../Sessions/SessionProviderAdapter.swift | 0 .../SessionResumeCommandBuilder.swift | 0 .../AgentSessionKit/Utilities/Base32.swift | 0 .../Utilities/ClaudeCoworkPaths.swift | 0 .../Utilities/CodexOriginator.swift | 0 .../Utilities/HumanPromptText.swift | 0 .../Utilities/JSONLHeadTail.swift | 0 .../Utilities/JSONLLineScanner.swift | 0 .../AgentSessionKit/Utilities/KitLog.swift | 0 .../Utilities/PrivacyPreservingHash.swift | 0 .../Utilities/RealHomeDirectory.swift | 0 .../AntigravityConversationReader.swift | 0 .../AntigravityConversationRegistry.swift | 0 .../AntigravityConversationTailer.swift | 0 .../Antigravity/AntigravityLiveAdapter.swift | 0 .../Antigravity/AntigravityStepMapper.swift | 0 .../Antigravity/AntigravityStepPayload.swift | 0 .../Antigravity/AntigravityStepStatus.swift | 0 .../Antigravity/AntigravityStepType.swift | 0 .../AntigravityWorkspaceIndex.swift | 0 .../Claude/ClaudeCoworkLiveAdapter.swift | 0 .../Adapters/Claude/ClaudeLiveAdapter.swift | 0 .../Adapters/Claude/ClaudeProjectPath.swift | 0 .../Adapters/Claude/ClaudeRecordMapper.swift | 0 .../Claude/ClaudeSessionsDirectory.swift | 0 .../Adapters/Claude/ClaudeSourceBuilder.swift | 0 .../Claude/ClaudeSubagentLinker.swift | 0 .../Claude/ClaudeTranscriptRecord.swift | 0 .../Adapters/Codex/CodexLiveAdapter.swift | 0 .../Adapters/Codex/CodexRecordMapper.swift | 0 .../Adapters/Codex/CodexRolloutRecord.swift | 0 .../Adapters/Codex/CodexSubagentLinker.swift | 0 .../Adapters/Codex/CodexToolCall.swift | 0 .../Adapters/Cursor/CursorLiveAdapter.swift | 0 .../Adapters/Cursor/CursorMessage.swift | 0 .../Adapters/Cursor/CursorMessageMapper.swift | 0 .../Adapters/Cursor/CursorPaths.swift | 0 .../Adapters/Cursor/CursorSessionTailer.swift | 0 .../Adapters/Cursor/CursorStoreMeta.swift | 0 .../Adapters/Cursor/CursorStoreReader.swift | 0 .../Cursor/CursorThinTranscriptMapper.swift | 0 .../Adapters/Cursor/CursorWorkerProbe.swift | 0 .../Adapters/Grok/GrokLiveAdapter.swift | 0 .../Adapters/Grok/GrokRecordMapper.swift | 0 .../Adapters/Grok/GrokRecords.swift | 0 .../Adapters/Grok/GrokSessionsPath.swift | 0 .../Adapters/Grok/GrokSignalsReader.swift | 0 .../Adapters/Grok/GrokToolCall.swift | 0 .../Adapters/GrokBot/GrokBotEntry.swift | 0 .../Adapters/GrokBot/GrokBotEntryMapper.swift | 0 .../Adapters/GrokBot/GrokBotLiveAdapter.swift | 0 .../Adapters/GrokBot/GrokBotStore.swift | 0 .../GrokBot/GrokBotTranscriptTailer.swift | 0 .../AgentSessionLive/AgentSessionLive.swift | 0 .../AgentSessionLive/Events/AgentEvent.swift | 0 .../Events/ContextUsage.swift | 0 .../Events/SessionBrief.swift | 0 .../Events/SessionIdentity.swift | 0 .../AgentSessionLive/Events/SessionKey.swift | 0 .../Events/SessionResume.swift | 0 .../Events/SessionSnapshot.swift | 0 .../Events/SessionState.swift | 0 .../Events/SessionStateReducer.swift | 0 .../Liveness/LivenessResolver.swift | 0 .../Liveness/LockFileProbe.swift | 0 .../Liveness/ProcessLinker.swift | 0 .../Liveness/ProcessTable.swift | 0 .../Liveness/ProcessTableReading.swift | 0 .../Liveness/RegistrySnapshot.swift | 0 .../SessionEnvironmentVariables.swift | 0 .../Sources/DiscoveryCache.swift | 0 .../Sources/DiscoveryIO.swift | 0 .../Sources/LivenessHint.swift | 0 .../Sources/SessionSource.swift | 0 .../Sources/SessionTailer.swift | 0 .../Sources/SourceAdapter.swift | 0 .../Sources/SourceCursor.swift | 0 .../Tailing/FSEventsWatcher.swift | 0 .../AgentSessionLive/Tailing/FileStamp.swift | 0 .../Tailing/IngestCoordinator.swift | 0 .../Tailing/JSONLTailer.swift | 0 .../Tailing/SQLiteChangeWatcher.swift | 0 .../Tailing/SourceCursorStore.swift | 0 .../Tailing/TailerBackpressure.swift | 0 .../Utilities/ArgvSanitizer.swift | 0 .../Utilities/EventText.swift | 0 .../AgentSessionKitInfoTests.swift | 5 +- .../AntigravityElapsedDateTests.swift | 0 .../AntigravitySessionAdapterTests.swift | 0 .../AgentSessionKitTests/Base32Tests.swift | 0 .../ClaudeCoworkSessionAdapterTests.swift | 0 .../ClaudeSessionAdapterTests.swift | 0 .../CodexSessionAdapterTests.swift | 0 .../CodexTitleHydratorTests.swift | 0 .../CursorSessionAdapterTests.swift | 0 .../GeminiSessionAdapterTests.swift | 0 .../GrokBotSessionAdapterTests.swift | 0 .../GrokSessionAdapterTests.swift | 0 .../HarnessNamingTests.swift | 0 .../HumanPromptTextTests.swift | 0 .../JSONLHeadTailTests.swift | 0 .../MCPJSONRPCTests.swift | 0 .../MCPSocketServerTests.swift | 0 .../MCPStdioBridgeTests.swift | 0 .../PrivacyPreservingHashTests.swift | 0 .../ProtobufWireReaderTests.swift | 0 .../SessionDeleterTests.swift | 0 .../SessionIndexContractTests.swift | 85 +++++++++++++++++++ .../SessionIndexServiceTests.swift | 0 .../SessionIndexStoreTests.swift | 0 .../SessionResumeCommandBuilderTests.swift | 0 .../Adapters/AntigravityFixtureSupport.swift | 0 .../AntigravityLiveAdapterTests.swift | 0 .../ClaudeCoworkLiveAdapterTests.swift | 0 .../Adapters/ClaudeFixtureSupport.swift | 0 .../Adapters/ClaudeLiveAdapterTests.swift | 0 .../Adapters/ClaudeRecordMapperTests.swift | 0 .../Adapters/CodexLiveAdapterTests.swift | 0 .../Adapters/CursorFixtureSupport.swift | 0 .../Adapters/CursorLiveAdapterTests.swift | 0 .../Adapters/GrokBotFixtureSupport.swift | 0 .../Adapters/GrokBotLiveAdapterTests.swift | 0 .../Adapters/GrokLiveAdapterTests.swift | 0 .../ArgvSanitizerTests.swift | 0 .../ContextUsageTests.swift | 0 .../CursorPersistenceTests.swift | 0 .../DiscoveryCostTests.swift | 0 .../DiscoveryRoutingTests.swift | 0 .../EventCodableTests.swift | 0 .../EventTextTests.swift | 0 .../FSEventsWatcherTests.swift | 0 .../AgentSessionLiveTests/Fixtures/README.md | 0 .../Fixtures/antigravity/.gitkeep | 0 .../Fixtures/claude/.gitkeep | 0 .../Fixtures/claude/README.md | 0 .../Fixtures/claude/session-pid.json | 0 .../Fixtures/claude/session.jsonl | 0 .../Fixtures/claude/subagent.jsonl | 0 .../Fixtures/claude/subagent.meta.json | 0 .../Fixtures/codex/.gitkeep | 0 .../Fixtures/codex/cli-shapes.jsonl | 0 .../Fixtures/codex/events-sampler.jsonl | 0 .../Fixtures/codex/rollout.jsonl | 0 .../Fixtures/cursor/.gitkeep | 0 .../Fixtures/cursor/thin-transcript.jsonl | 0 .../Fixtures/grok/.gitkeep | 0 .../Fixtures/grok/README.md | 0 .../Fixtures/grok/session/chat_history.jsonl | 0 .../Fixtures/grok/session/events.jsonl | 0 .../Fixtures/grok/session/signals.json | 0 .../Fixtures/grok/session/summary.json | 0 .../Fixtures/grok/session/updates.jsonl | 0 .../IngestCoordinatorTests.swift | 0 .../JSONLTailerTests.swift | 0 .../LiveTestSupport.swift | 0 .../LivenessResolverTests.swift | 0 .../LockFileProbeTests.swift | 0 .../PipelineCostTests.swift | 0 .../ProcessLinkerTests.swift | 0 .../ProcessTableTests.swift | 0 .../RegistrySnapshotTests.swift | 0 .../SessionBriefFixtureTests.swift | 0 .../SessionBriefTests.swift | 0 .../SessionKeyTests.swift | 0 .../SessionResumeTests.swift | 0 .../SessionStateReducerTests.swift | 0 .../AgentSessionLiveTests/TestSupport.swift | 0 212 files changed, 359 insertions(+), 28 deletions(-) rename .github/workflows/{ci.yml => ci-swift.yml} (71%) create mode 100644 contracts/storage/session-index-v5.sql rename {crates => implementations/rust/crates}/agent-session-core/Cargo.toml (96%) rename {crates => implementations/rust/crates}/agent-session-core/README.md (100%) rename {crates => implementations/rust/crates}/agent-session-core/examples/smoke.rs (100%) rename {crates => implementations/rust/crates}/agent-session-core/src/discovery.rs (100%) rename {crates => implementations/rust/crates}/agent-session-core/src/error.rs (100%) rename {crates => implementations/rust/crates}/agent-session-core/src/index.rs (96%) rename {crates => implementations/rust/crates}/agent-session-core/src/jsonl.rs (100%) rename {crates => implementations/rust/crates}/agent-session-core/src/lib.rs (100%) rename {crates => implementations/rust/crates}/agent-session-core/src/provider.rs (100%) rename {crates => implementations/rust/crates}/agent-session-core/src/resume.rs (100%) rename {crates => implementations/rust/crates}/agent-session-core/src/transcript.rs (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/AgentSessionKitInfo.swift (98%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Antigravity/AntigravityGenMetadataReader.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Harness/Harness.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/MCP/MCPArguments.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/MCP/MCPDescriptors.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/MCP/MCPJSONRPC.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/MCP/MCPSocketServer.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/MCP/MCPStdioBridge.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/AntigravityConversationIndex.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/AntigravitySessionAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/ClaudeCoworkSessionAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/ClaudeSessionAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/CodexSessionAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/CodexTitleHydrator.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/CursorSessionAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/GeminiSessionAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/GrokBotSessionAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/GrokSessionAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/LiveSQLiteReader.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/ProtobufWireReader.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/SessionDeleter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/SessionIndexService.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/SessionIndexStore.swift (99%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/SessionModels.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/SessionParsing.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/SessionProviderAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Sessions/SessionResumeCommandBuilder.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Utilities/Base32.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Utilities/ClaudeCoworkPaths.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Utilities/CodexOriginator.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Utilities/HumanPromptText.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Utilities/JSONLHeadTail.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Utilities/JSONLLineScanner.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Utilities/KitLog.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Utilities/PrivacyPreservingHash.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionKit/Utilities/RealHomeDirectory.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Antigravity/AntigravityConversationReader.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Antigravity/AntigravityConversationRegistry.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Antigravity/AntigravityConversationTailer.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Antigravity/AntigravityLiveAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Antigravity/AntigravityStepMapper.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Antigravity/AntigravityStepPayload.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Antigravity/AntigravityStepStatus.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Antigravity/AntigravityStepType.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Antigravity/AntigravityWorkspaceIndex.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Claude/ClaudeCoworkLiveAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Claude/ClaudeLiveAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Claude/ClaudeProjectPath.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Claude/ClaudeRecordMapper.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Claude/ClaudeSessionsDirectory.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Claude/ClaudeSourceBuilder.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Claude/ClaudeSubagentLinker.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Claude/ClaudeTranscriptRecord.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Codex/CodexLiveAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Codex/CodexRecordMapper.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Codex/CodexRolloutRecord.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Codex/CodexSubagentLinker.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Codex/CodexToolCall.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Cursor/CursorLiveAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Cursor/CursorMessage.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Cursor/CursorMessageMapper.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Cursor/CursorPaths.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Cursor/CursorSessionTailer.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Cursor/CursorStoreMeta.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Cursor/CursorStoreReader.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Cursor/CursorThinTranscriptMapper.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Cursor/CursorWorkerProbe.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Grok/GrokLiveAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Grok/GrokRecordMapper.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Grok/GrokRecords.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Grok/GrokSessionsPath.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Grok/GrokSignalsReader.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/Grok/GrokToolCall.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/GrokBot/GrokBotEntry.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/GrokBot/GrokBotEntryMapper.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/GrokBot/GrokBotLiveAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/GrokBot/GrokBotStore.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Adapters/GrokBot/GrokBotTranscriptTailer.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/AgentSessionLive.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Events/AgentEvent.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Events/ContextUsage.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Events/SessionBrief.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Events/SessionIdentity.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Events/SessionKey.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Events/SessionResume.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Events/SessionSnapshot.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Events/SessionState.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Events/SessionStateReducer.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Liveness/LivenessResolver.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Liveness/LockFileProbe.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Liveness/ProcessLinker.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Liveness/ProcessTable.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Liveness/ProcessTableReading.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Liveness/RegistrySnapshot.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Liveness/SessionEnvironmentVariables.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Sources/DiscoveryCache.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Sources/DiscoveryIO.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Sources/LivenessHint.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Sources/SessionSource.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Sources/SessionTailer.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Sources/SourceAdapter.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Sources/SourceCursor.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Tailing/FSEventsWatcher.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Tailing/FileStamp.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Tailing/IngestCoordinator.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Tailing/JSONLTailer.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Tailing/SQLiteChangeWatcher.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Tailing/SourceCursorStore.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Tailing/TailerBackpressure.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Utilities/ArgvSanitizer.swift (100%) rename {Sources => implementations/swift/Sources}/AgentSessionLive/Utilities/EventText.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/AgentSessionKitInfoTests.swift (96%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/AntigravityElapsedDateTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/AntigravitySessionAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/Base32Tests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/ClaudeCoworkSessionAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/ClaudeSessionAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/CodexSessionAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/CodexTitleHydratorTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/CursorSessionAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/GeminiSessionAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/GrokBotSessionAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/GrokSessionAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/HarnessNamingTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/HumanPromptTextTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/JSONLHeadTailTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/MCPJSONRPCTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/MCPSocketServerTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/MCPStdioBridgeTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/PrivacyPreservingHashTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/ProtobufWireReaderTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/SessionDeleterTests.swift (100%) create mode 100644 implementations/swift/Tests/AgentSessionKitTests/SessionIndexContractTests.swift rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/SessionIndexServiceTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/SessionIndexStoreTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionKitTests/SessionResumeCommandBuilderTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/AntigravityFixtureSupport.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/AntigravityLiveAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/ClaudeCoworkLiveAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/ClaudeFixtureSupport.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/ClaudeLiveAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/ClaudeRecordMapperTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/CodexLiveAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/CursorFixtureSupport.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/CursorLiveAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/GrokBotFixtureSupport.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/GrokBotLiveAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Adapters/GrokLiveAdapterTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/ArgvSanitizerTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/ContextUsageTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/CursorPersistenceTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/DiscoveryCostTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/DiscoveryRoutingTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/EventCodableTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/EventTextTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/FSEventsWatcherTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/README.md (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/antigravity/.gitkeep (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/claude/.gitkeep (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/claude/README.md (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/claude/session-pid.json (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/claude/session.jsonl (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/claude/subagent.jsonl (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/claude/subagent.meta.json (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/codex/.gitkeep (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/codex/cli-shapes.jsonl (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/codex/events-sampler.jsonl (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/codex/rollout.jsonl (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/cursor/.gitkeep (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/cursor/thin-transcript.jsonl (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/grok/.gitkeep (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/grok/README.md (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/grok/session/chat_history.jsonl (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/grok/session/events.jsonl (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/grok/session/signals.json (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/grok/session/summary.json (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/Fixtures/grok/session/updates.jsonl (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/IngestCoordinatorTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/JSONLTailerTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/LiveTestSupport.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/LivenessResolverTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/LockFileProbeTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/PipelineCostTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/ProcessLinkerTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/ProcessTableTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/RegistrySnapshotTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/SessionBriefFixtureTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/SessionBriefTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/SessionKeyTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/SessionResumeTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/SessionStateReducerTests.swift (100%) rename {Tests => implementations/swift/Tests}/AgentSessionLiveTests/TestSupport.swift (100%) diff --git a/.github/workflows/ci-rust.yml b/.github/workflows/ci-rust.yml index d0d7ec2..f99736f 100644 --- a/.github/workflows/ci-rust.yml +++ b/.github/workflows/ci-rust.yml @@ -7,7 +7,8 @@ on: - "Cargo.toml" - "Cargo.lock" - "rust-toolchain.toml" - - "crates/**" + - "implementations/rust/**" + - "contracts/**" - ".github/workflows/ci-rust.yml" pull_request: branches: [main] @@ -15,7 +16,8 @@ on: - "Cargo.toml" - "Cargo.lock" - "rust-toolchain.toml" - - "crates/**" + - "implementations/rust/**" + - "contracts/**" - ".github/workflows/ci-rust.yml" jobs: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci-swift.yml similarity index 71% rename from .github/workflows/ci.yml rename to .github/workflows/ci-swift.yml index 3dc1be9..b0159bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci-swift.yml @@ -1,9 +1,19 @@ -name: CI +name: CI (Swift) on: push: branches: [main] + paths: + - "Package.swift" + - "implementations/swift/**" + - "contracts/**" + - ".github/workflows/ci-swift.yml" pull_request: + paths: + - "Package.swift" + - "implementations/swift/**" + - "contracts/**" + - ".github/workflows/ci-swift.yml" workflow_dispatch: concurrency: diff --git a/AGENTS.md b/AGENTS.md index fb09ba5..d550f70 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,8 +21,16 @@ own mapping as an extension in its own module. ```text . ├── Package.swift # swift-tools-version 6.2, macOS 26 +├── Cargo.toml # Cargo workspace root ├── RELEASING.md # semver policy + how a tag is cut -├── Sources/ +├── contracts/ # Facts both lanes are tested against +│ └── storage/ +│ └── session-index-v5.sql # Canonical index DDL + user_version +├── implementations/rust/ +│ └── crates/ +│ └── agent-session-core/ # macOS / Linux / Windows, read-only +└── implementations/swift/ + ├── Sources/ │ ├── AgentSessionKit/ # Swift 5 language mode (migration pending) │ │ ├── AgentSessionKitInfo.swift # version, repository URL, release-notes URL │ │ ├── Harness/ @@ -57,12 +65,22 @@ own mapping as an extension in its own module. │ │ ├── MCPArguments.swift # Typed tools/call argument decoding │ │ ├── MCPSocketServer.swift # AF_UNIX listener, 0600 │ │ └── MCPStdioBridge.swift # Byte pump for spawn-a-command clients -│ └── AgentSessionLive/ # Swift 6 language mode. Placeholder. -└── Tests/ - ├── AgentSessionKitTests/ - └── AgentSessionLiveTests/ + │ └── AgentSessionLive/ # Swift 6 language mode. Placeholder. + └── Tests/ + ├── AgentSessionKitTests/ + └── AgentSessionLiveTests/ ``` +The two implementation lanes are peers. Neither is generated from the other, +so anything that must hold in both languages goes in `contracts/` with a test +on each side — `SessionIndexContractTests` in Swift and `index::contract_tests` +in Rust both read `contracts/storage/session-index-v5.sql`. Changing a shared +fact means changing the contract, both lanes, and the kit minor version in one +pull request. + +Both manifests stay at the repository root and reach into `implementations/` +with explicit paths, so consumers' git URLs, pins, and imports never move. + Boundary rule: `AgentSessionKit` answers "what is on disk right now" — one pass, one snapshot, no observers. Anything that watches, debounces, or tails belongs in `AgentSessionLive`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 580a78b..dd24843 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,38 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.7.0] - 2026-08-31 + +Swift and Rust become peer implementation lanes. The repository now says out +loud what it had already become when the Rust crate landed: one set of +session-reading semantics with two implementations, neither generated from +the other, and a `contracts/` directory holding the facts both must honour. + +### Changed +- **Layout: `implementations/swift/` and `implementations/rust/`.** The Swift + sources and tests moved out of the repository root, and the Rust crates + moved out of `crates/`. Both manifests stay at the root and reach in with + explicit paths, so **no consumer changes anything**: the git URL, the + `exact:` pin, `import AgentSessionKit`, and both product names are + unaffected. `crates/` at the root became `implementations/rust/crates/`, + which does move the Cargo path for anyone depending on the crate by path + rather than by git. +- `ci.yml` is now `ci-swift.yml`, and both lane workflows are scoped to their + own paths plus `contracts/`. + +### Added +- **`contracts/storage/session-index-v5.sql`** — the canonical index DDL and + `user_version`, extracted from the Swift writer that had been its only + definition. Both lanes are now tested against it: `SessionIndexContractTests` + checks that Swift creates exactly the objects the contract names, and + `index::contract_tests` builds a database from the file and opens it with the + Rust reader. A schema change in either lane alone now fails CI. + +### Fixed +- `AgentSessionKitInfoTests` resolved the repository root by walking three + directories up from `#filePath`, which the new layout broke. It is five now, + and the failure was real: the test could not find `CHANGELOG.md`. + ### Fixed - **Rust discovery/search/transcript parity.** The first Rust session-core slice now mirrors Swift's default title/user/assistant search scopes, diff --git a/Cargo.lock b/Cargo.lock index 9ff68e0..103f766 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "agent-session-core" -version = "0.1.0" +version = "0.7.0" dependencies = [ "rusqlite", "serde", diff --git a/Cargo.toml b/Cargo.toml index 058e1c7..ec04159 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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. diff --git a/Package.swift b/Package.swift index 29b4596..7686587 100644 --- a/Package.swift +++ b/Package.swift @@ -10,6 +10,10 @@ let package = Package( .library(name: "AgentSessionKit", targets: ["AgentSessionKit"]), .library(name: "AgentSessionLive", targets: ["AgentSessionLive"]) ], + // Swift and Rust are peer implementation lanes under implementations/. + // Explicit paths keep this manifest at the repository root so every + // consumer's git URL, exact pin, and `import AgentSessionKit` are + // unchanged by the layout. targets: [ // Discovery, parsing, indexing, and the MCP transport. Extracted // from Vibe Bar, where this code grew up inside the app target; @@ -17,6 +21,7 @@ let package = Package( // were written against it. Migrating is its own change. .target( name: "AgentSessionKit", + path: "implementations/swift/Sources/AgentSessionKit", swiftSettings: [.swiftLanguageMode(.v5)] ), // Live views over the same stores — file-system watching and @@ -24,11 +29,13 @@ let package = Package( // because nothing here predates strict concurrency. .target( name: "AgentSessionLive", - dependencies: ["AgentSessionKit"] + dependencies: ["AgentSessionKit"], + path: "implementations/swift/Sources/AgentSessionLive" ), .testTarget( name: "AgentSessionKitTests", dependencies: ["AgentSessionKit"], + path: "implementations/swift/Tests/AgentSessionKitTests", swiftSettings: [.swiftLanguageMode(.v5)] ), // `Fixtures/` holds sample source records, one directory per @@ -38,6 +45,7 @@ let package = Package( .testTarget( name: "AgentSessionLiveTests", dependencies: ["AgentSessionLive"], + path: "implementations/swift/Tests/AgentSessionLiveTests", resources: [.copy("Fixtures")] ) ] diff --git a/README.md b/README.md index f8235a3..0fa22bd 100644 --- a/README.md +++ b/README.md @@ -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. ```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,7 +584,7 @@ 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: @@ -552,8 +592,8 @@ 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 ``` diff --git a/contracts/storage/session-index-v5.sql b/contracts/storage/session-index-v5.sql new file mode 100644 index 0000000..3d1cec2 --- /dev/null +++ b/contracts/storage/session-index-v5.sql @@ -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; diff --git a/crates/agent-session-core/Cargo.toml b/implementations/rust/crates/agent-session-core/Cargo.toml similarity index 96% rename from crates/agent-session-core/Cargo.toml rename to implementations/rust/crates/agent-session-core/Cargo.toml index 4640eb2..6191de5 100644 --- a/crates/agent-session-core/Cargo.toml +++ b/implementations/rust/crates/agent-session-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agent-session-core" -version = "0.1.0" +version = "0.7.0" edition = "2021" license = "AGPL-3.0-only" description = "Cross-platform Rust implementation of agent-session-kit's session reading: index queries, lightweight discovery, transcripts, and resume commands." diff --git a/crates/agent-session-core/README.md b/implementations/rust/crates/agent-session-core/README.md similarity index 100% rename from crates/agent-session-core/README.md rename to implementations/rust/crates/agent-session-core/README.md diff --git a/crates/agent-session-core/examples/smoke.rs b/implementations/rust/crates/agent-session-core/examples/smoke.rs similarity index 100% rename from crates/agent-session-core/examples/smoke.rs rename to implementations/rust/crates/agent-session-core/examples/smoke.rs diff --git a/crates/agent-session-core/src/discovery.rs b/implementations/rust/crates/agent-session-core/src/discovery.rs similarity index 100% rename from crates/agent-session-core/src/discovery.rs rename to implementations/rust/crates/agent-session-core/src/discovery.rs diff --git a/crates/agent-session-core/src/error.rs b/implementations/rust/crates/agent-session-core/src/error.rs similarity index 100% rename from crates/agent-session-core/src/error.rs rename to implementations/rust/crates/agent-session-core/src/error.rs diff --git a/crates/agent-session-core/src/index.rs b/implementations/rust/crates/agent-session-core/src/index.rs similarity index 96% rename from crates/agent-session-core/src/index.rs rename to implementations/rust/crates/agent-session-core/src/index.rs index b2d3991..515dfe8 100644 --- a/crates/agent-session-core/src/index.rs +++ b/implementations/rust/crates/agent-session-core/src/index.rs @@ -1513,3 +1513,55 @@ mod tests { )); } } + +#[cfg(test)] +mod contract_tests { + use super::*; + + /// The canonical DDL lives in `contracts/storage/session-index-v5.sql` and + /// is shared with the Swift lane. Building a database from it and opening + /// that database with this reader proves the two agree on the schema this + /// crate refuses to rebuild — a drift in either direction fails here. + #[test] + fn reader_accepts_a_database_built_from_the_shared_contract() { + let sql = include_str!("../../../../../contracts/storage/session-index-v5.sql"); + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("session_index.sqlite3"); + let conn = Connection::open(&path).unwrap(); + conn.execute_batch(sql).unwrap(); + drop(conn); + + let reader = SessionIndexReader::open(&path).expect("contract schema must be readable"); + assert_eq!(reader.session_count().unwrap(), 0); + // Every query path must work against the contract's own objects. + assert!(reader + .list(&SessionListFilter { + limit: 5, + ..Default::default() + }) + .unwrap() + .is_empty()); + assert!(reader + .search( + "anything", + &SessionListFilter { + limit: 5, + ..Default::default() + } + ) + .unwrap() + .is_empty()); + } + + /// The version this crate enforces is the version the contract declares. + #[test] + fn supported_version_matches_the_contract_pragma() { + let sql = include_str!("../../../../../contracts/storage/session-index-v5.sql"); + let declared = sql + .lines() + .find_map(|line| line.trim().strip_prefix("PRAGMA user_version = ")) + .and_then(|rest| rest.trim_end_matches(';').trim().parse::().ok()) + .expect("contract declares a user_version"); + assert_eq!(declared, SUPPORTED_SCHEMA_VERSION); + } +} diff --git a/crates/agent-session-core/src/jsonl.rs b/implementations/rust/crates/agent-session-core/src/jsonl.rs similarity index 100% rename from crates/agent-session-core/src/jsonl.rs rename to implementations/rust/crates/agent-session-core/src/jsonl.rs diff --git a/crates/agent-session-core/src/lib.rs b/implementations/rust/crates/agent-session-core/src/lib.rs similarity index 100% rename from crates/agent-session-core/src/lib.rs rename to implementations/rust/crates/agent-session-core/src/lib.rs diff --git a/crates/agent-session-core/src/provider.rs b/implementations/rust/crates/agent-session-core/src/provider.rs similarity index 100% rename from crates/agent-session-core/src/provider.rs rename to implementations/rust/crates/agent-session-core/src/provider.rs diff --git a/crates/agent-session-core/src/resume.rs b/implementations/rust/crates/agent-session-core/src/resume.rs similarity index 100% rename from crates/agent-session-core/src/resume.rs rename to implementations/rust/crates/agent-session-core/src/resume.rs diff --git a/crates/agent-session-core/src/transcript.rs b/implementations/rust/crates/agent-session-core/src/transcript.rs similarity index 100% rename from crates/agent-session-core/src/transcript.rs rename to implementations/rust/crates/agent-session-core/src/transcript.rs diff --git a/Sources/AgentSessionKit/AgentSessionKitInfo.swift b/implementations/swift/Sources/AgentSessionKit/AgentSessionKitInfo.swift similarity index 98% rename from Sources/AgentSessionKit/AgentSessionKitInfo.swift rename to implementations/swift/Sources/AgentSessionKit/AgentSessionKitInfo.swift index f748e69..99e5e5f 100644 --- a/Sources/AgentSessionKit/AgentSessionKitInfo.swift +++ b/implementations/swift/Sources/AgentSessionKit/AgentSessionKitInfo.swift @@ -19,7 +19,7 @@ public enum AgentSessionKitInfo { /// /// Semver, bare — no `v` prefix, matching the tags. Bumping it is part /// of the release commit; see `RELEASING.md`. - public static let version = "0.6.3" + public static let version = "0.7.0" /// Where the package lives. Public because a host that shows the /// version usually wants somewhere to send the reader. diff --git a/Sources/AgentSessionKit/Antigravity/AntigravityGenMetadataReader.swift b/implementations/swift/Sources/AgentSessionKit/Antigravity/AntigravityGenMetadataReader.swift similarity index 100% rename from Sources/AgentSessionKit/Antigravity/AntigravityGenMetadataReader.swift rename to implementations/swift/Sources/AgentSessionKit/Antigravity/AntigravityGenMetadataReader.swift diff --git a/Sources/AgentSessionKit/Harness/Harness.swift b/implementations/swift/Sources/AgentSessionKit/Harness/Harness.swift similarity index 100% rename from Sources/AgentSessionKit/Harness/Harness.swift rename to implementations/swift/Sources/AgentSessionKit/Harness/Harness.swift diff --git a/Sources/AgentSessionKit/MCP/MCPArguments.swift b/implementations/swift/Sources/AgentSessionKit/MCP/MCPArguments.swift similarity index 100% rename from Sources/AgentSessionKit/MCP/MCPArguments.swift rename to implementations/swift/Sources/AgentSessionKit/MCP/MCPArguments.swift diff --git a/Sources/AgentSessionKit/MCP/MCPDescriptors.swift b/implementations/swift/Sources/AgentSessionKit/MCP/MCPDescriptors.swift similarity index 100% rename from Sources/AgentSessionKit/MCP/MCPDescriptors.swift rename to implementations/swift/Sources/AgentSessionKit/MCP/MCPDescriptors.swift diff --git a/Sources/AgentSessionKit/MCP/MCPJSONRPC.swift b/implementations/swift/Sources/AgentSessionKit/MCP/MCPJSONRPC.swift similarity index 100% rename from Sources/AgentSessionKit/MCP/MCPJSONRPC.swift rename to implementations/swift/Sources/AgentSessionKit/MCP/MCPJSONRPC.swift diff --git a/Sources/AgentSessionKit/MCP/MCPSocketServer.swift b/implementations/swift/Sources/AgentSessionKit/MCP/MCPSocketServer.swift similarity index 100% rename from Sources/AgentSessionKit/MCP/MCPSocketServer.swift rename to implementations/swift/Sources/AgentSessionKit/MCP/MCPSocketServer.swift diff --git a/Sources/AgentSessionKit/MCP/MCPStdioBridge.swift b/implementations/swift/Sources/AgentSessionKit/MCP/MCPStdioBridge.swift similarity index 100% rename from Sources/AgentSessionKit/MCP/MCPStdioBridge.swift rename to implementations/swift/Sources/AgentSessionKit/MCP/MCPStdioBridge.swift diff --git a/Sources/AgentSessionKit/Sessions/AntigravityConversationIndex.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/AntigravityConversationIndex.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/AntigravityConversationIndex.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/AntigravityConversationIndex.swift diff --git a/Sources/AgentSessionKit/Sessions/AntigravitySessionAdapter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/AntigravitySessionAdapter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/AntigravitySessionAdapter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/AntigravitySessionAdapter.swift diff --git a/Sources/AgentSessionKit/Sessions/ClaudeCoworkSessionAdapter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/ClaudeCoworkSessionAdapter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/ClaudeCoworkSessionAdapter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/ClaudeCoworkSessionAdapter.swift diff --git a/Sources/AgentSessionKit/Sessions/ClaudeSessionAdapter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/ClaudeSessionAdapter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/ClaudeSessionAdapter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/ClaudeSessionAdapter.swift diff --git a/Sources/AgentSessionKit/Sessions/CodexSessionAdapter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/CodexSessionAdapter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/CodexSessionAdapter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/CodexSessionAdapter.swift diff --git a/Sources/AgentSessionKit/Sessions/CodexTitleHydrator.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/CodexTitleHydrator.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/CodexTitleHydrator.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/CodexTitleHydrator.swift diff --git a/Sources/AgentSessionKit/Sessions/CursorSessionAdapter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/CursorSessionAdapter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/CursorSessionAdapter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/CursorSessionAdapter.swift diff --git a/Sources/AgentSessionKit/Sessions/GeminiSessionAdapter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/GeminiSessionAdapter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/GeminiSessionAdapter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/GeminiSessionAdapter.swift diff --git a/Sources/AgentSessionKit/Sessions/GrokBotSessionAdapter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/GrokBotSessionAdapter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/GrokBotSessionAdapter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/GrokBotSessionAdapter.swift diff --git a/Sources/AgentSessionKit/Sessions/GrokSessionAdapter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/GrokSessionAdapter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/GrokSessionAdapter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/GrokSessionAdapter.swift diff --git a/Sources/AgentSessionKit/Sessions/LiveSQLiteReader.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/LiveSQLiteReader.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/LiveSQLiteReader.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/LiveSQLiteReader.swift diff --git a/Sources/AgentSessionKit/Sessions/ProtobufWireReader.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/ProtobufWireReader.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/ProtobufWireReader.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/ProtobufWireReader.swift diff --git a/Sources/AgentSessionKit/Sessions/SessionDeleter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/SessionDeleter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/SessionDeleter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/SessionDeleter.swift diff --git a/Sources/AgentSessionKit/Sessions/SessionIndexService.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/SessionIndexService.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/SessionIndexService.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/SessionIndexService.swift diff --git a/Sources/AgentSessionKit/Sessions/SessionIndexStore.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/SessionIndexStore.swift similarity index 99% rename from Sources/AgentSessionKit/Sessions/SessionIndexStore.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/SessionIndexStore.swift index f297f2a..739c280 100644 --- a/Sources/AgentSessionKit/Sessions/SessionIndexStore.swift +++ b/implementations/swift/Sources/AgentSessionKit/Sessions/SessionIndexStore.swift @@ -160,6 +160,10 @@ public actor SessionIndexStore { DROP TABLE IF EXISTS session_index_meta; """ + /// Exposed for `SessionIndexContractTests`, which compares the objects + /// created here against `contracts/storage/session-index-v5.sql`. + static var schemaSQLForContractTests: String { schemaSQL } + private static let schemaSQL = """ CREATE TABLE IF NOT EXISTS session_index_meta ( key TEXT PRIMARY KEY, diff --git a/Sources/AgentSessionKit/Sessions/SessionModels.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/SessionModels.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/SessionModels.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/SessionModels.swift diff --git a/Sources/AgentSessionKit/Sessions/SessionParsing.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/SessionParsing.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/SessionParsing.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/SessionParsing.swift diff --git a/Sources/AgentSessionKit/Sessions/SessionProviderAdapter.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/SessionProviderAdapter.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/SessionProviderAdapter.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/SessionProviderAdapter.swift diff --git a/Sources/AgentSessionKit/Sessions/SessionResumeCommandBuilder.swift b/implementations/swift/Sources/AgentSessionKit/Sessions/SessionResumeCommandBuilder.swift similarity index 100% rename from Sources/AgentSessionKit/Sessions/SessionResumeCommandBuilder.swift rename to implementations/swift/Sources/AgentSessionKit/Sessions/SessionResumeCommandBuilder.swift diff --git a/Sources/AgentSessionKit/Utilities/Base32.swift b/implementations/swift/Sources/AgentSessionKit/Utilities/Base32.swift similarity index 100% rename from Sources/AgentSessionKit/Utilities/Base32.swift rename to implementations/swift/Sources/AgentSessionKit/Utilities/Base32.swift diff --git a/Sources/AgentSessionKit/Utilities/ClaudeCoworkPaths.swift b/implementations/swift/Sources/AgentSessionKit/Utilities/ClaudeCoworkPaths.swift similarity index 100% rename from Sources/AgentSessionKit/Utilities/ClaudeCoworkPaths.swift rename to implementations/swift/Sources/AgentSessionKit/Utilities/ClaudeCoworkPaths.swift diff --git a/Sources/AgentSessionKit/Utilities/CodexOriginator.swift b/implementations/swift/Sources/AgentSessionKit/Utilities/CodexOriginator.swift similarity index 100% rename from Sources/AgentSessionKit/Utilities/CodexOriginator.swift rename to implementations/swift/Sources/AgentSessionKit/Utilities/CodexOriginator.swift diff --git a/Sources/AgentSessionKit/Utilities/HumanPromptText.swift b/implementations/swift/Sources/AgentSessionKit/Utilities/HumanPromptText.swift similarity index 100% rename from Sources/AgentSessionKit/Utilities/HumanPromptText.swift rename to implementations/swift/Sources/AgentSessionKit/Utilities/HumanPromptText.swift diff --git a/Sources/AgentSessionKit/Utilities/JSONLHeadTail.swift b/implementations/swift/Sources/AgentSessionKit/Utilities/JSONLHeadTail.swift similarity index 100% rename from Sources/AgentSessionKit/Utilities/JSONLHeadTail.swift rename to implementations/swift/Sources/AgentSessionKit/Utilities/JSONLHeadTail.swift diff --git a/Sources/AgentSessionKit/Utilities/JSONLLineScanner.swift b/implementations/swift/Sources/AgentSessionKit/Utilities/JSONLLineScanner.swift similarity index 100% rename from Sources/AgentSessionKit/Utilities/JSONLLineScanner.swift rename to implementations/swift/Sources/AgentSessionKit/Utilities/JSONLLineScanner.swift diff --git a/Sources/AgentSessionKit/Utilities/KitLog.swift b/implementations/swift/Sources/AgentSessionKit/Utilities/KitLog.swift similarity index 100% rename from Sources/AgentSessionKit/Utilities/KitLog.swift rename to implementations/swift/Sources/AgentSessionKit/Utilities/KitLog.swift diff --git a/Sources/AgentSessionKit/Utilities/PrivacyPreservingHash.swift b/implementations/swift/Sources/AgentSessionKit/Utilities/PrivacyPreservingHash.swift similarity index 100% rename from Sources/AgentSessionKit/Utilities/PrivacyPreservingHash.swift rename to implementations/swift/Sources/AgentSessionKit/Utilities/PrivacyPreservingHash.swift diff --git a/Sources/AgentSessionKit/Utilities/RealHomeDirectory.swift b/implementations/swift/Sources/AgentSessionKit/Utilities/RealHomeDirectory.swift similarity index 100% rename from Sources/AgentSessionKit/Utilities/RealHomeDirectory.swift rename to implementations/swift/Sources/AgentSessionKit/Utilities/RealHomeDirectory.swift diff --git a/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationReader.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationReader.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationReader.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationReader.swift diff --git a/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationRegistry.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationRegistry.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationRegistry.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationRegistry.swift diff --git a/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationTailer.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationTailer.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationTailer.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityConversationTailer.swift diff --git a/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityLiveAdapter.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityLiveAdapter.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Antigravity/AntigravityLiveAdapter.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityLiveAdapter.swift diff --git a/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepMapper.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepMapper.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepMapper.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepMapper.swift diff --git a/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepPayload.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepPayload.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepPayload.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepPayload.swift diff --git a/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepStatus.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepStatus.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepStatus.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepStatus.swift diff --git a/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepType.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepType.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepType.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityStepType.swift diff --git a/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityWorkspaceIndex.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityWorkspaceIndex.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Antigravity/AntigravityWorkspaceIndex.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Antigravity/AntigravityWorkspaceIndex.swift diff --git a/Sources/AgentSessionLive/Adapters/Claude/ClaudeCoworkLiveAdapter.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeCoworkLiveAdapter.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Claude/ClaudeCoworkLiveAdapter.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeCoworkLiveAdapter.swift diff --git a/Sources/AgentSessionLive/Adapters/Claude/ClaudeLiveAdapter.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeLiveAdapter.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Claude/ClaudeLiveAdapter.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeLiveAdapter.swift diff --git a/Sources/AgentSessionLive/Adapters/Claude/ClaudeProjectPath.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeProjectPath.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Claude/ClaudeProjectPath.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeProjectPath.swift diff --git a/Sources/AgentSessionLive/Adapters/Claude/ClaudeRecordMapper.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeRecordMapper.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Claude/ClaudeRecordMapper.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeRecordMapper.swift diff --git a/Sources/AgentSessionLive/Adapters/Claude/ClaudeSessionsDirectory.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeSessionsDirectory.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Claude/ClaudeSessionsDirectory.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeSessionsDirectory.swift diff --git a/Sources/AgentSessionLive/Adapters/Claude/ClaudeSourceBuilder.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeSourceBuilder.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Claude/ClaudeSourceBuilder.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeSourceBuilder.swift diff --git a/Sources/AgentSessionLive/Adapters/Claude/ClaudeSubagentLinker.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeSubagentLinker.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Claude/ClaudeSubagentLinker.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeSubagentLinker.swift diff --git a/Sources/AgentSessionLive/Adapters/Claude/ClaudeTranscriptRecord.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeTranscriptRecord.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Claude/ClaudeTranscriptRecord.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Claude/ClaudeTranscriptRecord.swift diff --git a/Sources/AgentSessionLive/Adapters/Codex/CodexLiveAdapter.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexLiveAdapter.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Codex/CodexLiveAdapter.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexLiveAdapter.swift diff --git a/Sources/AgentSessionLive/Adapters/Codex/CodexRecordMapper.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexRecordMapper.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Codex/CodexRecordMapper.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexRecordMapper.swift diff --git a/Sources/AgentSessionLive/Adapters/Codex/CodexRolloutRecord.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexRolloutRecord.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Codex/CodexRolloutRecord.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexRolloutRecord.swift diff --git a/Sources/AgentSessionLive/Adapters/Codex/CodexSubagentLinker.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexSubagentLinker.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Codex/CodexSubagentLinker.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexSubagentLinker.swift diff --git a/Sources/AgentSessionLive/Adapters/Codex/CodexToolCall.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexToolCall.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Codex/CodexToolCall.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Codex/CodexToolCall.swift diff --git a/Sources/AgentSessionLive/Adapters/Cursor/CursorLiveAdapter.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorLiveAdapter.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Cursor/CursorLiveAdapter.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorLiveAdapter.swift diff --git a/Sources/AgentSessionLive/Adapters/Cursor/CursorMessage.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorMessage.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Cursor/CursorMessage.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorMessage.swift diff --git a/Sources/AgentSessionLive/Adapters/Cursor/CursorMessageMapper.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorMessageMapper.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Cursor/CursorMessageMapper.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorMessageMapper.swift diff --git a/Sources/AgentSessionLive/Adapters/Cursor/CursorPaths.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorPaths.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Cursor/CursorPaths.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorPaths.swift diff --git a/Sources/AgentSessionLive/Adapters/Cursor/CursorSessionTailer.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorSessionTailer.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Cursor/CursorSessionTailer.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorSessionTailer.swift diff --git a/Sources/AgentSessionLive/Adapters/Cursor/CursorStoreMeta.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorStoreMeta.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Cursor/CursorStoreMeta.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorStoreMeta.swift diff --git a/Sources/AgentSessionLive/Adapters/Cursor/CursorStoreReader.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorStoreReader.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Cursor/CursorStoreReader.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorStoreReader.swift diff --git a/Sources/AgentSessionLive/Adapters/Cursor/CursorThinTranscriptMapper.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorThinTranscriptMapper.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Cursor/CursorThinTranscriptMapper.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorThinTranscriptMapper.swift diff --git a/Sources/AgentSessionLive/Adapters/Cursor/CursorWorkerProbe.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorWorkerProbe.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Cursor/CursorWorkerProbe.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Cursor/CursorWorkerProbe.swift diff --git a/Sources/AgentSessionLive/Adapters/Grok/GrokLiveAdapter.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokLiveAdapter.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Grok/GrokLiveAdapter.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokLiveAdapter.swift diff --git a/Sources/AgentSessionLive/Adapters/Grok/GrokRecordMapper.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokRecordMapper.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Grok/GrokRecordMapper.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokRecordMapper.swift diff --git a/Sources/AgentSessionLive/Adapters/Grok/GrokRecords.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokRecords.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Grok/GrokRecords.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokRecords.swift diff --git a/Sources/AgentSessionLive/Adapters/Grok/GrokSessionsPath.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokSessionsPath.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Grok/GrokSessionsPath.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokSessionsPath.swift diff --git a/Sources/AgentSessionLive/Adapters/Grok/GrokSignalsReader.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokSignalsReader.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Grok/GrokSignalsReader.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokSignalsReader.swift diff --git a/Sources/AgentSessionLive/Adapters/Grok/GrokToolCall.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokToolCall.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/Grok/GrokToolCall.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/Grok/GrokToolCall.swift diff --git a/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotEntry.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotEntry.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/GrokBot/GrokBotEntry.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotEntry.swift diff --git a/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotEntryMapper.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotEntryMapper.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/GrokBot/GrokBotEntryMapper.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotEntryMapper.swift diff --git a/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotLiveAdapter.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotLiveAdapter.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/GrokBot/GrokBotLiveAdapter.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotLiveAdapter.swift diff --git a/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotStore.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotStore.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/GrokBot/GrokBotStore.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotStore.swift diff --git a/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotTranscriptTailer.swift b/implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotTranscriptTailer.swift similarity index 100% rename from Sources/AgentSessionLive/Adapters/GrokBot/GrokBotTranscriptTailer.swift rename to implementations/swift/Sources/AgentSessionLive/Adapters/GrokBot/GrokBotTranscriptTailer.swift diff --git a/Sources/AgentSessionLive/AgentSessionLive.swift b/implementations/swift/Sources/AgentSessionLive/AgentSessionLive.swift similarity index 100% rename from Sources/AgentSessionLive/AgentSessionLive.swift rename to implementations/swift/Sources/AgentSessionLive/AgentSessionLive.swift diff --git a/Sources/AgentSessionLive/Events/AgentEvent.swift b/implementations/swift/Sources/AgentSessionLive/Events/AgentEvent.swift similarity index 100% rename from Sources/AgentSessionLive/Events/AgentEvent.swift rename to implementations/swift/Sources/AgentSessionLive/Events/AgentEvent.swift diff --git a/Sources/AgentSessionLive/Events/ContextUsage.swift b/implementations/swift/Sources/AgentSessionLive/Events/ContextUsage.swift similarity index 100% rename from Sources/AgentSessionLive/Events/ContextUsage.swift rename to implementations/swift/Sources/AgentSessionLive/Events/ContextUsage.swift diff --git a/Sources/AgentSessionLive/Events/SessionBrief.swift b/implementations/swift/Sources/AgentSessionLive/Events/SessionBrief.swift similarity index 100% rename from Sources/AgentSessionLive/Events/SessionBrief.swift rename to implementations/swift/Sources/AgentSessionLive/Events/SessionBrief.swift diff --git a/Sources/AgentSessionLive/Events/SessionIdentity.swift b/implementations/swift/Sources/AgentSessionLive/Events/SessionIdentity.swift similarity index 100% rename from Sources/AgentSessionLive/Events/SessionIdentity.swift rename to implementations/swift/Sources/AgentSessionLive/Events/SessionIdentity.swift diff --git a/Sources/AgentSessionLive/Events/SessionKey.swift b/implementations/swift/Sources/AgentSessionLive/Events/SessionKey.swift similarity index 100% rename from Sources/AgentSessionLive/Events/SessionKey.swift rename to implementations/swift/Sources/AgentSessionLive/Events/SessionKey.swift diff --git a/Sources/AgentSessionLive/Events/SessionResume.swift b/implementations/swift/Sources/AgentSessionLive/Events/SessionResume.swift similarity index 100% rename from Sources/AgentSessionLive/Events/SessionResume.swift rename to implementations/swift/Sources/AgentSessionLive/Events/SessionResume.swift diff --git a/Sources/AgentSessionLive/Events/SessionSnapshot.swift b/implementations/swift/Sources/AgentSessionLive/Events/SessionSnapshot.swift similarity index 100% rename from Sources/AgentSessionLive/Events/SessionSnapshot.swift rename to implementations/swift/Sources/AgentSessionLive/Events/SessionSnapshot.swift diff --git a/Sources/AgentSessionLive/Events/SessionState.swift b/implementations/swift/Sources/AgentSessionLive/Events/SessionState.swift similarity index 100% rename from Sources/AgentSessionLive/Events/SessionState.swift rename to implementations/swift/Sources/AgentSessionLive/Events/SessionState.swift diff --git a/Sources/AgentSessionLive/Events/SessionStateReducer.swift b/implementations/swift/Sources/AgentSessionLive/Events/SessionStateReducer.swift similarity index 100% rename from Sources/AgentSessionLive/Events/SessionStateReducer.swift rename to implementations/swift/Sources/AgentSessionLive/Events/SessionStateReducer.swift diff --git a/Sources/AgentSessionLive/Liveness/LivenessResolver.swift b/implementations/swift/Sources/AgentSessionLive/Liveness/LivenessResolver.swift similarity index 100% rename from Sources/AgentSessionLive/Liveness/LivenessResolver.swift rename to implementations/swift/Sources/AgentSessionLive/Liveness/LivenessResolver.swift diff --git a/Sources/AgentSessionLive/Liveness/LockFileProbe.swift b/implementations/swift/Sources/AgentSessionLive/Liveness/LockFileProbe.swift similarity index 100% rename from Sources/AgentSessionLive/Liveness/LockFileProbe.swift rename to implementations/swift/Sources/AgentSessionLive/Liveness/LockFileProbe.swift diff --git a/Sources/AgentSessionLive/Liveness/ProcessLinker.swift b/implementations/swift/Sources/AgentSessionLive/Liveness/ProcessLinker.swift similarity index 100% rename from Sources/AgentSessionLive/Liveness/ProcessLinker.swift rename to implementations/swift/Sources/AgentSessionLive/Liveness/ProcessLinker.swift diff --git a/Sources/AgentSessionLive/Liveness/ProcessTable.swift b/implementations/swift/Sources/AgentSessionLive/Liveness/ProcessTable.swift similarity index 100% rename from Sources/AgentSessionLive/Liveness/ProcessTable.swift rename to implementations/swift/Sources/AgentSessionLive/Liveness/ProcessTable.swift diff --git a/Sources/AgentSessionLive/Liveness/ProcessTableReading.swift b/implementations/swift/Sources/AgentSessionLive/Liveness/ProcessTableReading.swift similarity index 100% rename from Sources/AgentSessionLive/Liveness/ProcessTableReading.swift rename to implementations/swift/Sources/AgentSessionLive/Liveness/ProcessTableReading.swift diff --git a/Sources/AgentSessionLive/Liveness/RegistrySnapshot.swift b/implementations/swift/Sources/AgentSessionLive/Liveness/RegistrySnapshot.swift similarity index 100% rename from Sources/AgentSessionLive/Liveness/RegistrySnapshot.swift rename to implementations/swift/Sources/AgentSessionLive/Liveness/RegistrySnapshot.swift diff --git a/Sources/AgentSessionLive/Liveness/SessionEnvironmentVariables.swift b/implementations/swift/Sources/AgentSessionLive/Liveness/SessionEnvironmentVariables.swift similarity index 100% rename from Sources/AgentSessionLive/Liveness/SessionEnvironmentVariables.swift rename to implementations/swift/Sources/AgentSessionLive/Liveness/SessionEnvironmentVariables.swift diff --git a/Sources/AgentSessionLive/Sources/DiscoveryCache.swift b/implementations/swift/Sources/AgentSessionLive/Sources/DiscoveryCache.swift similarity index 100% rename from Sources/AgentSessionLive/Sources/DiscoveryCache.swift rename to implementations/swift/Sources/AgentSessionLive/Sources/DiscoveryCache.swift diff --git a/Sources/AgentSessionLive/Sources/DiscoveryIO.swift b/implementations/swift/Sources/AgentSessionLive/Sources/DiscoveryIO.swift similarity index 100% rename from Sources/AgentSessionLive/Sources/DiscoveryIO.swift rename to implementations/swift/Sources/AgentSessionLive/Sources/DiscoveryIO.swift diff --git a/Sources/AgentSessionLive/Sources/LivenessHint.swift b/implementations/swift/Sources/AgentSessionLive/Sources/LivenessHint.swift similarity index 100% rename from Sources/AgentSessionLive/Sources/LivenessHint.swift rename to implementations/swift/Sources/AgentSessionLive/Sources/LivenessHint.swift diff --git a/Sources/AgentSessionLive/Sources/SessionSource.swift b/implementations/swift/Sources/AgentSessionLive/Sources/SessionSource.swift similarity index 100% rename from Sources/AgentSessionLive/Sources/SessionSource.swift rename to implementations/swift/Sources/AgentSessionLive/Sources/SessionSource.swift diff --git a/Sources/AgentSessionLive/Sources/SessionTailer.swift b/implementations/swift/Sources/AgentSessionLive/Sources/SessionTailer.swift similarity index 100% rename from Sources/AgentSessionLive/Sources/SessionTailer.swift rename to implementations/swift/Sources/AgentSessionLive/Sources/SessionTailer.swift diff --git a/Sources/AgentSessionLive/Sources/SourceAdapter.swift b/implementations/swift/Sources/AgentSessionLive/Sources/SourceAdapter.swift similarity index 100% rename from Sources/AgentSessionLive/Sources/SourceAdapter.swift rename to implementations/swift/Sources/AgentSessionLive/Sources/SourceAdapter.swift diff --git a/Sources/AgentSessionLive/Sources/SourceCursor.swift b/implementations/swift/Sources/AgentSessionLive/Sources/SourceCursor.swift similarity index 100% rename from Sources/AgentSessionLive/Sources/SourceCursor.swift rename to implementations/swift/Sources/AgentSessionLive/Sources/SourceCursor.swift diff --git a/Sources/AgentSessionLive/Tailing/FSEventsWatcher.swift b/implementations/swift/Sources/AgentSessionLive/Tailing/FSEventsWatcher.swift similarity index 100% rename from Sources/AgentSessionLive/Tailing/FSEventsWatcher.swift rename to implementations/swift/Sources/AgentSessionLive/Tailing/FSEventsWatcher.swift diff --git a/Sources/AgentSessionLive/Tailing/FileStamp.swift b/implementations/swift/Sources/AgentSessionLive/Tailing/FileStamp.swift similarity index 100% rename from Sources/AgentSessionLive/Tailing/FileStamp.swift rename to implementations/swift/Sources/AgentSessionLive/Tailing/FileStamp.swift diff --git a/Sources/AgentSessionLive/Tailing/IngestCoordinator.swift b/implementations/swift/Sources/AgentSessionLive/Tailing/IngestCoordinator.swift similarity index 100% rename from Sources/AgentSessionLive/Tailing/IngestCoordinator.swift rename to implementations/swift/Sources/AgentSessionLive/Tailing/IngestCoordinator.swift diff --git a/Sources/AgentSessionLive/Tailing/JSONLTailer.swift b/implementations/swift/Sources/AgentSessionLive/Tailing/JSONLTailer.swift similarity index 100% rename from Sources/AgentSessionLive/Tailing/JSONLTailer.swift rename to implementations/swift/Sources/AgentSessionLive/Tailing/JSONLTailer.swift diff --git a/Sources/AgentSessionLive/Tailing/SQLiteChangeWatcher.swift b/implementations/swift/Sources/AgentSessionLive/Tailing/SQLiteChangeWatcher.swift similarity index 100% rename from Sources/AgentSessionLive/Tailing/SQLiteChangeWatcher.swift rename to implementations/swift/Sources/AgentSessionLive/Tailing/SQLiteChangeWatcher.swift diff --git a/Sources/AgentSessionLive/Tailing/SourceCursorStore.swift b/implementations/swift/Sources/AgentSessionLive/Tailing/SourceCursorStore.swift similarity index 100% rename from Sources/AgentSessionLive/Tailing/SourceCursorStore.swift rename to implementations/swift/Sources/AgentSessionLive/Tailing/SourceCursorStore.swift diff --git a/Sources/AgentSessionLive/Tailing/TailerBackpressure.swift b/implementations/swift/Sources/AgentSessionLive/Tailing/TailerBackpressure.swift similarity index 100% rename from Sources/AgentSessionLive/Tailing/TailerBackpressure.swift rename to implementations/swift/Sources/AgentSessionLive/Tailing/TailerBackpressure.swift diff --git a/Sources/AgentSessionLive/Utilities/ArgvSanitizer.swift b/implementations/swift/Sources/AgentSessionLive/Utilities/ArgvSanitizer.swift similarity index 100% rename from Sources/AgentSessionLive/Utilities/ArgvSanitizer.swift rename to implementations/swift/Sources/AgentSessionLive/Utilities/ArgvSanitizer.swift diff --git a/Sources/AgentSessionLive/Utilities/EventText.swift b/implementations/swift/Sources/AgentSessionLive/Utilities/EventText.swift similarity index 100% rename from Sources/AgentSessionLive/Utilities/EventText.swift rename to implementations/swift/Sources/AgentSessionLive/Utilities/EventText.swift diff --git a/Tests/AgentSessionKitTests/AgentSessionKitInfoTests.swift b/implementations/swift/Tests/AgentSessionKitTests/AgentSessionKitInfoTests.swift similarity index 96% rename from Tests/AgentSessionKitTests/AgentSessionKitInfoTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/AgentSessionKitInfoTests.swift index 31f5c5e..339a422 100644 --- a/Tests/AgentSessionKitTests/AgentSessionKitInfoTests.swift +++ b/implementations/swift/Tests/AgentSessionKitTests/AgentSessionKitInfoTests.swift @@ -12,11 +12,14 @@ final class AgentSessionKitInfoTests: XCTestCase { /// The repository root, resolved from this file rather than from the /// working directory: `swift test` can be invoked from anywhere, and /// the package deliberately ships no test resource for this. - /// `Tests/AgentSessionKitTests/` → three levels up. + /// `implementations/swift/Tests/AgentSessionKitTests/` → + /// five levels up. private var repositoryRoot: URL { URL(fileURLWithPath: #filePath) .deletingLastPathComponent() // AgentSessionKitTests .deletingLastPathComponent() // Tests + .deletingLastPathComponent() // swift + .deletingLastPathComponent() // implementations .deletingLastPathComponent() // repository root } diff --git a/Tests/AgentSessionKitTests/AntigravityElapsedDateTests.swift b/implementations/swift/Tests/AgentSessionKitTests/AntigravityElapsedDateTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/AntigravityElapsedDateTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/AntigravityElapsedDateTests.swift diff --git a/Tests/AgentSessionKitTests/AntigravitySessionAdapterTests.swift b/implementations/swift/Tests/AgentSessionKitTests/AntigravitySessionAdapterTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/AntigravitySessionAdapterTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/AntigravitySessionAdapterTests.swift diff --git a/Tests/AgentSessionKitTests/Base32Tests.swift b/implementations/swift/Tests/AgentSessionKitTests/Base32Tests.swift similarity index 100% rename from Tests/AgentSessionKitTests/Base32Tests.swift rename to implementations/swift/Tests/AgentSessionKitTests/Base32Tests.swift diff --git a/Tests/AgentSessionKitTests/ClaudeCoworkSessionAdapterTests.swift b/implementations/swift/Tests/AgentSessionKitTests/ClaudeCoworkSessionAdapterTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/ClaudeCoworkSessionAdapterTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/ClaudeCoworkSessionAdapterTests.swift diff --git a/Tests/AgentSessionKitTests/ClaudeSessionAdapterTests.swift b/implementations/swift/Tests/AgentSessionKitTests/ClaudeSessionAdapterTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/ClaudeSessionAdapterTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/ClaudeSessionAdapterTests.swift diff --git a/Tests/AgentSessionKitTests/CodexSessionAdapterTests.swift b/implementations/swift/Tests/AgentSessionKitTests/CodexSessionAdapterTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/CodexSessionAdapterTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/CodexSessionAdapterTests.swift diff --git a/Tests/AgentSessionKitTests/CodexTitleHydratorTests.swift b/implementations/swift/Tests/AgentSessionKitTests/CodexTitleHydratorTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/CodexTitleHydratorTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/CodexTitleHydratorTests.swift diff --git a/Tests/AgentSessionKitTests/CursorSessionAdapterTests.swift b/implementations/swift/Tests/AgentSessionKitTests/CursorSessionAdapterTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/CursorSessionAdapterTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/CursorSessionAdapterTests.swift diff --git a/Tests/AgentSessionKitTests/GeminiSessionAdapterTests.swift b/implementations/swift/Tests/AgentSessionKitTests/GeminiSessionAdapterTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/GeminiSessionAdapterTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/GeminiSessionAdapterTests.swift diff --git a/Tests/AgentSessionKitTests/GrokBotSessionAdapterTests.swift b/implementations/swift/Tests/AgentSessionKitTests/GrokBotSessionAdapterTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/GrokBotSessionAdapterTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/GrokBotSessionAdapterTests.swift diff --git a/Tests/AgentSessionKitTests/GrokSessionAdapterTests.swift b/implementations/swift/Tests/AgentSessionKitTests/GrokSessionAdapterTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/GrokSessionAdapterTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/GrokSessionAdapterTests.swift diff --git a/Tests/AgentSessionKitTests/HarnessNamingTests.swift b/implementations/swift/Tests/AgentSessionKitTests/HarnessNamingTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/HarnessNamingTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/HarnessNamingTests.swift diff --git a/Tests/AgentSessionKitTests/HumanPromptTextTests.swift b/implementations/swift/Tests/AgentSessionKitTests/HumanPromptTextTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/HumanPromptTextTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/HumanPromptTextTests.swift diff --git a/Tests/AgentSessionKitTests/JSONLHeadTailTests.swift b/implementations/swift/Tests/AgentSessionKitTests/JSONLHeadTailTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/JSONLHeadTailTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/JSONLHeadTailTests.swift diff --git a/Tests/AgentSessionKitTests/MCPJSONRPCTests.swift b/implementations/swift/Tests/AgentSessionKitTests/MCPJSONRPCTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/MCPJSONRPCTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/MCPJSONRPCTests.swift diff --git a/Tests/AgentSessionKitTests/MCPSocketServerTests.swift b/implementations/swift/Tests/AgentSessionKitTests/MCPSocketServerTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/MCPSocketServerTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/MCPSocketServerTests.swift diff --git a/Tests/AgentSessionKitTests/MCPStdioBridgeTests.swift b/implementations/swift/Tests/AgentSessionKitTests/MCPStdioBridgeTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/MCPStdioBridgeTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/MCPStdioBridgeTests.swift diff --git a/Tests/AgentSessionKitTests/PrivacyPreservingHashTests.swift b/implementations/swift/Tests/AgentSessionKitTests/PrivacyPreservingHashTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/PrivacyPreservingHashTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/PrivacyPreservingHashTests.swift diff --git a/Tests/AgentSessionKitTests/ProtobufWireReaderTests.swift b/implementations/swift/Tests/AgentSessionKitTests/ProtobufWireReaderTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/ProtobufWireReaderTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/ProtobufWireReaderTests.swift diff --git a/Tests/AgentSessionKitTests/SessionDeleterTests.swift b/implementations/swift/Tests/AgentSessionKitTests/SessionDeleterTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/SessionDeleterTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/SessionDeleterTests.swift diff --git a/implementations/swift/Tests/AgentSessionKitTests/SessionIndexContractTests.swift b/implementations/swift/Tests/AgentSessionKitTests/SessionIndexContractTests.swift new file mode 100644 index 0000000..fe77108 --- /dev/null +++ b/implementations/swift/Tests/AgentSessionKitTests/SessionIndexContractTests.swift @@ -0,0 +1,85 @@ +import XCTest +@testable import AgentSessionKit + +/// The session index schema is shared with the Rust lane through +/// `contracts/storage/session-index-v5.sql`. Nothing generates one lane from +/// the other, so without a check the two drift silently: a column added in +/// Swift would simply be invisible to a Rust reader that still believes it +/// understands version 5. +/// +/// The Rust lane has the mirror of this test — it builds a database straight +/// from the contract file and opens it with its reader. +final class SessionIndexContractTests: XCTestCase { + /// `implementations/swift/Tests/AgentSessionKitTests/` → + /// five levels up. + private var repositoryRoot: URL { + URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + } + + private func contractSQL() throws -> String { + try String( + contentsOf: repositoryRoot + .appendingPathComponent("contracts/storage/session-index-v5.sql"), + encoding: .utf8 + ) + } + + /// The version this lane stamps is the version the contract declares. + func testSchemaVersionMatchesTheContract() throws { + let sql = try contractSQL() + let declared = sql + .split(separator: "\n") + .compactMap { line -> Int? in + let trimmed = line.trimmingCharacters(in: .whitespaces) + guard trimmed.hasPrefix("PRAGMA user_version = ") else { return nil } + return Int( + trimmed + .dropFirst("PRAGMA user_version = ".count) + .trimmingCharacters(in: CharacterSet(charactersIn: "; ")) + ) + } + .first + XCTAssertEqual(declared, SessionIndexStore.schemaVersion) + } + + /// Every object this lane creates is named in the contract, and every + /// object the contract names is created by this lane. Compared as sorted + /// object names rather than raw text so that formatting differences do + /// not fail the build, but a real addition or removal does. + func testCreatedObjectsMatchTheContract() throws { + let contractObjects = Self.createdObjectNames(in: try contractSQL()) + let swiftObjects = Self.createdObjectNames(in: SessionIndexStore.schemaSQLForContractTests) + XCTAssertFalse(contractObjects.isEmpty, "contract declares no objects") + XCTAssertEqual( + swiftObjects, + contractObjects, + "session index schema drifted from contracts/storage/session-index-v5.sql" + ) + } + + /// `CREATE [UNIQUE] INDEX|TABLE|VIRTUAL TABLE|TRIGGER [IF NOT EXISTS] ` + /// reduced to the bare object name. + private static func createdObjectNames(in sql: String) -> [String] { + var names: [String] = [] + for line in sql.split(separator: "\n") { + var tokens = line.trimmingCharacters(in: .whitespaces) + .split(separator: " ", omittingEmptySubsequences: true) + .map(String.init) + guard tokens.first?.uppercased() == "CREATE" else { continue } + tokens.removeFirst() + while let next = tokens.first?.uppercased(), + ["UNIQUE", "VIRTUAL", "TABLE", "INDEX", "TRIGGER", "IF", "NOT", "EXISTS"] + .contains(next) { + tokens.removeFirst() + } + guard let name = tokens.first else { continue } + names.append(name.split(separator: "(").first.map(String.init) ?? name) + } + return names.sorted() + } +} diff --git a/Tests/AgentSessionKitTests/SessionIndexServiceTests.swift b/implementations/swift/Tests/AgentSessionKitTests/SessionIndexServiceTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/SessionIndexServiceTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/SessionIndexServiceTests.swift diff --git a/Tests/AgentSessionKitTests/SessionIndexStoreTests.swift b/implementations/swift/Tests/AgentSessionKitTests/SessionIndexStoreTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/SessionIndexStoreTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/SessionIndexStoreTests.swift diff --git a/Tests/AgentSessionKitTests/SessionResumeCommandBuilderTests.swift b/implementations/swift/Tests/AgentSessionKitTests/SessionResumeCommandBuilderTests.swift similarity index 100% rename from Tests/AgentSessionKitTests/SessionResumeCommandBuilderTests.swift rename to implementations/swift/Tests/AgentSessionKitTests/SessionResumeCommandBuilderTests.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/AntigravityFixtureSupport.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/AntigravityFixtureSupport.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/AntigravityFixtureSupport.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/AntigravityFixtureSupport.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/AntigravityLiveAdapterTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/AntigravityLiveAdapterTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/AntigravityLiveAdapterTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/AntigravityLiveAdapterTests.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/ClaudeCoworkLiveAdapterTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/ClaudeCoworkLiveAdapterTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/ClaudeCoworkLiveAdapterTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/ClaudeCoworkLiveAdapterTests.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/ClaudeFixtureSupport.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/ClaudeFixtureSupport.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/ClaudeFixtureSupport.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/ClaudeFixtureSupport.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/ClaudeLiveAdapterTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/ClaudeLiveAdapterTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/ClaudeLiveAdapterTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/ClaudeLiveAdapterTests.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/ClaudeRecordMapperTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/ClaudeRecordMapperTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/ClaudeRecordMapperTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/ClaudeRecordMapperTests.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/CodexLiveAdapterTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/CodexLiveAdapterTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/CodexLiveAdapterTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/CodexLiveAdapterTests.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/CursorFixtureSupport.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/CursorFixtureSupport.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/CursorFixtureSupport.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/CursorFixtureSupport.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/CursorLiveAdapterTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/CursorLiveAdapterTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/CursorLiveAdapterTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/CursorLiveAdapterTests.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/GrokBotFixtureSupport.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/GrokBotFixtureSupport.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/GrokBotFixtureSupport.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/GrokBotFixtureSupport.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/GrokBotLiveAdapterTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/GrokBotLiveAdapterTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/GrokBotLiveAdapterTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/GrokBotLiveAdapterTests.swift diff --git a/Tests/AgentSessionLiveTests/Adapters/GrokLiveAdapterTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/Adapters/GrokLiveAdapterTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/Adapters/GrokLiveAdapterTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/Adapters/GrokLiveAdapterTests.swift diff --git a/Tests/AgentSessionLiveTests/ArgvSanitizerTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/ArgvSanitizerTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/ArgvSanitizerTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/ArgvSanitizerTests.swift diff --git a/Tests/AgentSessionLiveTests/ContextUsageTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/ContextUsageTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/ContextUsageTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/ContextUsageTests.swift diff --git a/Tests/AgentSessionLiveTests/CursorPersistenceTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/CursorPersistenceTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/CursorPersistenceTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/CursorPersistenceTests.swift diff --git a/Tests/AgentSessionLiveTests/DiscoveryCostTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/DiscoveryCostTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/DiscoveryCostTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/DiscoveryCostTests.swift diff --git a/Tests/AgentSessionLiveTests/DiscoveryRoutingTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/DiscoveryRoutingTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/DiscoveryRoutingTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/DiscoveryRoutingTests.swift diff --git a/Tests/AgentSessionLiveTests/EventCodableTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/EventCodableTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/EventCodableTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/EventCodableTests.swift diff --git a/Tests/AgentSessionLiveTests/EventTextTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/EventTextTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/EventTextTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/EventTextTests.swift diff --git a/Tests/AgentSessionLiveTests/FSEventsWatcherTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/FSEventsWatcherTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/FSEventsWatcherTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/FSEventsWatcherTests.swift diff --git a/Tests/AgentSessionLiveTests/Fixtures/README.md b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/README.md similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/README.md rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/README.md diff --git a/Tests/AgentSessionLiveTests/Fixtures/antigravity/.gitkeep b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/antigravity/.gitkeep similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/antigravity/.gitkeep rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/antigravity/.gitkeep diff --git a/Tests/AgentSessionLiveTests/Fixtures/claude/.gitkeep b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/.gitkeep similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/claude/.gitkeep rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/.gitkeep diff --git a/Tests/AgentSessionLiveTests/Fixtures/claude/README.md b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/README.md similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/claude/README.md rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/README.md diff --git a/Tests/AgentSessionLiveTests/Fixtures/claude/session-pid.json b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/session-pid.json similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/claude/session-pid.json rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/session-pid.json diff --git a/Tests/AgentSessionLiveTests/Fixtures/claude/session.jsonl b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/session.jsonl similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/claude/session.jsonl rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/session.jsonl diff --git a/Tests/AgentSessionLiveTests/Fixtures/claude/subagent.jsonl b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/subagent.jsonl similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/claude/subagent.jsonl rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/subagent.jsonl diff --git a/Tests/AgentSessionLiveTests/Fixtures/claude/subagent.meta.json b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/subagent.meta.json similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/claude/subagent.meta.json rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/claude/subagent.meta.json diff --git a/Tests/AgentSessionLiveTests/Fixtures/codex/.gitkeep b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/codex/.gitkeep similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/codex/.gitkeep rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/codex/.gitkeep diff --git a/Tests/AgentSessionLiveTests/Fixtures/codex/cli-shapes.jsonl b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/codex/cli-shapes.jsonl similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/codex/cli-shapes.jsonl rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/codex/cli-shapes.jsonl diff --git a/Tests/AgentSessionLiveTests/Fixtures/codex/events-sampler.jsonl b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/codex/events-sampler.jsonl similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/codex/events-sampler.jsonl rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/codex/events-sampler.jsonl diff --git a/Tests/AgentSessionLiveTests/Fixtures/codex/rollout.jsonl b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/codex/rollout.jsonl similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/codex/rollout.jsonl rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/codex/rollout.jsonl diff --git a/Tests/AgentSessionLiveTests/Fixtures/cursor/.gitkeep b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/cursor/.gitkeep similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/cursor/.gitkeep rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/cursor/.gitkeep diff --git a/Tests/AgentSessionLiveTests/Fixtures/cursor/thin-transcript.jsonl b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/cursor/thin-transcript.jsonl similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/cursor/thin-transcript.jsonl rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/cursor/thin-transcript.jsonl diff --git a/Tests/AgentSessionLiveTests/Fixtures/grok/.gitkeep b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/.gitkeep similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/grok/.gitkeep rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/.gitkeep diff --git a/Tests/AgentSessionLiveTests/Fixtures/grok/README.md b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/README.md similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/grok/README.md rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/README.md diff --git a/Tests/AgentSessionLiveTests/Fixtures/grok/session/chat_history.jsonl b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/chat_history.jsonl similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/grok/session/chat_history.jsonl rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/chat_history.jsonl diff --git a/Tests/AgentSessionLiveTests/Fixtures/grok/session/events.jsonl b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/events.jsonl similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/grok/session/events.jsonl rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/events.jsonl diff --git a/Tests/AgentSessionLiveTests/Fixtures/grok/session/signals.json b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/signals.json similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/grok/session/signals.json rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/signals.json diff --git a/Tests/AgentSessionLiveTests/Fixtures/grok/session/summary.json b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/summary.json similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/grok/session/summary.json rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/summary.json diff --git a/Tests/AgentSessionLiveTests/Fixtures/grok/session/updates.jsonl b/implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/updates.jsonl similarity index 100% rename from Tests/AgentSessionLiveTests/Fixtures/grok/session/updates.jsonl rename to implementations/swift/Tests/AgentSessionLiveTests/Fixtures/grok/session/updates.jsonl diff --git a/Tests/AgentSessionLiveTests/IngestCoordinatorTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/IngestCoordinatorTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/IngestCoordinatorTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/IngestCoordinatorTests.swift diff --git a/Tests/AgentSessionLiveTests/JSONLTailerTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/JSONLTailerTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/JSONLTailerTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/JSONLTailerTests.swift diff --git a/Tests/AgentSessionLiveTests/LiveTestSupport.swift b/implementations/swift/Tests/AgentSessionLiveTests/LiveTestSupport.swift similarity index 100% rename from Tests/AgentSessionLiveTests/LiveTestSupport.swift rename to implementations/swift/Tests/AgentSessionLiveTests/LiveTestSupport.swift diff --git a/Tests/AgentSessionLiveTests/LivenessResolverTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/LivenessResolverTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/LivenessResolverTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/LivenessResolverTests.swift diff --git a/Tests/AgentSessionLiveTests/LockFileProbeTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/LockFileProbeTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/LockFileProbeTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/LockFileProbeTests.swift diff --git a/Tests/AgentSessionLiveTests/PipelineCostTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/PipelineCostTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/PipelineCostTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/PipelineCostTests.swift diff --git a/Tests/AgentSessionLiveTests/ProcessLinkerTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/ProcessLinkerTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/ProcessLinkerTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/ProcessLinkerTests.swift diff --git a/Tests/AgentSessionLiveTests/ProcessTableTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/ProcessTableTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/ProcessTableTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/ProcessTableTests.swift diff --git a/Tests/AgentSessionLiveTests/RegistrySnapshotTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/RegistrySnapshotTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/RegistrySnapshotTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/RegistrySnapshotTests.swift diff --git a/Tests/AgentSessionLiveTests/SessionBriefFixtureTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/SessionBriefFixtureTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/SessionBriefFixtureTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/SessionBriefFixtureTests.swift diff --git a/Tests/AgentSessionLiveTests/SessionBriefTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/SessionBriefTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/SessionBriefTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/SessionBriefTests.swift diff --git a/Tests/AgentSessionLiveTests/SessionKeyTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/SessionKeyTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/SessionKeyTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/SessionKeyTests.swift diff --git a/Tests/AgentSessionLiveTests/SessionResumeTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/SessionResumeTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/SessionResumeTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/SessionResumeTests.swift diff --git a/Tests/AgentSessionLiveTests/SessionStateReducerTests.swift b/implementations/swift/Tests/AgentSessionLiveTests/SessionStateReducerTests.swift similarity index 100% rename from Tests/AgentSessionLiveTests/SessionStateReducerTests.swift rename to implementations/swift/Tests/AgentSessionLiveTests/SessionStateReducerTests.swift diff --git a/Tests/AgentSessionLiveTests/TestSupport.swift b/implementations/swift/Tests/AgentSessionLiveTests/TestSupport.swift similarity index 100% rename from Tests/AgentSessionLiveTests/TestSupport.swift rename to implementations/swift/Tests/AgentSessionLiveTests/TestSupport.swift From 285098c280dd8b70837fd14fcfc3c1bb3f148b6c Mon Sep 17 00:00:00 2001 From: AstroQore <69107895+AstroQore@users.noreply.github.com> Date: Mon, 31 Aug 2026 15:45:26 +0800 Subject: [PATCH 2/2] Repoint the release workflow and compare whole schema statements Two findings from review, both real. release.yml read the version constant from Sources/AgentSessionKit/, which the move emptied. Tagging 0.7.0 would have failed at the version check -- the one job that must not break on the commit that renames things. RELEASING.md named the same stale path. The contract test compared only created object names, so a changed column type, constraint, index expression, FTS tokenizer, or trigger body passed while the two lanes silently disagreed. It now compares whole normalised CREATE statements; only whitespace and IF NOT EXISTS are normalised away, since those differ between a file run once and a writer that re-runs on every open. Verified by flipping the FTS tokenizer in the contract alone: the old comparison missed it, this one fails. Co-Authored-By: Claude Opus 5 --- .github/workflows/release.yml | 2 +- RELEASING.md | 2 +- .../SessionIndexContractTests.swift | 54 +++++++++---------- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8365687..fd5fcdf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,7 @@ jobs: echo "tag=${TAG}" >> "$GITHUB_OUTPUT" echo "Tag: ${TAG}" - INFO="Sources/AgentSessionKit/AgentSessionKitInfo.swift" + INFO="implementations/swift/Sources/AgentSessionKit/AgentSessionKitInfo.swift" if [ ! -f "$INFO" ]; then echo "::error::${INFO} is missing; the version constant is the release contract." exit 1 diff --git a/RELEASING.md b/RELEASING.md index 3dc4040..272cfee 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -26,7 +26,7 @@ Nothing here is `1.0` yet, so `exact:` is the pin a host should use. See ## The version constant -`Sources/AgentSessionKit/AgentSessionKitInfo.swift` holds +`implementations/swift/Sources/AgentSessionKit/AgentSessionKitInfo.swift` holds `AgentSessionKitInfo.version`. This package is linked *statically* by its hosts: once it is compiled in there is no bundle, no `Info.plist`, and no dylib for anyone to read a version off. The constant is the only answer to diff --git a/implementations/swift/Tests/AgentSessionKitTests/SessionIndexContractTests.swift b/implementations/swift/Tests/AgentSessionKitTests/SessionIndexContractTests.swift index fe77108..9a4e39e 100644 --- a/implementations/swift/Tests/AgentSessionKitTests/SessionIndexContractTests.swift +++ b/implementations/swift/Tests/AgentSessionKitTests/SessionIndexContractTests.swift @@ -47,39 +47,37 @@ final class SessionIndexContractTests: XCTestCase { XCTAssertEqual(declared, SessionIndexStore.schemaVersion) } - /// Every object this lane creates is named in the contract, and every - /// object the contract names is created by this lane. Compared as sorted - /// object names rather than raw text so that formatting differences do - /// not fail the build, but a real addition or removal does. - func testCreatedObjectsMatchTheContract() throws { - let contractObjects = Self.createdObjectNames(in: try contractSQL()) - let swiftObjects = Self.createdObjectNames(in: SessionIndexStore.schemaSQLForContractTests) - XCTAssertFalse(contractObjects.isEmpty, "contract declares no objects") + /// Every statement this lane creates appears in the contract, and every + /// statement the contract declares is created by this lane — compared as + /// whole normalised definitions, so a changed column type, constraint, + /// index expression, FTS tokenizer, or trigger body fails here too. + /// + /// Normalisation collapses whitespace and drops `IF NOT EXISTS`, because + /// those differ between a file meant to be run once and a writer that + /// re-runs on every open. Nothing semantic is normalised away. + func testCreatedStatementsMatchTheContract() throws { + let contract = Self.createStatements(in: try contractSQL()) + let swift = Self.createStatements(in: SessionIndexStore.schemaSQLForContractTests) + XCTAssertFalse(contract.isEmpty, "contract declares no statements") XCTAssertEqual( - swiftObjects, - contractObjects, + swift, + contract, "session index schema drifted from contracts/storage/session-index-v5.sql" ) } - /// `CREATE [UNIQUE] INDEX|TABLE|VIRTUAL TABLE|TRIGGER [IF NOT EXISTS] ` - /// reduced to the bare object name. - private static func createdObjectNames(in sql: String) -> [String] { - var names: [String] = [] - for line in sql.split(separator: "\n") { - var tokens = line.trimmingCharacters(in: .whitespaces) - .split(separator: " ", omittingEmptySubsequences: true) - .map(String.init) - guard tokens.first?.uppercased() == "CREATE" else { continue } - tokens.removeFirst() - while let next = tokens.first?.uppercased(), - ["UNIQUE", "VIRTUAL", "TABLE", "INDEX", "TRIGGER", "IF", "NOT", "EXISTS"] - .contains(next) { - tokens.removeFirst() + /// Whole `CREATE …;` statements, normalised and sorted. + private static func createStatements(in sql: String) -> [String] { + sql + .split(separator: ";") + .map { statement in + statement + .split(whereSeparator: \.isWhitespace) + .joined(separator: " ") + .replacingOccurrences(of: "IF NOT EXISTS ", with: "") + .trimmingCharacters(in: .whitespaces) } - guard let name = tokens.first else { continue } - names.append(name.split(separator: "(").first.map(String.init) ?? name) - } - return names.sorted() + .filter { $0.uppercased().hasPrefix("CREATE ") } + .sorted() } }