Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
cae72da
Excalidraw component for writing machine
siddug Aug 31, 2025
f697e73
Adding a full page scribbling notepage for every note
siddug Sep 5, 2025
060e115
nit css
siddug Sep 5, 2025
1ae7d12
Collapsible sidebar! Toggleable menubar in notes
siddug Sep 5, 2025
3c2508c
Adding 2 new APIs to help with building a read-only app
siddug Oct 8, 2025
a6c0b9c
adding new listgo mobile repo
siddug Oct 8, 2025
65ab223
Adding widget auth
siddug Oct 9, 2025
fb96e7b
listgo v1
siddug Oct 9, 2025
c0568de
Bug fix
siddug Oct 9, 2025
d87f083
new api
siddug Oct 9, 2025
26afe68
list -> locus and locus-mobile
siddug Oct 10, 2025
4beb2dd
nit
siddug Oct 10, 2025
1427bb9
Merge branch 'main' of https://github.com/btw-so/btw into ft-writing-…
siddug Oct 27, 2025
f357872
Adding new APIs and routes so that I can embed editable note in apps
siddug Oct 29, 2025
daccfaa
nit fix
siddug Oct 29, 2025
e261f59
Trying some fix for private auth in production not working
siddug Oct 29, 2025
9e4747e
testing
siddug Oct 29, 2025
dea1762
another fix
siddug Oct 29, 2025
9ae42a1
#life
siddug Oct 29, 2025
f0de906
#life
siddug Oct 29, 2025
df5fa56
debug
siddug Oct 30, 2025
a2c200e
figured out fingerprint issue
siddug Oct 30, 2025
727f7c0
final fixes
siddug Oct 30, 2025
2ed0e88
faster private note
siddug Oct 30, 2025
f5fd0cf
easy way to do private ntoes
siddug Oct 30, 2025
8c1baa6
bug
siddug Oct 30, 2025
953312e
bug
siddug Oct 30, 2025
25f88be
nit
siddug Oct 30, 2025
42dc895
nit
siddug Oct 30, 2025
4cdc9ff
new scribble apis
siddug Oct 30, 2025
9608706
new scribble apis
siddug Oct 30, 2025
cf9b7ae
new scribble apis
siddug Oct 30, 2025
0bf7ef9
nit
siddug Oct 30, 2025
c0b7fcd
nit
siddug Oct 30, 2025
d30dd7d
Merging
siddug Nov 4, 2025
ff024e5
increasing max req size
siddug Nov 6, 2025
80ccc8c
locus-apps (monorepo for ios ipados and macos)
siddug Nov 8, 2025
19e57d5
adding new APIs for backups
siddug Nov 11, 2025
6e866bd
nit bug fixes
siddug Nov 11, 2025
d88c242
adding locus-apps folder instead of submodule.
siddug Nov 12, 2025
b5ba1cb
Backups for DB data of a user works now
siddug Nov 12, 2025
371818a
incremental sync + local first note taking features
siddug Nov 12, 2025
3d52b82
nit bug
siddug Nov 12, 2025
495a30e
removing local first approach. have to rethink
siddug Nov 12, 2025
30fd662
Adding new api
siddug Nov 17, 2025
e324657
nit
siddug Nov 17, 2025
94febe3
nit pinned nots paginated
siddug Nov 17, 2025
3d72404
nit
siddug Nov 18, 2025
395af74
Dash view for web and macos
siddug Nov 19, 2025
656b908
nit-n
siddug Nov 19, 2025
fb1f52e
nit
siddug Nov 19, 2025
2962cae
much netter
siddug Nov 19, 2025
6fb735a
nit css
siddug Nov 19, 2025
de5f99b
Add A1 agent system with tool-calling, voice transcription, and Teleg…
siddug Feb 16, 2026
ee52a10
Add Pro subscription with Stripe, Hetzner sandbox VMs, and sandbox tools
siddug Feb 18, 2026
18f3dbc
Split long Telegram messages into chunks and fix postgres password pe…
siddug Feb 18, 2026
d97f3c0
Add ElevenLabs text-to-speech tool for Telegram voice messages
siddug Feb 19, 2026
6602a52
Add agentic tasks system with scheduled autonomous agent loops, user …
siddug Feb 22, 2026
8857645
Add heartbeat system, onboarding tools, features guide, and Telegram …
siddug Feb 22, 2026
ca3196f
Fix heartbeat sending reminder digest messages by adding silent mode …
siddug Feb 22, 2026
f2bc4ac
Add web frontend app and backend API for phone OTP auth and chat
siddug Feb 24, 2026
3349a43
Security hardening: P0/P1 fixes across backend, web, and iOS
siddug Feb 25, 2026
c40039e
Add a1-app prod Dockerfile and security env vars to dev compose
siddug Feb 25, 2026
bf54889
Send OTP via Telegram for existing users, fall back to SMS for new users
siddug Feb 25, 2026
d0924d7
Redesign a1-web UI and add sandbox file browser
siddug Feb 28, 2026
616117d
Fix scheduled tasks missing sandbox tools due to undeckrypted SSH key
siddug Mar 2, 2026
a02d11a
Fix blank pages in writer and locus after login
siddug Mar 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
46 changes: 30 additions & 16 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
list/.tmp
list/.sass-cache
list/.publish
list/coverage/
list/build/
list/dist/
list/node_modules/
list/reports/
list/webpack.stats.json
list/.DS_Store

list/out/
locus/.tmp
locus/.sass-cache
locus/.publish
locus/coverage/
locus/build/
locus/dist/
locus/node_modules/
locus/reports/
locus/webpack.stats.json
locus/src/enterprise
locus/.DS_Store

locus/out/


writer/.tmp
writer/.sass-cache
Expand All @@ -20,6 +22,7 @@ writer/dist/
writer/node_modules/
writer/reports/
writer/webpack.stats.json
writer/src/enterprise
writer/.DS_Store

writer/out/
Expand All @@ -33,7 +36,18 @@ publisher/.DS_Store
deployment/
deployment/.DS_Store

.DS_Store

cors-config.json
migration_*.sql
locus-apps/build/
locus-apps/DerivedData/
locus-apps/**/*.xcuserstate
locus-apps/**/xcuserdata/
locus-apps/*.xcworkspace
locus-apps/Pods/
locus-apps/.DS_Store
locus-apps/*.swp
locus-apps/*.swo
locus-apps/**/*.hmap
locus-apps/**/*.ipa
locus-apps/**/*.dSYM
locus-apps/**/*.dSYM.zip

.DS_Store
202 changes: 202 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# BTW Project — Agent Guide

## Quick Reference

```bash
# Rebuild & deploy
sg docker "docker compose -f deploy/docker-compose.dev.yml up -d --build tasks"

# Restart without rebuild (only if files are volume-mounted, which they aren't)
sg docker "docker exec btw-tasks pm2 restart tasks"

# View logs
sg docker "docker exec btw-tasks pm2 logs tasks --lines 200 --nostream"

# DB access
sg docker "docker exec btw-postgres psql -U postgres -d btw"

# Run a query
sg docker "docker exec btw-postgres psql -U postgres -d btw -c \"SELECT ...\""
```

## Architecture Overview

**Stack**: Node.js/Express, PostgreSQL (raw SQL, `btw` schema), Redis + Bull queues, Docker Compose

```
tasks/
├── app.js # Express app setup, middleware, route mounting
├── bin/www # HTTP server entry point
├── logic/ # Business logic (agent, tools, integrations)
│ ├── agent.js # Core agent loop (runAgentLoop), system prompt, model fallback
│ ├── agenticTaskTools.js # Scheduled task CRUD tools + scheduling helpers
│ ├── messageRouter.js # Routes incoming messages to tasks, LLM classification
│ ├── entryPoints.js # Platform abstraction (telegram, whatsapp send/receive)
│ ├── tools.js # Base agent tools (reminders, web search, image gen, etc.)
│ ├── sandboxTools.js # Sandbox VM tools (bash, read, write, edit, grep, glob, todo)
│ ├── toolAdapter.js # Adapts legacy tools to pi-agent-core format + approval flow
│ ├── memoryTools.js # User memory CRUD (soul, global, daily)
│ ├── customTools.js # User-defined tools loaded from sandbox ~/.a1/tools/
│ ├── skills.js # User-defined skills loaded from sandbox ~/.a1/skills/
│ ├── mcpClient.js # MCP server connections from sandbox
│ ├── mcpManagementTools.js # MCP server add/remove tools
│ ├── telegram.js # Telegram API helpers, markdown→HTML converter
│ ├── whatsapp.js # WhatsApp API helpers
│ ├── approval.js # Tool approval flow (Redis polling, inline buttons)
│ ├── sandbox.js # Hetzner VM provisioning/management
│ ├── subscription.js # Stripe subscription logic
│ ├── ai.js # LLM calls for reminder parsing, recurring alerts
│ ├── transcribe.js # Voice message transcription
│ └── user.js # User lookup helpers
├── routes/
│ ├── telegram.js # Telegram webhook handler (main user interaction entry)
│ ├── jobs.js # Bull queue processors (alerts, reminders, sandbox, agentic tasks)
│ ├── stripe.js # Stripe webhook (mounted BEFORE express.json() for raw body)
│ └── ... # Other REST routes (files, notes, lists, etc.)
├── services/
│ ├── db.js # PostgreSQL connection pool
│ ├── redis.js # Redis client (database 3)
│ ├── queue.js # Bull queues (database 2): base, alerts, ux, sandbox, agentic
│ ├── ssh.js # SSHSession class for sandbox VM access
│ ├── hetzner.js # Hetzner Cloud API for VM provisioning
│ ├── stripe.js # Stripe SDK setup
│ └── twilio.js # Twilio for phone calls
├── migrations/ # SQL migration files (run manually)
└── utils/utils.js # Date/time formatting, timezone conversion
```

## Key Patterns

### Database Access
```js
const db = require("../services/db");
const tasksDB = await db.getTasksDB();
const { rows } = await tasksDB.query(`SELECT * FROM btw.table WHERE id = $1`, [id]);
```
- All tables in `btw` schema
- Raw SQL via `pg`, no ORM
- User settings: `btw.users.settings` (JSON column, not JSONB)

### Tool Definitions (Legacy Format)
```js
{
name: "tool_name",
description: "What it does",
parameters: Type.Object({
param: Type.String({ description: "..." }),
}),
requiresApproval: true, // optional, triggers approval flow
execute: async (_toolCallId, args) => {
return { output: JSON.stringify({ success: true, ... }) };
},
}
```
Tools are wrapped by `toolAdapter.js` into pi-agent-core's `AgentTool` format.

### Bull Queue Pattern
```js
const { myQueue } = require("../services/queue");

// Register processor
myQueue.process("job-name", async (job, done) => {
const { data } = job.data;
// ... do work ...
done(); // or done(err)
});

// Add job
myQueue.add("job-name", { data }, {
delay: 5000, // optional delay in ms
jobId: "unique-id", // optional dedup key
removeOnComplete: true,
removeOnFail: true,
});
```

### pi-ai / pi-agent-core Library
- `getModel(provider, model)` returns a config object (NOT callable)
- `completeSimple(model, { systemPrompt, messages })` for simple text generation
- `Agent` class for tool-calling loops:
```js
const agent = new Agent({
initialState: { systemPrompt, model, tools, messages, thinkingLevel: "off", transformContext },
});
agent.subscribe((event) => { /* turn_start, tool_execution_start/end, agent_end */ });
await agent.continue(); // runs the loop
await agent.waitForIdle();
```
- `event.messages` in `agent_end` contains only NEW messages from the agent, NOT the initial ones. Always combine: `[...finalMessages, ...event.messages]`

## Agentic Tasks System

Every user interaction is an "agentic task" with persistent message history.

### Flow
1. **Incoming message** → `routes/telegram.js` → `messageRouter.routeMessage()`
2. Router finds existing task (via reply-to or recent active task) or creates new one
3. Task's saved messages loaded from `btw.agentic_tasks.messages` (JSONB)
4. `agent.js:runAgentLoop()` runs with full message history
5. After agent completes, messages saved back via `messageRouter.saveTaskState()`
6. Bot response linked to task via `btw.entry_point_messages` for reply-to routing

### Scheduled (Auto) Tasks
- Created via `create_agentic_task` tool with `cron_expression` and optional `end_at` deadline
- Stored in `btw.agentic_tasks` with `mode = 'auto'`, `status = 'active'`
- **Chain scheduling**: task creation schedules first delayed Bull job → after each run, next run is scheduled as another delayed job
- **Failsafe poll**: 60-second repeating job (`check-due-tasks`) catches any missed runs
- **Dedup**: `jobId = agentic-run-${taskId}` (stable per task, no timestamp) prevents double-firing
- **Deadline**: `end_at` column checked before each run and before scheduling next run; auto-completes when passed
- **Self-termination**: agent has `stop_this_task` tool during scheduled runs
- **Workspace**: pro+sandbox tasks get `/root/tasks/task_${id}/` directory, agent reads/writes `PROGRESS.md` for cross-run context
- All scheduling in `routes/jobs.js`, tool definitions in `logic/agenticTaskTools.js`

### DB Tables
- `btw.agentic_tasks` — task definition, message history, schedule, workspace
- `btw.task_runs` — audit log of each run (started_at, completed_at, status, error)
- `btw.entry_point_messages` — maps platform message IDs to tasks for reply-to routing

## Pro / Sandbox System

- Pro users get a Hetzner VM (Ubuntu 24.04, 2 vCPU, 4GB RAM)
- SSH access via `services/ssh.js`: `new SSHSession({ host, privateKey })` → `.connect()` → `.exec(cmd)` → `.close()`
- Sandbox tools require approval in manual chat (`chatId` triggers approval flow), auto-approved in scheduled runs (`chatId = null`)
- VM lifecycle: `provision-sandbox` → `check-sandbox-ready` (polling) → ready; `destroy-sandbox` for cleanup
- User extensions loaded from sandbox: skills (`~/.a1/skills/`), custom tools (`~/.a1/tools/`), MCP servers (DB config)

## Agent System Prompt Structure

Built by `agent.js:buildSystemPrompt()`. Sections:
1. Base persona (A1/Baymax) + current date/time + user timezone
2. Family members (if any)
3. Capabilities list (reminders, web search, image gen, etc.)
4. Sandbox section (if pro+sandbox)
5. Platform section (Telegram commands, formatting)
6. Extensions (skills, custom tools, MCP servers)
7. Memories (soul, global, daily)
8. Agentic task section (if scheduled run: instruction, workspace, stop_this_task)

## Telegram Integration

- Webhook at `routes/telegram.js`, handles text, voice, photos, contacts, callbacks
- `logic/telegram.js` has `markdownToTelegramHTML()` — converts standard markdown to Telegram HTML
- Fallback: if Telegram rejects HTML (`can't parse`), retries with tags stripped
- Long messages split into chunks via `splitMessage()`

## Timezone Handling

- User timezone stored as `settings.timezoneOffsetInSeconds` (seconds from UTC)
- Cron expressions are in user's LOCAL time
- Cron parsing: feed parser local "now" (`UTC + offset`), convert result back to UTC (`nextLocal - offset`)
- All DB timestamps are in UTC (`TIMESTAMPTZ`)
- Display: `utils.js` has `getDDMMYYYYFromUTCToLocal()`, `getHHMMSSFromUTCToLocal()`, etc.

## Common Pitfalls

1. **Stripe webhook needs raw body** — route mounted BEFORE `express.json()` in `app.js`
2. **Gemini requires alternating roles** — consecutive same-role messages must be merged
3. **`event.messages` from agent_end is only NEW messages** — combine with initial messages for full history
4. **Bull jobId must be stable for dedup** — don't include timestamps in jobId if you want dedup
5. **Settings column is `json` not `jsonb`** — can't use Postgres JSON operators on it
6. **Docker rebuild required for code changes** — files are NOT volume-mounted; `pm2 restart` alone won't pick up changes
7. **MAX_STEPS = 30** — agent aborts after 30 tool-calling turns; aborted runs produce empty text
8. **Context pruning at 40 messages** — `transformContext` summarizes older messages when history exceeds 40
Loading