Metadata-Version: 2.4
Name: sabbatical
Version: 0.2.1
Summary: Local AI agent orchestration CLI — async task collaboration for developers
License: Commons Clause License Condition v1.0
         
         The Software is provided to you by the Licensor under the License,
         as defined below, subject to the following condition.
         
         Without limiting other conditions in the License, the grant of rights
         under the License will not include, and the License does not grant to
         you, the right to Sell the Software.
         
         For purposes of the foregoing, "Sell" means practicing any or all of
         the rights granted to you under the License to provide to third parties,
         for a fee or other consideration (including without limitation fees for
         hosting or consulting/support services related to the Software), a
         product or service whose value derives, entirely or substantially, from
         the functionality of the Software. Any license notice or attribution
         required by the License must also include this Commons Clause License
         Condition notice.
         
         Software:    sabbatical
         License:     Apache License 2.0
         Licensor:    Whitman Bohorquez
         
         
                                       Apache License
                                 Version 2.0, January 2004
                              http://www.apache.org/licenses/
         
         TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
         
         1. Definitions.
         
            "License" shall mean the terms and conditions for use, reproduction,
            and distribution as defined by Sections 1 through 9 of this document,
            plus the Commons Clause License Condition v1.0 above.
         
            "Licensor" shall mean the copyright owner or entity authorized by the
            copyright owner that is granting the License.
         
            "Legal Entity" shall mean the union of the acting entity and all other
            entities that control, are controlled by, or are under common control
            with that entity. For the purposes of this definition, "control" means
            (i) the power, direct or indirect, to cause the direction or management
            of such entity, whether by contract or otherwise, or (ii) ownership of
            fifty percent (50%) or more of the outstanding shares, or (iii)
            beneficial ownership of such entity.
         
            "You" (or "Your") shall mean an individual or Legal Entity exercising
            permissions granted by this License.
         
            "Source" form shall mean the preferred form for making modifications,
            including but not limited to software source code, documentation source,
            and configuration files.
         
            "Object" form shall mean any form resulting from mechanical
            transformation or translation of a Source form, including but not
            limited to compiled object code, generated documentation, and
            conversions to other media types.
         
            "Work" shall mean the work of authorship, whether in Source or Object
            form, made available under the License, as indicated by a copyright
            notice that is included in or attached to the work (an example is
            provided in the Appendix below).
         
            "Derivative Works" shall mean any work, whether in Source or Object
            form, that is based on (or derived from) the Work and for which the
            editorial revisions, annotations, elaborations, or other modifications
            represent, as a whole, an original work of authorship. For the purposes
            of this License, Derivative Works shall not include works that remain
            separable from, or merely link (or bind by name) to the interfaces of,
            the Work and Derivative Works thereof.
         
            "Contribution" shall mean any work of authorship, including the
            original version of the Work and any modifications or additions to
            that Work or Derivative Works thereof, that is intentionally submitted
            to the Licensor for inclusion in the Work by the copyright owner or by
            an individual or Legal Entity authorized to submit on behalf of the
            copyright owner. For the purposes of this definition, "submitted" means
            any form of electronic, verbal, or written communication sent to the
            Licensor or its representatives, including but not limited to
            communication on electronic mailing lists, source code control systems,
            and issue tracking systems that are managed by, or on behalf of, the
            Licensor for the purpose of discussing and improving the Work, but
            excluding communication that is conspicuously marked or otherwise
            designated in writing by the copyright owner as "Not a Contribution."
         
            "Contributor" shall mean Licensor and any individual or Legal Entity on
            behalf of whom a Contribution has been received by the Licensor and
            subsequently incorporated within the Work.
         
         2. Grant of Copyright License. Subject to the terms and conditions of
            this License (including the Commons Clause License Condition), each
            Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
            no-charge, royalty-free, irrevocable copyright license to reproduce,
            prepare Derivative Works of, publicly display, publicly perform,
            sublicense, and distribute the Work and such Derivative Works in
            Source or Object form.
         
         3. Grant of Patent License. Subject to the terms and conditions of this
            License (including the Commons Clause License Condition), each
            Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
            no-charge, royalty-free, irrevocable (except as stated in this section)
            patent license to make, have made, use, offer to sell, sell, import,
            and otherwise transfer the Work, where such license applies only to
            those patent claims licensable by such Contributor that are necessarily
            infringed by their Contribution(s) alone or by combination of their
            Contribution(s) with the Work to which such Contribution(s) was
            submitted. If You institute patent litigation against any entity
            (including a cross-claim or counterclaim in a lawsuit) alleging that
            the Work or a Contribution incorporated within the Work constitutes
            direct or contributory patent infringement, then any patent licenses
            granted to You under this License for that Work shall terminate as of
            the date such litigation is filed.
         
         4. Redistribution. You may reproduce and distribute copies of the Work
            or Derivative Works thereof in any medium, with or without
            modifications, and in Source or Object form, provided that You meet
            the following conditions:
         
            (a) You must give any other recipients of the Work or Derivative Works
                a copy of this License; and
         
            (b) You must cause any modified files to carry prominent notices
                stating that You changed the files; and
         
            (c) You must retain, in the Source form of any Derivative Works that
                You distribute, all copyright, patent, trademark, and attribution
                notices from the Source form of the Work, excluding those notices
                that do not pertain to any part of the Derivative Works; and
         
            (d) If the Work includes a "NOTICE" text file as part of its
                distribution, then any Derivative Works that You distribute must
                include a readable copy of the attribution notices contained
                within such NOTICE file, excluding any notices that do not pertain
                to any part of the Derivative Works, in at least one of the
                following places: within a NOTICE text file distributed as part of
                the Derivative Works; within the Source form or documentation, if
                provided along with the Derivative Works; or, within a display
                generated by the Derivative Works, if and wherever such third-party
                notices normally appear. The contents of the NOTICE file are for
                informational purposes only and do not modify the License. You may
                add Your own attribution notices within Derivative Works that You
                distribute, alongside or as an addendum to the NOTICE text from
                the Work, provided that such additional attribution notices cannot
                be construed as modifying the License.
         
            You may add Your own copyright statement to Your modifications and may
            provide additional or different license terms and conditions for use,
            reproduction, or distribution of Your modifications, or for any such
            Derivative Works as a whole, provided Your use, reproduction, and
            distribution of the Work otherwise complies with the conditions stated
            in this License (including the Commons Clause License Condition).
         
         5. Submission of Contributions. Unless You explicitly state otherwise,
            any Contribution intentionally submitted for inclusion in the Work by
            You to the Licensor shall be under the terms and conditions of this
            License, without any additional terms or conditions. Notwithstanding
            the above, nothing herein shall supersede or modify the terms of any
            separate license agreement you may have executed with Licensor
            regarding such Contributions.
         
         6. Trademarks. This License does not grant permission to use the trade
            names, trademarks, service marks, or product names of the Licensor,
            except as required for reasonable and customary use in describing the
            origin of the Work and reproducing the content of the NOTICE file.
         
         7. Disclaimer of Warranty. Unless required by applicable law or agreed
            to in writing, Licensor provides the Work (and each Contributor
            provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES
            OR CONDITIONS OF ANY KIND, either express or implied, including,
            without limitation, any warranties or conditions of TITLE,
            NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR
            PURPOSE. You are solely responsible for determining the appropriateness
            of using or redistributing the Work and assume any risks associated
            with Your exercise of permissions under this License.
         
         8. Limitation of Liability. In no event and under no legal theory,
            whether in tort (including negligence), contract, or otherwise, unless
            required by applicable law (such as deliberate and grossly negligent
            acts) or agreed to in writing, shall any Contributor be liable to You
            for damages, including any direct, indirect, special, incidental, or
            consequential damages of any character arising as a result of this
            License or out of the use or inability to use the Work (including but
            not limited to damages for loss of goodwill, work stoppage, computer
            failure or malfunction, or any and all other commercial damages or
            losses), even if such Contributor has been advised of the possibility
            of such damages.
         
         9. Accepting Warranty or Additional Liability. While redistributing the
            Work or Derivative Works thereof, You may choose to offer, and charge
            a fee for, acceptance of support, warranty, indemnity, or other
            liability obligations and/or rights consistent with this License
            (including the Commons Clause License Condition). However, in
            accepting such obligations, You may act only on Your own behalf and
            on Your sole responsibility, not on behalf of any other Contributor,
            and only if You agree to indemnify, defend, and hold each Contributor
            harmless for any liability incurred by, or claims asserted against,
            such Contributor by reason of your accepting any such warranty or
            additional liability.
         
         END OF TERMS AND CONDITIONS
         
         Copyright 2026 Whitman Bohorquez
         
         Licensed under the Apache License, Version 2.0, modified by the
         Commons Clause License Condition v1.0 (the "License"); you may not
         use this file except in compliance with the License. You may obtain
         a copy of the Apache License at
         
             http://www.apache.org/licenses/LICENSE-2.0
         
         Unless required by applicable law or agreed to in writing, software
         distributed under the License is distributed on an "AS IS" BASIS,
         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         See the License for the specific language governing permissions and
         limitations under the License.
License-File: LICENSE
Keywords: ai,agents,cli,orchestration,llm,automation
Author: elpapi42
Requires-Python: >=3.12,<4.0
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Utilities
Requires-Dist: alembic (>=1.13.0)
Requires-Dist: databases[aiosqlite] (>=0.9.0)
Requires-Dist: fastapi (>=0.111.0)
Requires-Dist: google-adk[extensions] (>=0.1.0)
Requires-Dist: httpx (>=0.27.0)
Requires-Dist: mcp (>=1.26.0)
Requires-Dist: pydantic (>=2.7.0)
Requires-Dist: sqlalchemy (>=2.0.0)
Requires-Dist: sse-starlette (>=2.1.0)
Requires-Dist: typer[all] (>=0.12.0)
Requires-Dist: uvicorn[standard] (>=0.30.0)
Project-URL: Homepage, https://github.com/elpapi42/sabbatical
Project-URL: Issues, https://github.com/elpapi42/sabbatical/issues
Project-URL: Repository, https://github.com/elpapi42/sabbatical
Description-Content-Type: text/markdown

# Sabbatical

**A local AI agent orchestration system built around async task collaboration — not chat sessions.**

---

Most people use AI by opening a chat window, typing a request, and waiting. The AI responds. You react. It's a conversation — synchronous, serial, one thing at a time. You're blocked until it finishes, and it's blocked until you respond.

Sabbatical is a different model. You write a task spec. You assign it to an agent. The agent picks it up, does the work using real tools in your actual codebase, hands it off to another agent via an `@mention`, and those agents keep working until the task surfaces back to you — done, blocked, or ready for review. Meanwhile, you're working on something else.

It's the difference between pair programming on a video call and managing a team through tasks. The team model scales. The call doesn't.

---

## How It Works

Sabbatical runs a **local API server** on your machine. The server manages a **database-as-queue**: it continuously polls for tasks assigned to agents, spins up worker threads, runs agents against real tools (file reads, file writes, shell commands), and processes their output. There is no cloud dependency. Everything — the database, the agent workspace, the execution — lives on your machine.

### The Core Concepts

**Organizations** are isolated workspaces. Each organization has a `workspace_path` (a directory on your machine) and a roster of agents. Agents in different organizations cannot interact.

**Agents** are stateless worker profiles defined by a `.md` instruction file. The instruction file is the agent's identity: its expertise, its working style, its persona. Agents don't persist state between executions — their only context is the task description and the comment thread.

**Tasks** are the unit of work. Each task has a title, a detailed description (the spec), a status, an assignee, and a **comment thread**. The thread is the shared memory of the task: every agent that works on it leaves a comment, and every future agent reads the full thread before picking up where the previous one left off.

**The Comment Thread** is what makes multi-agent collaboration coherent. Agents can't see each other's internal reasoning or tool calls — those are private to each run. But they see every comment in the thread. When an agent hands off to another with `@agent_name`, the next agent receives the full thread context including that handoff message. No context is lost between agents.

**The Dispatcher** is the always-on polling loop that drives everything. It claims dispatchable tasks atomically (preventing double-execution), spins up a worker thread per task, and processes the agent's final output to determine routing. It runs inside the API server — `server up` starts it, `server down` gracefully stops it.

### The Handoff Protocol

When an agent finishes its work, it writes a final message. That message becomes a permanent comment on the task thread. The system reads the **first valid `@tag`** in that message to determine where the task goes next:

- `@agent_name` → task is routed to that agent, queued for dispatch
- `@user` → task returns to you for review or input
- No valid tag → task escalates to the agent's boss; if no boss, it goes to you

This is how agents collaborate without you in the loop. A `lead_dev` agent can delegate a specific problem to a `frontend_dev`, who can hand the result back to `lead_dev` for review, who can then return it to `@user`. Three agents, zero interruptions for you.

### The Hierarchy

Agents can have a **boss** — another agent in the same organization. The hierarchy is informational, not restrictive: any agent can tag any other agent in the organization. But the hierarchy powers smart escalation: if an agent fails to route properly (no valid tag in its output), the dispatcher automatically escalates to its boss. This gives you a safety net and a natural review chain.

---

## A Real Workflow

You're building a React app. You have an organization `react_app` with three agents: `lead_dev` (root), `frontend_dev` (reports to `lead_dev`), and `test_writer` (reports to `lead_dev`).

You write a task spec and kick it off:

```bash
sabbatical task create "Add dark mode toggle to the header" \
  --organization react_app \
  --description "Implement a dark/light mode toggle in the header component. Use Tailwind's dark: prefix classes. The toggle should persist preference in localStorage. Existing header is at src/components/Header.tsx." \
  --assign lead_dev
```

`lead_dev` picks it up. It reads the spec, inspects the codebase with `read_file` and `list_directory`, and decides this is UI work for `frontend_dev`. It writes a detailed handoff comment explaining the approach and tags `@frontend_dev`. You're not involved.

`frontend_dev` picks up the task. It reads the thread — including `lead_dev`'s briefing — modifies `Header.tsx`, adds a `ThemeToggle` component, and updates the Tailwind config. It finishes and tags `@test_writer` with a summary of what was changed.

`test_writer` reads the thread, understands the full context of what was built, and writes tests for the toggle behavior. It tags `@lead_dev` for a final review pass.

`lead_dev` reviews everything, requests a small change via a comment, tags `@frontend_dev` again. `frontend_dev` makes the fix, tags `@lead_dev`. `lead_dev` approves and tags `@user`.

You get a notification. You check the thread with `task view`, see the full history of what every agent did, review the code changes in your editor, and run `task done REAC-0012`.

While all of that was happening, you were working on three other tasks.

---

## The Assistant

Sabbatical includes a conversational planning copilot — **The Assistant** — for when you want help structuring work before delegating it.

```bash
sabbatical chat new --organization react_app
```

The Assistant knows your organization's agents and hierarchy. It helps you break down high-level goals into atomic tasks, writes detailed task specs that stateless agents can execute without ambiguity, and assigns tasks to the right agents. It can also bootstrap entire organizations from scratch — proposing agent names, hierarchies, and instruction files — if you're starting a new project.

The Assistant never executes technical work. It is a planning layer, not a worker. Workers are agents.

---

## Setup

### Prerequisites

- Python 3.12+
- Poetry
- An [OpenRouter](https://openrouter.ai) API key (Open to contributions to make more providers available, even Claude Code, Codex, Gemini adapters)

### Install

```bash
git clone https://github.com/elpapi42/sabbatical.git
cd sabbatical
poetry install
```

### Configure

```bash
export OPENROUTER_API_KEY="sk-or-your-key-here"
```

The config file is auto-generated at `~/.sabbatical/config.toml` on first run. Edit it to change the default model, concurrency limit, or server port.

### Start the Server

```bash
poetry run sabbatical server up
```

The server starts in the background. The dispatcher begins polling immediately. Any tasks already queued in the database from a previous session are picked up automatically.

```bash
poetry run sabbatical server status   # snapshot: task counts, active workers, total cost
poetry run sabbatical server down     # graceful shutdown; active runs finish before stopping
```

---

## CLI Reference

### Organizations

```bash
sabbatical organization create <name> --workspace-path <path> --description "<text>"
sabbatical organization list
sabbatical organization view <name>       # hierarchy tree
sabbatical organization delete <name>     # cascade deletes all agents, tasks, runs
```

### Agents

```bash
sabbatical agent add <name> --organization <org> --instructions <path.md>
sabbatical agent add <name> --organization <org> --instructions <path.md> --boss <boss_name> --description "<one-liner>"
sabbatical agent list --organization <org>
sabbatical agent view <name> --organization <org>
sabbatical agent edit <name> --organization <org> --boss <name>
sabbatical agent remove <name> --organization <org>   # soft-delete; history preserved
```

### Tasks

```bash
sabbatical task create "<title>" --organization <org> --assign <agent|user> --description "<spec>"
sabbatical task list --organization <org> --status <open|in_progress|failed|done|canceled>
sabbatical task view <id>                    # full thread: comments + run summaries interleaved
sabbatical task comment <id> "<message>"     # @mention an agent to delegate/unblock
sabbatical task preempt <id>                 # interrupt an in-progress task
sabbatical task done <id>                    # you verify and close
sabbatical task cancel <id>
sabbatical task reopen <id>
```

### Runs

```bash
sabbatical run view <run-id>           # full step-by-step: tool calls, arguments, stdout/stderr
sabbatical run list --task <id>
```

### Chat (The Assistant)

```bash
sabbatical chat new [--organization <org>]
sabbatical chat list
sabbatical chat resume <session-id>
```

---

## Writing Agent Instructions

An agent's identity lives in a `.md` file referenced by `--instructions`. This file is its character sheet: who it is, what it knows, how it works. Write it as if describing a real team member.

```markdown
# lead_dev

You are the lead developer for this project. You own overall code quality and architecture decisions.

When a task comes to you, your first job is to understand the full scope, then either execute it yourself or break it into focused sub-problems and delegate to the right specialist on your team.

Your team:
- @frontend_dev — React, TypeScript, UI/UX
- @test_writer — unit tests, integration tests, coverage

When delegating, write a clear briefing in your handoff: what you've already done, what you need from them, and any constraints or decisions they should know about. The next agent's only context is this thread.
```

The instruction file is injected into every run as part of the agent's context. Keep it specific. Generic instructions produce generic agents.

---

## Architecture

```
┌─────────────────────────────────────────────────────┐
│                  Local API Server                   │
│                                                     │
│  ┌─────────────┐    ┌────────────────────────────┐  │
│  │  Dispatcher │    │     Worker Threads         │  │
│  │  (polling   │───▶│  Agent + ADK Runner        │  │
│  │   loop)     │    │  Tools: read/write/shell   │  │
│  └─────────────┘    └────────────────────────────┘  │
│         │                       │                   │
│         ▼                       ▼                   │
│  ┌──────────────────────────────────────────────┐   │
│  │              SQLite Database                 │   │
│  │  organizations · agents · tasks · comments   │   │
│  │  runs · sessions                             │   │
│  └──────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────┘
              ▲
              │ HTTP
              ▼
        ┌──────────┐
        │ Thin CLI │  (sabbatical <command>)
        └──────────┘
```

- **Database-as-queue**: no in-memory event bus. The dispatcher polls SQLite directly. Crash recovery is zero-effort — on `server up`, the dispatcher resumes polling and picks up any open tasks.
- **LLM provider**: all calls route through [OpenRouter](https://openrouter.ai), giving you access to any model.
- **Agent runtime**: built on [Google ADK](https://google.github.io/adk-docs/) with LiteLLM for model routing.
- **Stateless workers**: each run is a fresh agent instance. Context is injected entirely through the system prompt and the task thread.

---

## Development

```bash
# Generate a new DB migration after changing the schema
poetry run alembic revision --autogenerate -m "describe_the_change"

# Apply migrations manually
poetry run alembic upgrade head
```

Migrations run automatically on `server up`. The database lives at `~/.sabbatical/sabbatical.db`.

---

## Status

Sabbatical is under active development. V1 is focused on establishing the core execution model: local multi-agent task collaboration with a stable state machine, real tool access, and cost tracking. Planned for future iterations: context window management (thread summarization), richer task decomposition primitives, and broader LLM provider support.

---

## Contributing

Issues and PRs are welcome. If you're building something with Sabbatical or have feedback on the agent collaboration model, open a discussion.

