Trigger persistent Letta agents from file changes.
Hypervigilant watches selected local files and sends each saved change to a persistent Letta Agent SDK conversation. Text files produce unified diffs. Binary files produce metadata events.
The agent can review changes, use attached Letta tools, or use guarded local file tools. Conversations and delivered-file state survive watcher restarts.
- Bun 1.2 or newer
- Node.js 22.19 or newer
- A Letta Cloud account, a configured local model provider, or a self-hosted Letta App Server
Clone the repository and install its dependencies:
git clone https://github.com/letta-ai/hypervigilant.git
cd hypervigilant
bun install
cp .env.example .envSet LETTA_API_KEY=sk-let-... in .env. Then create an empty watched directory and configure an agent:
mkdir -p ~/hypervigilant-demo
bun run dev -- init ~/hypervigilant-demo --create-agentStart the watcher:
bun run dev -- watch ~/hypervigilant-demoThe first run records existing files without sending them. Keep the watcher running. In another terminal, create a watched file:
printf '# First change\n' > ~/hypervigilant-demo/README.mdHypervigilant sends the addition to the agent. Later saves resume the same conversation.
If you already have an agent, configure it without prompts:
bun run dev -- init ~/hypervigilant-demo \
--non-interactive \
--agent-id agent-xxx \
--project hypervigilant-demoTo send files that already exist in the directory, run one scan:
bun run dev -- scan ~/hypervigilant-demoA scan sends every current file selected by the configuration and exits. A later watcher reuses the saved snapshots and conversation routes. Another scan sends the selected files again.
Build and link the command to use hypervigilant directly:
bun run build
bun link
hypervigilant helpHypervigilant uses the Agent SDK across three deployment shapes. The configured backend owns both agent identity and conversation routes; switching it resets saved routes without discarding file snapshots.
Cloud is the default. Agent state lives in Letta Cloud while an SDK-owned local App Server executes guarded filesystem tools on the watcher machine:
[connection]
backend = "cloud"Set LETTA_API_KEY=sk-let-... in the watched project's .env, the invocation directory's .env, or the process environment.
This mode does not use a managed sandbox. Hypervigilant keeps agent state in Letta Cloud but runs its App Server, guarded tools, and real project cwd on the current device. Run the behavioral proof in demo/cloud-local-device.
Local mode starts one command-scoped App Server with the Letta local backend. Agent state, model access, and filesystem tools remain on the watcher machine. The server is reused for setup and delivery, then stopped when the scan or watcher exits. Configure a local provider in Letta Code first, then initialize Hypervigilant without a Cloud key:
letta --backend local connect ollama
bun run dev -- init /path/to/project --backend local --create-agent --non-interactiveThe resulting configuration contains:
[connection]
backend = "local"Local agents can use only models exposed by the local backend. letta/auto is a Cloud router, not a client-side policy for choosing among local models.
Remote mode connects directly to a Letta App Server you operate:
letta server --backend local --listen ws://127.0.0.1:4500
bun run dev -- init /path/to/project \
--backend remote \
--server-url ws://127.0.0.1:4500 \
--agent-id agent-local-xxx \
--mode review \
--non-interactive[connection]
backend = "remote"
url = "ws://127.0.0.1:4500"
shared_filesystem = falseRemote connections are diff-only by default. Hypervigilant sends saved diffs but exposes none of its managed filesystem tools because those tools would execute on the App Server machine rather than necessarily against the watched checkout. Tools attached directly to the selected agent remain governed by their App Server rules and must be configured independently.
Set shared_filesystem = true or pass --shared-filesystem only when the App Server sees the watched project at the same absolute path. With worktree mode, the generated isolated worktree must also exist at the same absolute path. That explicit contract enables the normal read and edit tools.
Non-loopback App Servers require TLS and bearer authentication. Keep the token outside the configuration:
[connection]
backend = "remote"
url = "wss://agents.example.com"
auth_token_env = "LETTA_APP_SERVER_TOKEN"
shared_filesystem = falseHypervigilant resolves the named variable from the same project-first .env chain and sends it as the WebSocket bearer token. It never stores the token in configuration or state. Plaintext and unauthenticated remote connections are accepted only for loopback hosts.
- Sends existing matching files once or watches for later changes.
- Resumes one project conversation, one conversation per file, or named specialist conversations.
- Gives the default conversation guarded local file tools under
review,ask, oryolopolicy. - Lets attached Letta tools react to a change through their existing tool rules.
- Hypervigilant records a baseline for files that match the configured globs.
- It batches additions, changes, and deletions. Repeated saves collapse to the latest content.
- It sends text diffs or binary metadata to the configured conversation routes.
- It advances each snapshot only after all required deliveries succeed.
- It suppresses agent file writes from the watcher and records their final state.
Hypervigilant detects changes made while it was stopped at the next startup. Agent turns run sequentially.
scan uses the same file selection, routes, permissions, and state. It presents each current file as an add event. Before delivery, it prints file, byte, and estimated token totals. It blocks scans above the configured file or text-byte limits. After success, it removes stale matching snapshots without reporting deletion events.
One configuration selects one model. Run multiple configurations when files need different models or instructions.
Each configuration must use a separate state_dir. Use non-overlapping include globs when each file must go to only one model.
The model-routing demo starts two watchers with the same agent:
bun demo/model-routing/run.ts --agent-id agent-xxxThe code watcher uses letta/auto. The notes watcher uses openai/gpt-5.6-luna for lower-cost triage. Each watcher owns a separate conversation.
See demo/model-routing for the generated configurations, sample edits, and model override flags.
This configuration sends new images to an image-capable agent. The agent records descriptions and tags in catalog.md.
version = 1
project = "image-inbox"
agent_id = "agent-xxx"
include = ["**/*.png", "**/*.jpg", "**/*.jpeg", "**/*.webp"]
exclude = [".hypervigilant/**"]
max_file_size_bytes = 25000000
mode = "edit"
routing = "project"
instructions = "Use ViewImage for each added or changed image. Update catalog.md with its path, description, category, and tags."
[tools]
auto_allow = ["ViewImage"]Start the watcher before you add images. If catalog writes must run without approval, use the following command:
hypervigilant permissions yolo /path/to/image-inboxauto_allow gives ViewImage its native path access. If each image read needs approval, replace it with ask = ["ViewImage"].
Hypervigilant reports image events but does not move files. File moves need a guarded client tool or an attached Letta tool.
version = 1
project = "notes"
agent_id = "agent-xxx"
include = ["**/*.md"]
exclude = [".obsidian/**", ".trash/**", ".hypervigilant/**"]
mode = "edit"
routing = "per-file"
instructions = "Keep each note accurate and internally consistent. Preserve the author's voice."Each note gets a persistent conversation. The default edit policy asks before each agent write.
version = 1
project = "application"
agent_id = "agent-xxx"
include = ["src/**", "tests/**", "docs/**", "*.md"]
exclude = ["node_modules/**", "dist/**", ".git/**", ".hypervigilant/**"]
mode = "edit"
routing = "project"
instructions = "Review changes in project context. Fix concrete regressions without unrelated refactors."
[worktree]
enabled = true
auto_commit = true
branch_prefix = "hypervigilant"Hypervigilant runs local file tools in a linked worktree. It commits each successful batch for an explicit merge into the source checkout.
Watch source files and let the agent append a summary to CHANGES.md for each batch.
version = 1
project = "changelog"
agent_id = "agent-xxx"
include = ["src/**", "tests/**"]
exclude = ["node_modules/**", ".git/**", ".hypervigilant/**", "CHANGES.md"]
mode = "edit"
routing = "project"
instructions = "Append a one-line entry to CHANGES.md for each batch. Describe what changed and why."The agent writes to CHANGES.md in the watched root. Hypervigilant suppresses the resulting watcher event, so the changelog update does not trigger another delivery.
Add the following rules to an existing configuration:
[[prompt_rules]]
name = "security-review"
match = ["src/auth/**", "infra/**", "**/*.tf"]
events = ["add", "change", "delete"]
prompt = "Review authentication, authorization, secret handling, and trust boundaries."
conversation = "security"
[[prompt_rules]]
name = "test-review"
match = ["src/**"]
prompt = "Check whether this change has adequate regression coverage."
conversation = "tests"Hypervigilant creates each named conversation on its first match and resumes it later. Named conversations can use attached tools but cannot modify local files.
Test rule selection without contacting an agent:
hypervigilant prompts list /path/to/project
hypervigilant prompts test src/auth/login.ts \
--event change \
--project /path/to/projectHypervigilant reads hypervigilant.toml from the watched project root. A configuration requires the following three fields:
version = 1
project = "my-project"
agent_id = "agent-xxx"Add model to select a model for the project:
model = "letta/auto"The setting applies to every project, per-file, and named conversation in one configuration. It does not change the agent default or unrelated conversations.
If you omit model, each watcher conversation keeps its existing effective model. A prompt_rule cannot override the setting.
Run another configuration when one prompt or file group needs a different model.
The remaining fields have defaults. See the following files for the complete configuration:
hypervigilant.example.tomlcontains every common section.hypervigilant.schema.jsondescribes each accepted key.
Legacy hypervigilant.json files load only when no TOML file exists.
| Configuration key | Default | Purpose |
|---|---|---|
model |
inherited | Select the model for Hypervigilant-owned conversations only. |
include |
Markdown and text files | Select project-relative paths. |
exclude |
Git, node_modules, and state paths |
Remove matching paths from the watch set. |
max_file_size_bytes |
1048576 |
Skip files above this size. |
max_scan_files |
100 |
Block a scan with more selected files. |
max_scan_text_bytes |
65536 |
Block a scan with more total text bytes. |
[connection] |
cloud |
Select Cloud, fully local, or user-managed App Server agent state. |
mode |
edit |
Select read-only or approval-gated local file tools. |
routing |
project |
Use one project conversation or one conversation per file. |
state_dir |
.hypervigilant |
Store private snapshots and route IDs. |
[batching] |
debounce |
Control when saved changes form a batch. |
[tools] |
no extra tools | Expose selected Agent SDK client tools. |
[worktree] |
disabled | Run local file tools on an isolated Git branch. |
[[prompt_rules]] |
none | Add path-specific prompts and named conversations. |
include and exclude use project-relative globs. Hypervigilant does not follow symbolic links.
Text files produce unified diffs. A file with a null byte in its first 8 KiB produces a metadata-only binary event.
Batching supports the following strategies:
debouncewaits for inactivity and enforces a maximum wait.fixed-windowcollects changes for a fixed interval.immediateflushes changes on the next microtask.
Prompt rules match add, change, and delete events. They add message text but cannot grant tools or change permissions.
Restart the watcher after you change project configuration. Each scan loads the current configuration before delivery.
Hypervigilant uses the following local file policies:
| Policy | Local file tools | Approval |
|---|---|---|
review |
Read, LS, Glob, and Grep |
No file mutations. |
ask |
Read, LS, Glob, Grep, Edit, and Write |
Ask before each mutation. |
yolo |
Read, LS, Glob, Grep, Edit, and Write |
Approve guarded mutations automatically. |
The configuration mode review selects the review policy. The mode edit selects the ask policy.
Change the runtime policy without restarting the watcher:
hypervigilant permissions status /path/to/project
hypervigilant permissions review /path/to/project
hypervigilant permissions ask /path/to/project
hypervigilant permissions yolo /path/to/project
hypervigilant permissions reset /path/to/projectHypervigilant confines managed Edit and Write calls to file paths inside the watched root. It denies control files, Git metadata, and symbolic-link paths.
The local toolset starts from base: "none". Hypervigilant denies shell tools, process tools, subagent delegation, interactive input, memory mutation, and alternate file editors.
The [tools] section can expose other tools bundled with the local Letta Code runtime:
[tools]
auto_allow = ["ViewImage"]
ask = []auto_allowapproves every call to the named tool.askrequests approval for each call.- Ask prompts show the tool name but hide its input because the input can contain secrets.
- Extra tools keep their native authority. Watched-root guards apply only to managed
EditandWritecalls.
Tool names must exist in the active Letta Code runtime. An unknown name stops delivery before the agent processes the change.
TaskCreate, TaskGet, TaskList, TaskUpdate, and TodoWrite manage session metadata. They do not launch subagents. write_artifact_file keeps its native ~/.letta/artifacts boundary.
Tools attached to the selected Letta agent remain under their Letta tool rules. The [tools] section does not select or restrict attached tools.
The Hypervigilant terminal cannot answer approvals from attached server tools. Configure attached-tool rules before an unattended run.
YOLO applies only to managed Edit and Write calls. It does not approve tools under ask or change attached-tool rules.
Enable worktree mode with --worktree during initialization or set worktree.enabled = true.
The source repository must have a commit and a clean checkout. Hypervigilant creates one linked worktree and branch outside the source repository.
Use the following commands to inspect and finish worktree changes:
hypervigilant worktree status /path/to/project
hypervigilant worktree merge /path/to/project
hypervigilant worktree cleanup /path/to/projectNormal cleanup requires a clean worktree and a branch merged into the source HEAD. If you intend to delete unmerged work, add --discard:
hypervigilant worktree cleanup /path/to/project --discardHypervigilant runs normal Git hooks. It does not push branches, bypass hooks, or change Git configuration.
Agents and conversations live in the configured backend. Cloud mode runs guarded file tools through an SDK-owned local App Server. Local mode keeps state and execution on the watcher machine. Remote mode uses the user-managed App Server and remains diff-only unless a same-path shared filesystem is explicit.
Hypervigilant stores private state under .hypervigilant/ by default:
- Full text, hash, and size for each delivered text file
- Hash, size, and file kind for each delivered binary file
- Project, per-file, and named conversation IDs
- Permission overrides, worktree metadata, and process locks
Keep the state directory private. Hypervigilant excludes its default state directory from Git.
Cloud API keys and explicitly named remote bearer-token variables use this lookup order:
- The watched project's
.env - The invocation directory's
.env - The ambient environment
Cloud keys must start with sk-let-. Remote bearer tokens have no prefix requirement. Hypervigilant passes credentials to the selected transport but does not store their values in configuration or state.
The following rules protect delivery and local files:
- Failed deliveries do not advance affected snapshots.
- Ambiguous post-send failures do not retry automatically.
- Agent file writes do not trigger feedback deliveries.
- Binary event prompts and snapshots do not contain file bytes.
- Worktree commits contain only delivered paths and approved mutation paths.
- Process locks prevent overlapping worktree scans, watchers, merges, and cleanup.
cloud-local-device proves that a Cloud agent can execute on the current computer without a managed sandbox. The agent must read a random marker from an excluded local-only file; the script verifies the response and archives the temporary Cloud conversation.
export LETTA_API_KEY=sk-let-...
bun demo/cloud-local-device/run.ts --agent-id agent-xxxThe Doc turns one browser-edited PROJECT.md file into a persistent project interface:
bun run demo:the-docThe demo sends each saved change to an agent. Verified agent edits appear in the open editor without a page reload.
Spec Guardian reviews TypeScript changes against a local SPEC.md. It demonstrates prompt rules, named reviewers, approvals, YOLO, and worktree lifecycle commands.
hypervigilant init [path] Create project configuration
hypervigilant scan [path] Send matching files once and exit
hypervigilant status [path] Show read-only configuration and state overview
hypervigilant watch [path] Start the watcher
hypervigilant watch [path] --config FILE Use another config file
hypervigilant permissions ... Inspect or change runtime policy
hypervigilant prompts list ... List prompt rules and routes
hypervigilant prompts test ... Test rule matching without an agent
hypervigilant worktree status|merge|cleanup Manage the isolated worktree
hypervigilant version Print the installed version
hypervigilant help Show all commands and flags
Show configuration, file selection, snapshot state, routing, and worktree status without contacting the Letta API or mutating state:
hypervigilant status /path/to/project- Binary classification checks for null bytes in the first 8 KiB. Hypervigilant can treat other binary formats as text.
- A rename appears as a deletion and an addition.
- Batching reports the net change instead of each intermediate save.
- With project routing, a scan sends all matching files to the default conversation in one turn.
- Prompt rules and tool configuration load when each scan or watcher starts.
- Agent turns and named specialist turns run sequentially.
- One watcher and one isolated worktree can own a state directory.
- The JSON state store rewrites stored text snapshots as one file. Storage cost grows with total watched text.
Run the complete local checks:
bun install
bun run qualityquality runs the specification, type, lint, unit, build, demo, dependency-audit, package-manifest, and staged/unstaged diff gates. It also fails if the gate rewrites generated build or specification output that was already present when the run began; inspect and retain that output, then rerun it. Creating ignored build output in a fresh checkout is expected.
The opt-in live suite creates a real Agent SDK session and archives its temporary conversations:
HYPERVIGILANT_TEST_AGENT_ID=agent-xxx \
LETTA_API_KEY=... \
bun run test:liveApache-2.0