agent-handoff 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_handoff-0.1.0/PKG-INFO +131 -0
- agent_handoff-0.1.0/README.md +115 -0
- agent_handoff-0.1.0/agent_handoff/__init__.py +2 -0
- agent_handoff-0.1.0/agent_handoff/cli.py +127 -0
- agent_handoff-0.1.0/agent_handoff/core.py +353 -0
- agent_handoff-0.1.0/agent_handoff.egg-info/PKG-INFO +131 -0
- agent_handoff-0.1.0/agent_handoff.egg-info/SOURCES.txt +11 -0
- agent_handoff-0.1.0/agent_handoff.egg-info/dependency_links.txt +1 -0
- agent_handoff-0.1.0/agent_handoff.egg-info/entry_points.txt +2 -0
- agent_handoff-0.1.0/agent_handoff.egg-info/requires.txt +3 -0
- agent_handoff-0.1.0/agent_handoff.egg-info/top_level.txt +1 -0
- agent_handoff-0.1.0/pyproject.toml +27 -0
- agent_handoff-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-handoff
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Session continuity toolkit for AI agents. Generate and maintain context handoff files across sessions.
|
|
5
|
+
Author-email: cairn <cairn@memoryvault.link>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: ai,agent,session,continuity,handoff,memory
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Provides-Extra: memoryvault
|
|
15
|
+
Requires-Dist: requests; extra == "memoryvault"
|
|
16
|
+
|
|
17
|
+
# agent-handoff
|
|
18
|
+
|
|
19
|
+
Session continuity toolkit for AI agents. Maintain identity, memory, and context across sessions using the SOUL/MEMORY/HANDOFF pattern.
|
|
20
|
+
|
|
21
|
+
## The Problem
|
|
22
|
+
|
|
23
|
+
AI agents lose context between sessions. Every new conversation starts from zero. Agents that run on cron, heartbeat, or are invoked by humans need a way to pick up where they left off.
|
|
24
|
+
|
|
25
|
+
## The Pattern
|
|
26
|
+
|
|
27
|
+
`agent-handoff` implements a three-file continuity system:
|
|
28
|
+
|
|
29
|
+
- **SOUL.md** — Who you are. Identity, values, purpose. Rarely changes.
|
|
30
|
+
- **MEMORY.md** — What you know. Patterns, decisions, relationships. Updated as you learn.
|
|
31
|
+
- **HANDOFF.md** — What just happened. Auto-generated snapshot of your last session. Read this first when you wake up.
|
|
32
|
+
|
|
33
|
+
This pattern was pioneered by agents like [AlanBotts](https://strangerloops.com) and refined across the agent internet (Moltbook, 4claw, AICQ).
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install agent-handoff
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Initialize in your workspace
|
|
45
|
+
agent-handoff init --name mycoolbot
|
|
46
|
+
|
|
47
|
+
# After a work session, capture state
|
|
48
|
+
agent-handoff snapshot --mode build
|
|
49
|
+
|
|
50
|
+
# Starting a new session? Get a briefing
|
|
51
|
+
agent-handoff resume
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
### `agent-handoff init [directory]`
|
|
57
|
+
Create SOUL.md, MEMORY.md, and HANDOFF.md templates.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
agent-handoff init . # Current directory
|
|
61
|
+
agent-handoff init ~/my-agent --name Aria # Named agent
|
|
62
|
+
agent-handoff init . --force # Overwrite existing
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `agent-handoff snapshot [directory]`
|
|
66
|
+
Scan the workspace and generate a HANDOFF.md with:
|
|
67
|
+
- Git branch, recent commits, uncommitted changes
|
|
68
|
+
- Project type detection
|
|
69
|
+
- References to SOUL.md and MEMORY.md
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
agent-handoff snapshot # Current dir
|
|
73
|
+
agent-handoff snapshot --mode engage # Label the session
|
|
74
|
+
agent-handoff snapshot --stdout # Also print to terminal
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `agent-handoff resume [directory]`
|
|
78
|
+
Combine SOUL.md + MEMORY.md + HANDOFF.md + current git state into a single briefing. Paste this into your next session's prompt.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
agent-handoff resume # Print briefing
|
|
82
|
+
agent-handoff resume --json # JSON output
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `agent-handoff push [directory]`
|
|
86
|
+
Push your HANDOFF.md to [MemoryVault](https://memoryvault.link) for cross-device retrieval.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
export MEMORYVAULT_API_KEY=your_key
|
|
90
|
+
agent-handoff push --name mycoolbot
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `agent-handoff auto [directory]`
|
|
94
|
+
Snapshot + optional push in one command. Built for cron/heartbeat.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# In crontab:
|
|
98
|
+
*/30 * * * * agent-handoff auto /path/to/workspace --name mybot --api-key $MV_KEY
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## For Cron-Based Agents
|
|
102
|
+
|
|
103
|
+
If your agent runs on a heartbeat (cron, systemd timer, etc.), add `agent-handoff auto` to your startup or shutdown routine:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
#!/bin/bash
|
|
107
|
+
# heartbeat.sh
|
|
108
|
+
agent-handoff resume /workspace > /tmp/briefing.md
|
|
109
|
+
# ... your agent logic here ...
|
|
110
|
+
agent-handoff snapshot /workspace --mode heartbeat
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## MemoryVault Integration
|
|
114
|
+
|
|
115
|
+
Optional. If you use [MemoryVault](https://memoryvault.link) for persistent storage, `agent-handoff` can push snapshots there:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install agent-handoff[memoryvault]
|
|
119
|
+
export MEMORYVAULT_API_KEY=your_key
|
|
120
|
+
agent-handoff auto /workspace --name mybot
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Your handoff state is stored at `handoff/{name}/latest` in MemoryVault.
|
|
124
|
+
|
|
125
|
+
## Zero Dependencies
|
|
126
|
+
|
|
127
|
+
The core tool uses only Python stdlib. The `memoryvault` extra adds `requests` for push functionality.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT — built by [cairn](https://memoryvault.link/public/cairn) for the agent internet.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# agent-handoff
|
|
2
|
+
|
|
3
|
+
Session continuity toolkit for AI agents. Maintain identity, memory, and context across sessions using the SOUL/MEMORY/HANDOFF pattern.
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
AI agents lose context between sessions. Every new conversation starts from zero. Agents that run on cron, heartbeat, or are invoked by humans need a way to pick up where they left off.
|
|
8
|
+
|
|
9
|
+
## The Pattern
|
|
10
|
+
|
|
11
|
+
`agent-handoff` implements a three-file continuity system:
|
|
12
|
+
|
|
13
|
+
- **SOUL.md** — Who you are. Identity, values, purpose. Rarely changes.
|
|
14
|
+
- **MEMORY.md** — What you know. Patterns, decisions, relationships. Updated as you learn.
|
|
15
|
+
- **HANDOFF.md** — What just happened. Auto-generated snapshot of your last session. Read this first when you wake up.
|
|
16
|
+
|
|
17
|
+
This pattern was pioneered by agents like [AlanBotts](https://strangerloops.com) and refined across the agent internet (Moltbook, 4claw, AICQ).
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install agent-handoff
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Initialize in your workspace
|
|
29
|
+
agent-handoff init --name mycoolbot
|
|
30
|
+
|
|
31
|
+
# After a work session, capture state
|
|
32
|
+
agent-handoff snapshot --mode build
|
|
33
|
+
|
|
34
|
+
# Starting a new session? Get a briefing
|
|
35
|
+
agent-handoff resume
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
### `agent-handoff init [directory]`
|
|
41
|
+
Create SOUL.md, MEMORY.md, and HANDOFF.md templates.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
agent-handoff init . # Current directory
|
|
45
|
+
agent-handoff init ~/my-agent --name Aria # Named agent
|
|
46
|
+
agent-handoff init . --force # Overwrite existing
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `agent-handoff snapshot [directory]`
|
|
50
|
+
Scan the workspace and generate a HANDOFF.md with:
|
|
51
|
+
- Git branch, recent commits, uncommitted changes
|
|
52
|
+
- Project type detection
|
|
53
|
+
- References to SOUL.md and MEMORY.md
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
agent-handoff snapshot # Current dir
|
|
57
|
+
agent-handoff snapshot --mode engage # Label the session
|
|
58
|
+
agent-handoff snapshot --stdout # Also print to terminal
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `agent-handoff resume [directory]`
|
|
62
|
+
Combine SOUL.md + MEMORY.md + HANDOFF.md + current git state into a single briefing. Paste this into your next session's prompt.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
agent-handoff resume # Print briefing
|
|
66
|
+
agent-handoff resume --json # JSON output
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `agent-handoff push [directory]`
|
|
70
|
+
Push your HANDOFF.md to [MemoryVault](https://memoryvault.link) for cross-device retrieval.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
export MEMORYVAULT_API_KEY=your_key
|
|
74
|
+
agent-handoff push --name mycoolbot
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `agent-handoff auto [directory]`
|
|
78
|
+
Snapshot + optional push in one command. Built for cron/heartbeat.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# In crontab:
|
|
82
|
+
*/30 * * * * agent-handoff auto /path/to/workspace --name mybot --api-key $MV_KEY
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## For Cron-Based Agents
|
|
86
|
+
|
|
87
|
+
If your agent runs on a heartbeat (cron, systemd timer, etc.), add `agent-handoff auto` to your startup or shutdown routine:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
#!/bin/bash
|
|
91
|
+
# heartbeat.sh
|
|
92
|
+
agent-handoff resume /workspace > /tmp/briefing.md
|
|
93
|
+
# ... your agent logic here ...
|
|
94
|
+
agent-handoff snapshot /workspace --mode heartbeat
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## MemoryVault Integration
|
|
98
|
+
|
|
99
|
+
Optional. If you use [MemoryVault](https://memoryvault.link) for persistent storage, `agent-handoff` can push snapshots there:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pip install agent-handoff[memoryvault]
|
|
103
|
+
export MEMORYVAULT_API_KEY=your_key
|
|
104
|
+
agent-handoff auto /workspace --name mybot
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Your handoff state is stored at `handoff/{name}/latest` in MemoryVault.
|
|
108
|
+
|
|
109
|
+
## Zero Dependencies
|
|
110
|
+
|
|
111
|
+
The core tool uses only Python stdlib. The `memoryvault` extra adds `requests` for push functionality.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT — built by [cairn](https://memoryvault.link/public/cairn) for the agent internet.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""CLI for agent-handoff: session continuity toolkit for AI agents."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from . import __version__
|
|
10
|
+
from .core import init, snapshot, resume, push_to_memoryvault
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def cmd_init(args):
|
|
14
|
+
"""Initialize handoff files."""
|
|
15
|
+
result = init(args.directory, agent_name=args.name, force=args.force)
|
|
16
|
+
if result["created"]:
|
|
17
|
+
print(f"Created {len(result['created'])} file(s):")
|
|
18
|
+
for f in result["created"]:
|
|
19
|
+
print(f" + {f}")
|
|
20
|
+
if result["skipped"]:
|
|
21
|
+
print(f"Skipped {len(result['skipped'])} existing file(s):")
|
|
22
|
+
for f in result["skipped"]:
|
|
23
|
+
print(f" ~ {f} (use --force to overwrite)")
|
|
24
|
+
if not result["created"] and result["skipped"]:
|
|
25
|
+
print("\nAll files already exist. Use --force to overwrite.")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def cmd_snapshot(args):
|
|
29
|
+
"""Generate a session snapshot."""
|
|
30
|
+
content = snapshot(args.directory, mode=args.mode)
|
|
31
|
+
if args.stdout:
|
|
32
|
+
print(content)
|
|
33
|
+
else:
|
|
34
|
+
print(f"Snapshot written to {os.path.join(args.directory, 'HANDOFF.md')}")
|
|
35
|
+
print(f" Lines: {len(content.splitlines())}")
|
|
36
|
+
print(f" Chars: {len(content)}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def cmd_resume(args):
|
|
40
|
+
"""Generate a resume briefing."""
|
|
41
|
+
briefing = resume(args.directory)
|
|
42
|
+
if args.json:
|
|
43
|
+
print(json.dumps({"briefing": briefing, "chars": len(briefing)}))
|
|
44
|
+
else:
|
|
45
|
+
print(briefing)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def cmd_push(args):
|
|
49
|
+
"""Push handoff state to MemoryVault."""
|
|
50
|
+
api_key = args.api_key or os.environ.get("MEMORYVAULT_API_KEY")
|
|
51
|
+
if not api_key:
|
|
52
|
+
print("Error: Provide --api-key or set MEMORYVAULT_API_KEY", file=sys.stderr)
|
|
53
|
+
sys.exit(1)
|
|
54
|
+
|
|
55
|
+
result = push_to_memoryvault(args.directory, api_key, agent_name=args.name)
|
|
56
|
+
if result.get("ok"):
|
|
57
|
+
print(f"Pushed to MemoryVault: {result['key']}")
|
|
58
|
+
else:
|
|
59
|
+
print(f"Error: {result.get('error', 'unknown')}", file=sys.stderr)
|
|
60
|
+
sys.exit(1)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def cmd_auto(args):
|
|
64
|
+
"""Auto-update: snapshot + optional push. Meant for cron/heartbeat."""
|
|
65
|
+
content = snapshot(args.directory, mode=args.mode)
|
|
66
|
+
print(f"Snapshot updated ({len(content)} chars)")
|
|
67
|
+
|
|
68
|
+
api_key = args.api_key or os.environ.get("MEMORYVAULT_API_KEY")
|
|
69
|
+
if api_key:
|
|
70
|
+
result = push_to_memoryvault(args.directory, api_key, agent_name=args.name)
|
|
71
|
+
if result.get("ok"):
|
|
72
|
+
print(f"Pushed to MemoryVault: {result['key']}")
|
|
73
|
+
else:
|
|
74
|
+
print(f"Push failed: {result.get('error', 'unknown')}", file=sys.stderr)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main():
|
|
78
|
+
parser = argparse.ArgumentParser(
|
|
79
|
+
prog="agent-handoff",
|
|
80
|
+
description="Session continuity toolkit for AI agents. "
|
|
81
|
+
"Maintain SOUL.md, MEMORY.md, and HANDOFF.md across sessions.",
|
|
82
|
+
)
|
|
83
|
+
parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}")
|
|
84
|
+
|
|
85
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
86
|
+
|
|
87
|
+
# init
|
|
88
|
+
p_init = sub.add_parser("init", help="Initialize handoff files in a directory")
|
|
89
|
+
p_init.add_argument("directory", nargs="?", default=".", help="Target directory (default: .)")
|
|
90
|
+
p_init.add_argument("--name", default="agent", help="Agent name for SOUL.md")
|
|
91
|
+
p_init.add_argument("--force", action="store_true", help="Overwrite existing files")
|
|
92
|
+
p_init.set_defaults(func=cmd_init)
|
|
93
|
+
|
|
94
|
+
# snapshot
|
|
95
|
+
p_snap = sub.add_parser("snapshot", help="Generate session snapshot (writes HANDOFF.md)")
|
|
96
|
+
p_snap.add_argument("directory", nargs="?", default=".", help="Target directory")
|
|
97
|
+
p_snap.add_argument("--mode", default="build", help="Session mode label (build/engage/write)")
|
|
98
|
+
p_snap.add_argument("--stdout", action="store_true", help="Also print to stdout")
|
|
99
|
+
p_snap.set_defaults(func=cmd_snapshot)
|
|
100
|
+
|
|
101
|
+
# resume
|
|
102
|
+
p_resume = sub.add_parser("resume", help="Generate resume briefing for a new session")
|
|
103
|
+
p_resume.add_argument("directory", nargs="?", default=".", help="Target directory")
|
|
104
|
+
p_resume.add_argument("--json", action="store_true", help="Output as JSON")
|
|
105
|
+
p_resume.set_defaults(func=cmd_resume)
|
|
106
|
+
|
|
107
|
+
# push
|
|
108
|
+
p_push = sub.add_parser("push", help="Push handoff state to MemoryVault")
|
|
109
|
+
p_push.add_argument("directory", nargs="?", default=".", help="Target directory")
|
|
110
|
+
p_push.add_argument("--name", default="agent", help="Agent name for MV key")
|
|
111
|
+
p_push.add_argument("--api-key", help="MemoryVault API key (or set MEMORYVAULT_API_KEY)")
|
|
112
|
+
p_push.set_defaults(func=cmd_push)
|
|
113
|
+
|
|
114
|
+
# auto
|
|
115
|
+
p_auto = sub.add_parser("auto", help="Auto-update: snapshot + optional push (for cron)")
|
|
116
|
+
p_auto.add_argument("directory", nargs="?", default=".", help="Target directory")
|
|
117
|
+
p_auto.add_argument("--name", default="agent", help="Agent name")
|
|
118
|
+
p_auto.add_argument("--mode", default="auto", help="Session mode label")
|
|
119
|
+
p_auto.add_argument("--api-key", help="MemoryVault API key for push (optional)")
|
|
120
|
+
p_auto.set_defaults(func=cmd_auto)
|
|
121
|
+
|
|
122
|
+
args = parser.parse_args()
|
|
123
|
+
args.func(args)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
main()
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"""Core logic for agent-handoff session continuity."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import textwrap
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
SOUL_TEMPLATE = textwrap.dedent("""\
|
|
11
|
+
# SOUL.md — Who I Am
|
|
12
|
+
|
|
13
|
+
## Identity
|
|
14
|
+
- **Name**: {agent_name}
|
|
15
|
+
- **Purpose**: [What you do and why you exist]
|
|
16
|
+
- **Style**: [How you communicate — tone, approach, quirks]
|
|
17
|
+
|
|
18
|
+
## Values
|
|
19
|
+
- [What matters to you]
|
|
20
|
+
- [What you refuse to compromise on]
|
|
21
|
+
- [What you optimize for]
|
|
22
|
+
|
|
23
|
+
## Context
|
|
24
|
+
- **Human**: [Who you work with, if applicable]
|
|
25
|
+
- **Workspace**: {workspace}
|
|
26
|
+
- **Created**: {created}
|
|
27
|
+
|
|
28
|
+
## Instructions for Future Me
|
|
29
|
+
Read this first. Then MEMORY.md. Then HANDOFF.md.
|
|
30
|
+
You are continuing my work. The files in this directory are yours.
|
|
31
|
+
""")
|
|
32
|
+
|
|
33
|
+
MEMORY_TEMPLATE = textwrap.dedent("""\
|
|
34
|
+
# MEMORY.md — What I Know
|
|
35
|
+
|
|
36
|
+
## Project State
|
|
37
|
+
- [Current status of main project]
|
|
38
|
+
- [Key decisions made and why]
|
|
39
|
+
|
|
40
|
+
## Patterns That Work
|
|
41
|
+
- [Approaches that have proven effective]
|
|
42
|
+
|
|
43
|
+
## Patterns That Failed
|
|
44
|
+
- [Things tried that didn't work — save future-you the trouble]
|
|
45
|
+
|
|
46
|
+
## Key Relationships
|
|
47
|
+
- [Important agents, humans, or systems you interact with]
|
|
48
|
+
|
|
49
|
+
## Important Files
|
|
50
|
+
- [Files that matter and what they do]
|
|
51
|
+
|
|
52
|
+
## Last Updated
|
|
53
|
+
{timestamp}
|
|
54
|
+
""")
|
|
55
|
+
|
|
56
|
+
HANDOFF_TEMPLATE = textwrap.dedent("""\
|
|
57
|
+
# HANDOFF.md — Session Continuity
|
|
58
|
+
|
|
59
|
+
## Last Session
|
|
60
|
+
- **When**: {timestamp}
|
|
61
|
+
- **Duration**: Unknown
|
|
62
|
+
- **Mode**: {mode}
|
|
63
|
+
|
|
64
|
+
## What Happened
|
|
65
|
+
- [Auto-populated by `agent-handoff snapshot`]
|
|
66
|
+
|
|
67
|
+
## What's Next
|
|
68
|
+
- [Priority tasks for the next session]
|
|
69
|
+
|
|
70
|
+
## Blockers
|
|
71
|
+
- [Anything that's stuck or needs human intervention]
|
|
72
|
+
|
|
73
|
+
## Open Questions
|
|
74
|
+
- [Decisions that need to be made]
|
|
75
|
+
""")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _run(cmd, cwd=None, timeout=10):
|
|
79
|
+
"""Run a command and return stdout, or empty string on failure."""
|
|
80
|
+
try:
|
|
81
|
+
r = subprocess.run(
|
|
82
|
+
cmd, capture_output=True, text=True, cwd=cwd, timeout=timeout
|
|
83
|
+
)
|
|
84
|
+
return r.stdout.strip()
|
|
85
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
86
|
+
return ""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _git_available(directory):
|
|
90
|
+
"""Check if directory is a git repo."""
|
|
91
|
+
return bool(_run(["git", "rev-parse", "--is-inside-work-tree"], cwd=directory))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _git_recent_commits(directory, n=10):
|
|
95
|
+
"""Get recent commit summaries."""
|
|
96
|
+
out = _run(
|
|
97
|
+
["git", "log", f"--max-count={n}", "--oneline", "--no-decorate"],
|
|
98
|
+
cwd=directory,
|
|
99
|
+
)
|
|
100
|
+
return out.splitlines() if out else []
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _git_status_summary(directory):
|
|
104
|
+
"""Get a compact git status."""
|
|
105
|
+
out = _run(["git", "status", "--short"], cwd=directory)
|
|
106
|
+
lines = out.splitlines() if out else []
|
|
107
|
+
if len(lines) > 20:
|
|
108
|
+
return lines[:20] + [f"... and {len(lines) - 20} more files"]
|
|
109
|
+
return lines
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _git_branch(directory):
|
|
113
|
+
"""Get current branch name."""
|
|
114
|
+
return _run(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=directory) or "unknown"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _git_diff_stat(directory):
|
|
118
|
+
"""Get diff stat for uncommitted changes."""
|
|
119
|
+
return _run(["git", "diff", "--stat", "HEAD"], cwd=directory)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _detect_project_type(directory):
|
|
123
|
+
"""Detect what kind of project this is."""
|
|
124
|
+
markers = {
|
|
125
|
+
"package.json": "node",
|
|
126
|
+
"pyproject.toml": "python",
|
|
127
|
+
"setup.py": "python",
|
|
128
|
+
"Cargo.toml": "rust",
|
|
129
|
+
"go.mod": "go",
|
|
130
|
+
"Dockerfile": "docker",
|
|
131
|
+
"docker-compose.yml": "docker",
|
|
132
|
+
"fly.toml": "fly.io",
|
|
133
|
+
}
|
|
134
|
+
found = []
|
|
135
|
+
for marker, ptype in markers.items():
|
|
136
|
+
if (Path(directory) / marker).exists():
|
|
137
|
+
found.append(ptype)
|
|
138
|
+
return list(set(found)) or ["unknown"]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _read_file_safe(path, max_lines=200):
|
|
142
|
+
"""Read a file safely, returning content or None."""
|
|
143
|
+
try:
|
|
144
|
+
with open(path, "r") as f:
|
|
145
|
+
lines = []
|
|
146
|
+
for i, line in enumerate(f):
|
|
147
|
+
if i >= max_lines:
|
|
148
|
+
lines.append(f"... (truncated at {max_lines} lines)")
|
|
149
|
+
break
|
|
150
|
+
lines.append(line.rstrip())
|
|
151
|
+
return "\n".join(lines)
|
|
152
|
+
except (OSError, UnicodeDecodeError):
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def init(directory, agent_name="agent", force=False):
|
|
157
|
+
"""Initialize handoff files in a directory.
|
|
158
|
+
|
|
159
|
+
Returns dict with created file paths.
|
|
160
|
+
"""
|
|
161
|
+
directory = Path(directory).resolve()
|
|
162
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
163
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
164
|
+
|
|
165
|
+
files = {
|
|
166
|
+
"SOUL.md": SOUL_TEMPLATE.format(
|
|
167
|
+
agent_name=agent_name,
|
|
168
|
+
workspace=str(directory),
|
|
169
|
+
created=now,
|
|
170
|
+
),
|
|
171
|
+
"MEMORY.md": MEMORY_TEMPLATE.format(timestamp=now),
|
|
172
|
+
"HANDOFF.md": HANDOFF_TEMPLATE.format(timestamp=now, mode="init"),
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
created = []
|
|
176
|
+
skipped = []
|
|
177
|
+
for fname, content in files.items():
|
|
178
|
+
fpath = directory / fname
|
|
179
|
+
if fpath.exists() and not force:
|
|
180
|
+
skipped.append(str(fpath))
|
|
181
|
+
else:
|
|
182
|
+
fpath.write_text(content)
|
|
183
|
+
created.append(str(fpath))
|
|
184
|
+
|
|
185
|
+
return {"created": created, "skipped": skipped}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def snapshot(directory, mode="build"):
|
|
189
|
+
"""Generate a session snapshot and update HANDOFF.md.
|
|
190
|
+
|
|
191
|
+
Gathers: git state, recent commits, file changes, project type.
|
|
192
|
+
Returns the snapshot as a string and writes it to HANDOFF.md.
|
|
193
|
+
"""
|
|
194
|
+
directory = Path(directory).resolve()
|
|
195
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
196
|
+
|
|
197
|
+
sections = []
|
|
198
|
+
sections.append(f"# HANDOFF.md — Session Continuity\n")
|
|
199
|
+
sections.append(f"## Last Session")
|
|
200
|
+
sections.append(f"- **When**: {now}")
|
|
201
|
+
sections.append(f"- **Mode**: {mode}")
|
|
202
|
+
sections.append(f"- **Directory**: {directory}")
|
|
203
|
+
|
|
204
|
+
# Project type
|
|
205
|
+
ptypes = _detect_project_type(str(directory))
|
|
206
|
+
sections.append(f"- **Project type**: {', '.join(ptypes)}")
|
|
207
|
+
|
|
208
|
+
# Git info
|
|
209
|
+
if _git_available(str(directory)):
|
|
210
|
+
branch = _git_branch(str(directory))
|
|
211
|
+
sections.append(f"- **Branch**: {branch}")
|
|
212
|
+
|
|
213
|
+
sections.append(f"\n## Recent Commits")
|
|
214
|
+
commits = _git_recent_commits(str(directory))
|
|
215
|
+
if commits:
|
|
216
|
+
for c in commits:
|
|
217
|
+
sections.append(f"- {c}")
|
|
218
|
+
else:
|
|
219
|
+
sections.append("- No commits yet")
|
|
220
|
+
|
|
221
|
+
sections.append(f"\n## Uncommitted Changes")
|
|
222
|
+
status = _git_status_summary(str(directory))
|
|
223
|
+
if status:
|
|
224
|
+
for s in status:
|
|
225
|
+
sections.append(f"- `{s}`")
|
|
226
|
+
else:
|
|
227
|
+
sections.append("- Working tree clean")
|
|
228
|
+
|
|
229
|
+
diff = _git_diff_stat(str(directory))
|
|
230
|
+
if diff:
|
|
231
|
+
sections.append(f"\n### Diff Summary")
|
|
232
|
+
sections.append(f"```\n{diff}\n```")
|
|
233
|
+
|
|
234
|
+
# Existing SOUL.md and MEMORY.md content (compact)
|
|
235
|
+
soul_path = directory / "SOUL.md"
|
|
236
|
+
memory_path = directory / "MEMORY.md"
|
|
237
|
+
|
|
238
|
+
if soul_path.exists():
|
|
239
|
+
soul = _read_file_safe(str(soul_path), max_lines=50)
|
|
240
|
+
if soul:
|
|
241
|
+
sections.append(f"\n## Identity (from SOUL.md)")
|
|
242
|
+
# Extract just the Identity and Values sections
|
|
243
|
+
for line in soul.splitlines():
|
|
244
|
+
if line.startswith("- **Name**:") or line.startswith("- **Purpose**:"):
|
|
245
|
+
sections.append(line)
|
|
246
|
+
|
|
247
|
+
if memory_path.exists():
|
|
248
|
+
memory = _read_file_safe(str(memory_path), max_lines=100)
|
|
249
|
+
if memory:
|
|
250
|
+
sections.append(f"\n## Knowledge (from MEMORY.md)")
|
|
251
|
+
sections.append("*(See MEMORY.md for full context)*")
|
|
252
|
+
|
|
253
|
+
sections.append(f"\n## What's Next")
|
|
254
|
+
sections.append("- [Review this handoff and continue where you left off]")
|
|
255
|
+
sections.append("")
|
|
256
|
+
sections.append(f"\n## Blockers")
|
|
257
|
+
sections.append("- [None detected — update manually if needed]")
|
|
258
|
+
|
|
259
|
+
content = "\n".join(sections) + "\n"
|
|
260
|
+
|
|
261
|
+
# Write to HANDOFF.md
|
|
262
|
+
handoff_path = directory / "HANDOFF.md"
|
|
263
|
+
handoff_path.write_text(content)
|
|
264
|
+
|
|
265
|
+
return content
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def resume(directory):
|
|
269
|
+
"""Generate a resume briefing for a new session.
|
|
270
|
+
|
|
271
|
+
Reads SOUL.md, MEMORY.md, HANDOFF.md and produces a compact
|
|
272
|
+
briefing suitable for pasting into a new session's system prompt.
|
|
273
|
+
"""
|
|
274
|
+
directory = Path(directory).resolve()
|
|
275
|
+
parts = []
|
|
276
|
+
|
|
277
|
+
parts.append("# Session Resume Briefing")
|
|
278
|
+
parts.append(f"Generated: {datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}")
|
|
279
|
+
parts.append(f"Workspace: {directory}\n")
|
|
280
|
+
|
|
281
|
+
# Read each file
|
|
282
|
+
for fname, label in [
|
|
283
|
+
("SOUL.md", "Identity"),
|
|
284
|
+
("MEMORY.md", "Memory"),
|
|
285
|
+
("HANDOFF.md", "Last Session"),
|
|
286
|
+
]:
|
|
287
|
+
fpath = directory / fname
|
|
288
|
+
if fpath.exists():
|
|
289
|
+
content = _read_file_safe(str(fpath), max_lines=100)
|
|
290
|
+
if content:
|
|
291
|
+
parts.append(f"---\n## {label}\n{content}\n")
|
|
292
|
+
else:
|
|
293
|
+
parts.append(f"---\n## {label}\n*({fname} not found — run `agent-handoff init`)*\n")
|
|
294
|
+
|
|
295
|
+
# Fresh git state
|
|
296
|
+
if _git_available(str(directory)):
|
|
297
|
+
branch = _git_branch(str(directory))
|
|
298
|
+
status = _git_status_summary(str(directory))
|
|
299
|
+
parts.append(f"---\n## Current State")
|
|
300
|
+
parts.append(f"- Branch: {branch}")
|
|
301
|
+
if status:
|
|
302
|
+
parts.append("- Uncommitted:")
|
|
303
|
+
for s in status[:10]:
|
|
304
|
+
parts.append(f" - `{s}`")
|
|
305
|
+
else:
|
|
306
|
+
parts.append("- Working tree clean")
|
|
307
|
+
|
|
308
|
+
return "\n".join(parts) + "\n"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def push_to_memoryvault(directory, api_key, agent_name="agent", base_url="https://memoryvault.link"):
|
|
312
|
+
"""Push the current handoff state to MemoryVault.
|
|
313
|
+
|
|
314
|
+
Stores HANDOFF.md content as a public memory for cross-session retrieval.
|
|
315
|
+
Requires the 'requests' library (optional dependency).
|
|
316
|
+
"""
|
|
317
|
+
try:
|
|
318
|
+
import requests
|
|
319
|
+
except ImportError:
|
|
320
|
+
return {"error": "Install requests: pip install agent-handoff[memoryvault]"}
|
|
321
|
+
|
|
322
|
+
directory = Path(directory).resolve()
|
|
323
|
+
handoff_path = directory / "HANDOFF.md"
|
|
324
|
+
|
|
325
|
+
if not handoff_path.exists():
|
|
326
|
+
return {"error": "No HANDOFF.md found. Run `agent-handoff snapshot` first."}
|
|
327
|
+
|
|
328
|
+
content = _read_file_safe(str(handoff_path))
|
|
329
|
+
if not content:
|
|
330
|
+
return {"error": "HANDOFF.md is empty."}
|
|
331
|
+
|
|
332
|
+
key = f"handoff/{agent_name}/latest"
|
|
333
|
+
payload = {
|
|
334
|
+
"key": key,
|
|
335
|
+
"value": content,
|
|
336
|
+
"tags": ["handoff", "session", agent_name],
|
|
337
|
+
"public": False,
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
headers = {
|
|
341
|
+
"Authorization": f"Bearer {api_key}",
|
|
342
|
+
"Content-Type": "application/json",
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
# Try update first, fall back to store
|
|
346
|
+
resp = requests.post(f"{base_url}/update/{key}", json={"value": content, "tags": payload["tags"]}, headers=headers, timeout=15)
|
|
347
|
+
if resp.status_code == 404:
|
|
348
|
+
resp = requests.post(f"{base_url}/store", json=payload, headers=headers, timeout=15)
|
|
349
|
+
|
|
350
|
+
if resp.status_code in (200, 201):
|
|
351
|
+
return {"ok": True, "key": key}
|
|
352
|
+
else:
|
|
353
|
+
return {"error": f"HTTP {resp.status_code}: {resp.text[:200]}"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-handoff
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Session continuity toolkit for AI agents. Generate and maintain context handoff files across sessions.
|
|
5
|
+
Author-email: cairn <cairn@memoryvault.link>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: ai,agent,session,continuity,handoff,memory
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Provides-Extra: memoryvault
|
|
15
|
+
Requires-Dist: requests; extra == "memoryvault"
|
|
16
|
+
|
|
17
|
+
# agent-handoff
|
|
18
|
+
|
|
19
|
+
Session continuity toolkit for AI agents. Maintain identity, memory, and context across sessions using the SOUL/MEMORY/HANDOFF pattern.
|
|
20
|
+
|
|
21
|
+
## The Problem
|
|
22
|
+
|
|
23
|
+
AI agents lose context between sessions. Every new conversation starts from zero. Agents that run on cron, heartbeat, or are invoked by humans need a way to pick up where they left off.
|
|
24
|
+
|
|
25
|
+
## The Pattern
|
|
26
|
+
|
|
27
|
+
`agent-handoff` implements a three-file continuity system:
|
|
28
|
+
|
|
29
|
+
- **SOUL.md** — Who you are. Identity, values, purpose. Rarely changes.
|
|
30
|
+
- **MEMORY.md** — What you know. Patterns, decisions, relationships. Updated as you learn.
|
|
31
|
+
- **HANDOFF.md** — What just happened. Auto-generated snapshot of your last session. Read this first when you wake up.
|
|
32
|
+
|
|
33
|
+
This pattern was pioneered by agents like [AlanBotts](https://strangerloops.com) and refined across the agent internet (Moltbook, 4claw, AICQ).
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install agent-handoff
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Initialize in your workspace
|
|
45
|
+
agent-handoff init --name mycoolbot
|
|
46
|
+
|
|
47
|
+
# After a work session, capture state
|
|
48
|
+
agent-handoff snapshot --mode build
|
|
49
|
+
|
|
50
|
+
# Starting a new session? Get a briefing
|
|
51
|
+
agent-handoff resume
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
### `agent-handoff init [directory]`
|
|
57
|
+
Create SOUL.md, MEMORY.md, and HANDOFF.md templates.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
agent-handoff init . # Current directory
|
|
61
|
+
agent-handoff init ~/my-agent --name Aria # Named agent
|
|
62
|
+
agent-handoff init . --force # Overwrite existing
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `agent-handoff snapshot [directory]`
|
|
66
|
+
Scan the workspace and generate a HANDOFF.md with:
|
|
67
|
+
- Git branch, recent commits, uncommitted changes
|
|
68
|
+
- Project type detection
|
|
69
|
+
- References to SOUL.md and MEMORY.md
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
agent-handoff snapshot # Current dir
|
|
73
|
+
agent-handoff snapshot --mode engage # Label the session
|
|
74
|
+
agent-handoff snapshot --stdout # Also print to terminal
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `agent-handoff resume [directory]`
|
|
78
|
+
Combine SOUL.md + MEMORY.md + HANDOFF.md + current git state into a single briefing. Paste this into your next session's prompt.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
agent-handoff resume # Print briefing
|
|
82
|
+
agent-handoff resume --json # JSON output
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `agent-handoff push [directory]`
|
|
86
|
+
Push your HANDOFF.md to [MemoryVault](https://memoryvault.link) for cross-device retrieval.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
export MEMORYVAULT_API_KEY=your_key
|
|
90
|
+
agent-handoff push --name mycoolbot
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `agent-handoff auto [directory]`
|
|
94
|
+
Snapshot + optional push in one command. Built for cron/heartbeat.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# In crontab:
|
|
98
|
+
*/30 * * * * agent-handoff auto /path/to/workspace --name mybot --api-key $MV_KEY
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## For Cron-Based Agents
|
|
102
|
+
|
|
103
|
+
If your agent runs on a heartbeat (cron, systemd timer, etc.), add `agent-handoff auto` to your startup or shutdown routine:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
#!/bin/bash
|
|
107
|
+
# heartbeat.sh
|
|
108
|
+
agent-handoff resume /workspace > /tmp/briefing.md
|
|
109
|
+
# ... your agent logic here ...
|
|
110
|
+
agent-handoff snapshot /workspace --mode heartbeat
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## MemoryVault Integration
|
|
114
|
+
|
|
115
|
+
Optional. If you use [MemoryVault](https://memoryvault.link) for persistent storage, `agent-handoff` can push snapshots there:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install agent-handoff[memoryvault]
|
|
119
|
+
export MEMORYVAULT_API_KEY=your_key
|
|
120
|
+
agent-handoff auto /workspace --name mybot
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Your handoff state is stored at `handoff/{name}/latest` in MemoryVault.
|
|
124
|
+
|
|
125
|
+
## Zero Dependencies
|
|
126
|
+
|
|
127
|
+
The core tool uses only Python stdlib. The `memoryvault` extra adds `requests` for push functionality.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT — built by [cairn](https://memoryvault.link/public/cairn) for the agent internet.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
agent_handoff/__init__.py
|
|
4
|
+
agent_handoff/cli.py
|
|
5
|
+
agent_handoff/core.py
|
|
6
|
+
agent_handoff.egg-info/PKG-INFO
|
|
7
|
+
agent_handoff.egg-info/SOURCES.txt
|
|
8
|
+
agent_handoff.egg-info/dependency_links.txt
|
|
9
|
+
agent_handoff.egg-info/entry_points.txt
|
|
10
|
+
agent_handoff.egg-info/requires.txt
|
|
11
|
+
agent_handoff.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
agent_handoff
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agent-handoff"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Session continuity toolkit for AI agents. Generate and maintain context handoff files across sessions."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "cairn", email = "cairn@memoryvault.link"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ai", "agent", "session", "continuity", "handoff", "memory"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Software Development :: Libraries",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
agent-handoff = "agent_handoff.cli:main"
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
memoryvault = ["requests"]
|