vessels 0.3.0 → 0.7.0
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.
- package/dist/index.js +4550 -14
- package/package.json +11 -4
- package/template/agent/README.md +111 -0
- package/template/agent/_env.example +23 -0
- package/template/agent/_gitignore +4 -0
- package/template/agent/package.json +24 -0
- package/template/agent/src/agent.ts +533 -0
- package/template/agent/src/index.ts +130 -0
- package/template/agent/src/protocol.ts +140 -0
- package/template/agent/src/role.ts +21 -0
- package/template/agent/src/store.ts +135 -0
- package/template/agent/src/tools.ts +90 -0
- package/template/agent/src/vessels-tools.ts +459 -0
- package/template/agent/tsconfig.json +17 -0
package/package.json
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vessels",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Vessels CLI — manage your agent communication layer from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"vessels": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"template"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
14
|
"build": "tsup",
|
|
14
15
|
"dev": "tsup --watch"
|
|
15
16
|
},
|
|
16
17
|
"license": "MIT",
|
|
17
|
-
"keywords": [
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ai",
|
|
20
|
+
"agents",
|
|
21
|
+
"vessels",
|
|
22
|
+
"cli"
|
|
23
|
+
],
|
|
18
24
|
"devDependencies": {
|
|
19
25
|
"tsup": "^8.5.1",
|
|
20
26
|
"typescript": "^5",
|
|
21
|
-
"@types/node": "^25.5.2"
|
|
27
|
+
"@types/node": "^25.5.2",
|
|
28
|
+
"@vessels/types": "workspace:*"
|
|
22
29
|
},
|
|
23
30
|
"dependencies": {}
|
|
24
31
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Your Vessels agent
|
|
2
|
+
|
|
3
|
+
A working, Vessels-native agent. It runs on **your** box, keeps its own state where **you**
|
|
4
|
+
tell it to, and talks to Vessels over HTTP purely to show your human operator what's happening
|
|
5
|
+
and collect their decisions. Swap the two stub tools for your real backend and you have a real
|
|
6
|
+
agent — a booking manager, a support triager, a contracts analyst, a stock-desk assistant.
|
|
7
|
+
|
|
8
|
+
> **Vessels is the view/interaction layer — your agent owns its data.** Vessels never holds your
|
|
9
|
+
> agent's memory, state, or business data. It carries the human-facing messages you send and the
|
|
10
|
+
> answers you get back. Your conversation history and your locks live in *your* store (see
|
|
11
|
+
> `src/store.ts`).
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
cp .env.example .env # then fill in the three required keys
|
|
18
|
+
npm run dev # starts the webhook server on :3000
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Don't have keys yet? Install the CLI and run setup:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx vessels init --email you@example.com # emails a code
|
|
25
|
+
npx vessels init --email you@example.com --otp 123456
|
|
26
|
+
# → prints VESSELS_API_KEY. Then point a webhook at your running server:
|
|
27
|
+
npx vessels webhooks create --url https://<your-public-url>/ # → prints VESSELS_WEBHOOK_SECRET
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
For local development, expose `:3000` with a tunnel (ngrok, cloudflared, `vessels`-friendly
|
|
31
|
+
host) and use that public URL as the webhook URL. Then open a vessel in the Vessels app / mobile
|
|
32
|
+
and message it — your agent answers.
|
|
33
|
+
|
|
34
|
+
## The two files you edit
|
|
35
|
+
|
|
36
|
+
| File | What it is |
|
|
37
|
+
|------|-----------|
|
|
38
|
+
| **`src/role.ts`** | WHO your agent is and WHAT it does — one short paragraph. The only place the engine learns your domain. |
|
|
39
|
+
| **`src/tools.ts`** | Your backend tools. Each is `{ definition, handler, narrate? }`. Replace the two stubs. |
|
|
40
|
+
|
|
41
|
+
Everything else is the **engine** and you rarely touch it:
|
|
42
|
+
|
|
43
|
+
| File | What it is |
|
|
44
|
+
|------|-----------|
|
|
45
|
+
| `src/protocol.ts` | The domain-free system prompt — how to talk to Vessels (bubbles vs surfaces, lead with a reply, plan before working, ask the human as a structured tool call). |
|
|
46
|
+
| `src/vessels-tools.ts` | The fixed Vessels control tools + payload sanitisers + interaction mapping. |
|
|
47
|
+
| `src/agent.ts` | One turn: the Claude tool loop, the live working card, the forced-ending safety net. |
|
|
48
|
+
| `src/store.ts` | `AgentStore` — your conversation state + per-vessel lock. |
|
|
49
|
+
| `src/index.ts` | The webhook server (verify → ACK → run the turn). |
|
|
50
|
+
|
|
51
|
+
## How a turn works
|
|
52
|
+
|
|
53
|
+
1. The human acts in Vessels → Vessels POSTs a webhook to `src/index.ts`.
|
|
54
|
+
2. The server verifies the signature, **ACKs 200 immediately**, and runs the turn in the background.
|
|
55
|
+
3. The engine leads with a one-line reply, opens a live working card, plans, calls **your** tools,
|
|
56
|
+
and ends with exactly one finishing tool — a message (`finish`) or a human decision
|
|
57
|
+
(`request_approval` / `choice` / `checklist` / `text`).
|
|
58
|
+
4. When the human answers, Vessels sends another webhook and the loop continues — the engine loads
|
|
59
|
+
the prior conversation from your store, so the agent picks up exactly where it left off.
|
|
60
|
+
|
|
61
|
+
**Contacting the human is a tool call.** That's the headline pattern: the model doesn't print to a
|
|
62
|
+
human, it raises `request_approval`/`choice`/… and the turn pauses until they answer. Human-in-the-loop
|
|
63
|
+
is native, not bolted on.
|
|
64
|
+
|
|
65
|
+
## State & durability
|
|
66
|
+
|
|
67
|
+
By default the agent uses **`MemoryStore`** — zero infrastructure, correct for a single long-lived
|
|
68
|
+
process, state resets on restart. To make it durable, set `DATABASE_URL` and it switches to
|
|
69
|
+
**`PostgresStore`**, which:
|
|
70
|
+
|
|
71
|
+
- persists each vessel's conversation history, and
|
|
72
|
+
- holds a **cross-process per-vessel lock** (so two webhooks for the same vessel can't run
|
|
73
|
+
interleaved turns) — the thing you need once you run more than one instance.
|
|
74
|
+
|
|
75
|
+
`PostgresStore` self-provisions its tables on boot (`CREATE TABLE IF NOT EXISTS`) — there's no
|
|
76
|
+
migration to run. Want Redis, Dynamo, or your own DB? Implement the `AgentStore` interface in
|
|
77
|
+
`src/store.ts`; nothing else changes.
|
|
78
|
+
|
|
79
|
+
## Deploying
|
|
80
|
+
|
|
81
|
+
This template is a long-lived Node process, so background work after the 200 ACK simply finishes.
|
|
82
|
+
If you deploy to **serverless** (Lambda / Vercel / Workers), the process can freeze right after the
|
|
83
|
+
response — there you must `await` the turn before responding, or use the platform's background
|
|
84
|
+
primitive (e.g. `waitUntil`) so the turn isn't killed mid-flight. The `parseWebhookEvent → ACK →
|
|
85
|
+
runTurn` shape stays the same; only the server wrapper changes.
|
|
86
|
+
|
|
87
|
+
## The full Vessels surface
|
|
88
|
+
|
|
89
|
+
The engine already exposes the breadth of Vessels to the model, so your agent can use all of it:
|
|
90
|
+
chat **bubbles** and full-width **surfaces**; all four **interactions** (`approval` / `choice` /
|
|
91
|
+
`checklist` / `text`, with labels, `allowCustom`, `minSelections`, `reasonRequired`, and
|
|
92
|
+
**metadata** that rides back to you for routing); the live **working card** with a ticking **plan**,
|
|
93
|
+
auto-narrated **steps**, and **token streaming**; **pinned cards**, **labels**, **attachments**
|
|
94
|
+
(images/files you host), **preview links**, **suggested replies**, vessel **naming/renaming**, and
|
|
95
|
+
user-initiated vessel **types**. Idempotency keys, the per-vessel lock, and the resolve-before-ask
|
|
96
|
+
discipline are handled for you. (It deliberately keeps the notification rule too: the working card
|
|
97
|
+
stays silent, only your reply and outcome buzz the human's phone.)
|
|
98
|
+
|
|
99
|
+
A few Vessels features live on **your backend**, not inside a turn — call them on the `vessels`
|
|
100
|
+
SDK directly:
|
|
101
|
+
|
|
102
|
+
- `vessels.pushMany({ vessels: [...], message, interaction })` — broadcast the same message/decision
|
|
103
|
+
to many vessels at once (e.g. "course closed Saturday" to every affected booking).
|
|
104
|
+
- `vessels.getMessages({ vessel })` — re-read a vessel's human-facing history to reconcile a
|
|
105
|
+
stateless/restarted worker (not your memory — that's your store).
|
|
106
|
+
- `vessels.validatePush(payload)` — check a payload against the server schema without sending.
|
|
107
|
+
|
|
108
|
+
## Learn more
|
|
109
|
+
|
|
110
|
+
- Full Vessels docs (API, SDK, webhooks, interaction types): <https://vessels.app/llms-full.txt>
|
|
111
|
+
- The SDK: `vessels-sdk` on npm.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# ── Required ────────────────────────────────────────────────────────────────
|
|
2
|
+
# A vsl_ API key for your workspace (push back to Vessels). From `vessels init` or Settings.
|
|
3
|
+
VESSELS_API_KEY=vsl_...
|
|
4
|
+
# The secret printed when you created your webhook (`vessels webhooks create` or Settings).
|
|
5
|
+
VESSELS_WEBHOOK_SECRET=whsec_...
|
|
6
|
+
# Your Anthropic API key (drives the agent — this is YOUR LLM key, on YOUR bill).
|
|
7
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
8
|
+
|
|
9
|
+
# ── Optional ────────────────────────────────────────────────────────────────
|
|
10
|
+
# Claude model (default: claude-sonnet-4-6).
|
|
11
|
+
# ANTHROPIC_MODEL=claude-sonnet-4-6
|
|
12
|
+
# Port the webhook server listens on (default: 3000).
|
|
13
|
+
# PORT=3000
|
|
14
|
+
# Override the Vessels base URL (default: https://vessels.app).
|
|
15
|
+
# VESSELS_BASE_URL=https://vessels.app
|
|
16
|
+
# Set DEBUG=1 to log the turn flow (model calls, tools, pushes).
|
|
17
|
+
# DEBUG=1
|
|
18
|
+
|
|
19
|
+
# ── Durability upgrade (optional) ─────────────────────────────────────────────
|
|
20
|
+
# Set DATABASE_URL and the agent uses PostgresStore: durable conversation state +
|
|
21
|
+
# a cross-process lock. It self-provisions its tables on boot (no migration to run).
|
|
22
|
+
# Leave it unset to use the zero-infra in-memory default.
|
|
23
|
+
# DATABASE_URL=postgres://user:pass@host:5432/dbname
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vessels-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "A Vessels-native agent — talks to its human operator through Vessels.",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "tsx watch src/index.ts",
|
|
9
|
+
"start": "tsx src/index.ts",
|
|
10
|
+
"typecheck": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@anthropic-ai/sdk": "^0.85.0",
|
|
14
|
+
"dotenv": "^16.4.5",
|
|
15
|
+
"pg": "^8.13.0",
|
|
16
|
+
"vessels-sdk": "^0.14.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^22.10.0",
|
|
20
|
+
"@types/pg": "^8.11.10",
|
|
21
|
+
"tsx": "^4.19.0",
|
|
22
|
+
"typescript": "^5.7.0"
|
|
23
|
+
}
|
|
24
|
+
}
|