Skip to content

Sessions

A session is the lifecycle container for one unit of agent work.

A session routes one agent task into an isolated workspace and records enough metadata to review, promote, discard, extend, or evict it.

  1. Consumer creates GitTrix with one durable adapter and one ephemeral adapter.
  2. Consumer calls await gittrix.init().
  3. Consumer starts a session with startSession({ task, durablePath?, durableRef?, durableBranch?, eviction? }).
  4. Gittrix records the durable branch head as baselineSha.
  5. Gittrix initializes an ephemeral workspace from the baseline.
  6. The agent receives session.forAgent().
  7. Agent reads, writes, deletes, lists, diffs, and commits inside the session API.
  8. User-facing app calls session.diff() and session.promote() with selected files or all touched files.
  9. Gittrix checks durable drift against the baseline.
  10. If selected files overlap durable changes since baseline, promotion fails with BASELINE_CONFLICT.
  11. If promotion succeeds, Gittrix applies a synthetic durable commit and marks the session promoted.
  12. If untilPromote is enabled, Gittrix evicts the ephemeral workspace after promotion.
flowchart TD
  Start["startSession()"] --> Baseline["record durable baselineSha"]
  Baseline --> Workspace["initialize ephemeral workspace"]
  Workspace --> Agent["AgentSession\nno promote()"]
  Agent --> Touch["read / write / delete / list / diff / commit"]
  Touch --> Review["UserSession.diff()"]
  Review --> Promote["UserSession.promote()"]
  Promote --> Drift["check durable drift since baseline"]
  Drift -->|overlap| Conflict["BASELINE_CONFLICT"]
  Drift -->|no overlap| Commit["durable.applyCommit()"]
  Commit --> State["state = promoted"]
  State --> Evict["evict when policy says so"]

Default session store:

~/.gittrix/sessions

Session files:

~/.gittrix/sessions/<session-id>/
├── metadata.json
├── .lock
└── workspace/

Mixed remote durable + remote ephemeral sessions may have no durablePath, but can still expose a local ephemeralPath for agent execution.

interface SessionMetadata {
metadataVersion: 1
id: string
task: string
durableRef: string
durablePath?: string
durableBranch?: string
ephemeralRef: string
ephemeralPath?: string
baselineSha: string
workspaceKind?: 'worktree' | 'clone' | 'copy' | 'remote'
isGitBacked?: boolean
state: 'active' | 'promoted' | 'discarded' | 'expired'
createdAt: string
updatedAt: string
lastAccessAt: string
evictionPolicy: {
ttlIdleMs: number | null
ttlAbsoluteMs: number | null
untilPromote: boolean
manual: boolean
}
touchedFiles: string[]
promote: {
strategy: 'auto' | 'commit' | 'branch' | 'pr' | 'patch'
result: { sha: string; branch: string; prUrl?: string } | null
}
}

Example GitHub durable + Cloudflare ephemeral metadata:

{
metadataVersion: 1,
id: 'sess_abc123',
task: 'update docs',
durableRef: 'github://owner/repo#main',
durableBranch: 'main',
ephemeralRef: 'cloudflare://default/gittrix-eph-sess_abc123',
ephemeralPath: '/Users/jack/.gittrix/cf-artifacts-ephemeral/sess_abc123',
baselineSha: '<durable-head-sha>',
workspaceKind: 'remote',
isGitBacked: true,
state: 'active',
touchedFiles: [],
promote: { strategy: 'auto', result: null }
}
type SessionState = 'active' | 'promoted' | 'discarded' | 'expired'
{
ttlIdleMs: 4 * 60 * 60 * 1000,
ttlAbsoluteMs: null,
untilPromote: true,
manual: false,
}

Default sweep interval:

5 * 60 * 1000

The user-facing app gets a UserSession, which can review, promote, discard, and extend the session.

The agent gets an AgentSession, which does not expose promote().

const agent = session.forAgent()