vessels 0.6.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 CHANGED
@@ -6,9 +6,10 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/index.ts
9
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
9
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, cpSync, renameSync } from "fs";
10
10
  import { homedir } from "os";
11
- import { join } from "path";
11
+ import { join, dirname, resolve } from "path";
12
+ import { fileURLToPath } from "url";
12
13
  import * as readline from "readline/promises";
13
14
 
14
15
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
@@ -4060,8 +4061,7 @@ var InteractionTypeSchema = external_exports.enum([
4060
4061
  "approval",
4061
4062
  "choice",
4062
4063
  "checklist",
4063
- "text_input",
4064
- "confirm_preview"
4064
+ "text_input"
4065
4065
  ]);
4066
4066
  var ApprovalInteractionSchema = external_exports.object({
4067
4067
  type: external_exports.literal("approval"),
@@ -4104,22 +4104,11 @@ var TextInputInteractionSchema = external_exports.object({
4104
4104
  submitLabel: external_exports.string().optional(),
4105
4105
  metadata: external_exports.record(external_exports.unknown()).optional()
4106
4106
  });
4107
- var ConfirmPreviewInteractionSchema = external_exports.object({
4108
- type: external_exports.literal("confirm_preview"),
4109
- prompt: external_exports.string().min(1),
4110
- previewUrl: external_exports.string().url(),
4111
- previewLabel: external_exports.string().optional(),
4112
- approveLabel: external_exports.string().optional(),
4113
- rejectLabel: external_exports.string().optional(),
4114
- reasonRequiredOnReject: external_exports.boolean().optional(),
4115
- metadata: external_exports.record(external_exports.unknown()).optional()
4116
- });
4117
4107
  var InteractionSchema = external_exports.discriminatedUnion("type", [
4118
4108
  ApprovalInteractionSchema,
4119
4109
  ChoiceInteractionSchema,
4120
4110
  ChecklistInteractionSchema,
4121
- TextInputInteractionSchema,
4122
- ConfirmPreviewInteractionSchema
4111
+ TextInputInteractionSchema
4123
4112
  ]);
4124
4113
  var AgentActivityTypeSchema = external_exports.enum(["thinking", "searching", "tool_use", "browsing", "processing"]);
4125
4114
  var AgentTodoStatusSchema = external_exports.enum(["pending", "in_progress", "done"]);
@@ -4139,7 +4128,10 @@ var CardFieldSchema = external_exports.object({
4139
4128
  value: external_exports.string()
4140
4129
  });
4141
4130
  var CardSchema = external_exports.object({
4142
- title: external_exports.string().min(1),
4131
+ // Optional: a glance-facts card under a surface takes its heading from the
4132
+ // surface `title`, so a card title is redundant there. Still allowed (e.g. a
4133
+ // standalone card on a bubble, or a pinned card).
4134
+ title: external_exports.string().min(1).optional(),
4143
4135
  fields: external_exports.array(CardFieldSchema)
4144
4136
  });
4145
4137
  var AttachmentSchema = external_exports.discriminatedUnion("type", [
@@ -4147,6 +4139,8 @@ var AttachmentSchema = external_exports.discriminatedUnion("type", [
4147
4139
  external_exports.object({ type: external_exports.literal("file"), url: external_exports.string().url(), filename: external_exports.string().optional() })
4148
4140
  ]);
4149
4141
  var VesselStatusSchema = external_exports.enum(["active", "waiting", "resolved"]);
4142
+ var DisplaySchema = external_exports.enum(["bubble", "document"]);
4143
+ var KindSchema = external_exports.enum(["bubble", "surface"]);
4150
4144
  var PushPayloadSchema = external_exports.object({
4151
4145
  message: external_exports.string().min(1).max(1e4).optional(),
4152
4146
  vessel: external_exports.string().optional(),
@@ -4159,13 +4153,28 @@ var PushPayloadSchema = external_exports.object({
4159
4153
  "metadata exceeds 16KB limit"
4160
4154
  ).optional(),
4161
4155
  pinCard: CardSchema.nullable().optional(),
4156
+ /** @deprecated `waiting` is now system-derived from the message's interaction; you don't set status. Still accepted for back-compat. */
4162
4157
  vesselStatus: VesselStatusSchema.optional(),
4163
4158
  labels: external_exports.array(external_exports.string().min(1).max(50)).max(10).optional(),
4164
4159
  attachments: external_exports.array(AttachmentSchema).max(10).optional(),
4165
4160
  suggestions: external_exports.array(external_exports.string().min(1).max(500)).max(5).optional(),
4166
- agentActivity: AgentActivitySchema.optional()
4167
- }).refine((d) => d.message || d.agentActivity, {
4168
- message: "Either message or agentActivity is required"
4161
+ agentActivity: AgentActivitySchema.optional(),
4162
+ /**
4163
+ * Live token-stream buffer an ephemeral monospace block the human watches
4164
+ * fill in real time (set it on the message you create, then keep replacing it
4165
+ * via `PATCH /messages/:id`, and clear it with `null` when done). It is a live
4166
+ * window, not a transcript: send the tail you want shown (the SDK trims to the
4167
+ * last 8000 chars). Plaintext, like agentActivity. Vanishes when cleared.
4168
+ */
4169
+ tokenStream: external_exports.string().max(8e3).optional(),
4170
+ /** Bubble (chat) vs surface (composed artifact). Defaults from interaction/card. */
4171
+ kind: KindSchema.optional(),
4172
+ /** Surface heading. Ignored on bubbles. */
4173
+ title: external_exports.string().max(200).optional(),
4174
+ /** @deprecated legacy presentation hint — use `kind`. 'document' → surface. */
4175
+ display: DisplaySchema.optional()
4176
+ }).refine((d) => d.message || d.agentActivity || d.tokenStream, {
4177
+ message: "One of message, agentActivity, or tokenStream is required"
4169
4178
  });
4170
4179
  var PushManyPayloadSchema = external_exports.object({
4171
4180
  vessels: external_exports.array(external_exports.string().min(1)).min(1).max(100),
@@ -4174,20 +4183,33 @@ var PushManyPayloadSchema = external_exports.object({
4174
4183
  card: CardSchema.optional(),
4175
4184
  interaction: InteractionSchema.optional(),
4176
4185
  pinCard: CardSchema.nullable().optional(),
4186
+ /** @deprecated `waiting` is now system-derived from the message's interaction; you don't set status. Still accepted for back-compat. */
4177
4187
  vesselStatus: VesselStatusSchema.optional(),
4178
4188
  attachments: external_exports.array(AttachmentSchema).max(10).optional(),
4179
4189
  suggestions: external_exports.array(external_exports.string().min(1).max(500)).max(5).optional(),
4180
4190
  metadata: external_exports.record(external_exports.unknown()).refine(
4181
4191
  (v) => JSON.stringify(v).length < 16e3,
4182
4192
  "metadata exceeds 16KB limit"
4183
- ).optional()
4193
+ ).optional(),
4194
+ kind: KindSchema.optional(),
4195
+ title: external_exports.string().max(200).optional(),
4196
+ /** @deprecated use `kind`. */
4197
+ display: DisplaySchema.optional()
4184
4198
  });
4185
4199
  var MessagePatchSchema = external_exports.object({
4186
4200
  content: external_exports.string().min(1).max(1e4).optional(),
4187
4201
  card: CardSchema.nullable().optional(),
4188
4202
  attachments: external_exports.array(AttachmentSchema).max(10).nullable().optional(),
4189
4203
  suggestions: external_exports.array(external_exports.string().min(1).max(500)).max(5).nullable().optional(),
4190
- agentActivity: AgentActivitySchema.nullable().optional()
4204
+ agentActivity: AgentActivitySchema.nullable().optional(),
4205
+ /** Replace the live token-stream window, or `null` to clear it (block vanishes). */
4206
+ tokenStream: external_exports.string().max(8e3).nullable().optional(),
4207
+ /** Switch kind: 'surface' = full-width artifact, 'bubble' or null = chat bubble. */
4208
+ kind: KindSchema.nullable().optional(),
4209
+ /** Update the surface heading, or `null` to clear it. */
4210
+ title: external_exports.string().max(200).nullable().optional(),
4211
+ /** @deprecated use `kind`. */
4212
+ display: DisplaySchema.nullable().optional()
4191
4213
  }).refine((d) => Object.values(d).some((v) => v !== void 0), {
4192
4214
  message: "At least one field required"
4193
4215
  });
@@ -4205,16 +4227,11 @@ var ChecklistResponseSchema = external_exports.object({
4205
4227
  var TextInputResponseSchema = external_exports.object({
4206
4228
  text: external_exports.string()
4207
4229
  });
4208
- var ConfirmPreviewResponseSchema = external_exports.object({
4209
- action: external_exports.enum(["approved", "rejected"]),
4210
- reason: external_exports.string().optional()
4211
- });
4212
4230
  var InteractionResponseSchema = external_exports.discriminatedUnion("interactionType", [
4213
4231
  external_exports.object({ interactionType: external_exports.literal("approval"), response: ApprovalResponseSchema }),
4214
4232
  external_exports.object({ interactionType: external_exports.literal("choice"), response: ChoiceResponseSchema }),
4215
4233
  external_exports.object({ interactionType: external_exports.literal("checklist"), response: ChecklistResponseSchema }),
4216
- external_exports.object({ interactionType: external_exports.literal("text_input"), response: TextInputResponseSchema }),
4217
- external_exports.object({ interactionType: external_exports.literal("confirm_preview"), response: ConfirmPreviewResponseSchema })
4234
+ external_exports.object({ interactionType: external_exports.literal("text_input"), response: TextInputResponseSchema })
4218
4235
  ]);
4219
4236
  var WebhookVesselSchema = external_exports.object({
4220
4237
  id: external_exports.string(),
@@ -4252,6 +4269,11 @@ var WebhookInteractionResponsePayloadSchema = external_exports.object({
4252
4269
  vessel: WebhookVesselSchema
4253
4270
  })
4254
4271
  });
4272
+ var SupersededInteractionSchema = external_exports.object({
4273
+ message_id: external_exports.string(),
4274
+ interaction_type: InteractionTypeSchema,
4275
+ prompt: external_exports.string().nullable()
4276
+ });
4255
4277
  var WebhookUserMessagePayloadSchema = external_exports.object({
4256
4278
  event: external_exports.literal("message.user"),
4257
4279
  vessel_id: external_exports.string(),
@@ -4261,7 +4283,8 @@ var WebhookUserMessagePayloadSchema = external_exports.object({
4261
4283
  message_id: external_exports.string(),
4262
4284
  content: external_exports.string(),
4263
4285
  vessel: WebhookVesselSchema,
4264
- context: external_exports.array(WebhookContextMessageSchema)
4286
+ context: external_exports.array(WebhookContextMessageSchema),
4287
+ superseded_interaction: SupersededInteractionSchema.optional()
4265
4288
  })
4266
4289
  });
4267
4290
  var WebhookVesselCreatedPayloadSchema = external_exports.object({
@@ -4609,10 +4632,10 @@ async function cmdPush(args) {
4609
4632
  console.log(`Message sent. vessel_id=${data.vessel_id} message_id=${data.message_id}`);
4610
4633
  }
4611
4634
  function readStdin() {
4612
- return new Promise((resolve) => {
4635
+ return new Promise((resolve2) => {
4613
4636
  const chunks = [];
4614
4637
  process.stdin.on("data", (c) => chunks.push(c));
4615
- process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
4638
+ process.stdin.on("end", () => resolve2(Buffer.concat(chunks).toString("utf8")));
4616
4639
  });
4617
4640
  }
4618
4641
  function zodIssueLines(err) {
@@ -4761,7 +4784,42 @@ async function cmdTypesToggle(enabled) {
4761
4784
  });
4762
4785
  console.log(`User-initiated vessels ${((_a = data.workspace) == null ? void 0 : _a.userVesselsEnabled) ? "enabled" : "disabled"}.`);
4763
4786
  }
4787
+ function templateDir() {
4788
+ const here = dirname(fileURLToPath(import.meta.url));
4789
+ return join(here, "..", "template", "agent");
4790
+ }
4791
+ async function cmdInitAgentTemplate(args) {
4792
+ const target = resolve(args.find((a) => !a.startsWith("--")) ?? "vessels-agent");
4793
+ const src = templateDir();
4794
+ if (!existsSync(src)) {
4795
+ console.error("Template not found in this CLI build. Reinstall: npm i -g vessels@latest");
4796
+ process.exit(1);
4797
+ }
4798
+ if (existsSync(target) && readdirSync(target).length > 0) {
4799
+ console.error(`Refusing to scaffold into a non-empty directory: ${target}`);
4800
+ process.exit(1);
4801
+ }
4802
+ cpSync(src, target, { recursive: true });
4803
+ for (const [from, to] of [["_gitignore", ".gitignore"], ["_env.example", ".env.example"]]) {
4804
+ const p = join(target, from);
4805
+ if (existsSync(p)) renameSync(p, join(target, to));
4806
+ }
4807
+ console.log(`
4808
+ Scaffolded a Vessels agent into ${target}
4809
+ `);
4810
+ console.log("Next:\n");
4811
+ console.log(` cd ${target}`);
4812
+ console.log(" npm install");
4813
+ console.log(" cp .env.example .env # then fill in your keys");
4814
+ console.log(" npm run dev # webhook server on :3000\n");
4815
+ console.log("Need keys? Run `vessels init --email you@example.com` (then --otp <code>),");
4816
+ console.log("then point a webhook at your running server:");
4817
+ console.log(" vessels webhooks create --url https://<your-public-url>/\n");
4818
+ console.log("Edit src/role.ts (who your agent is) and src/tools.ts (its backend tools).");
4819
+ console.log("Full reference: https://vessels.app/llms-full.txt");
4820
+ }
4764
4821
  async function cmdInit(args) {
4822
+ if (args.includes("--agent-template")) return cmdInitAgentTemplate(args);
4765
4823
  const flags = parseFlags(args);
4766
4824
  const email = flags.email || await prompt("Email: ");
4767
4825
  if (!flags.otp) {
@@ -4842,11 +4900,20 @@ Quick setup (Claude Code / AI agents \u2014 fully non-interactive):
4842
4900
  # With a webhook (optional, run once your server is deployed):
4843
4901
  vessels init --email me@example.com --otp 847293 --webhook-url https://myapp.com/hooks/vessels
4844
4902
 
4903
+ Want a running agent to start from?
4904
+
4905
+ vessels init --agent-template ./my-agent # scaffolds a Vessels-native tool-loop agent
4906
+
4845
4907
  Commands:
4846
4908
  vessels init --email <email> [--otp <code>] [--name <key-name>] [--webhook-url <url>]
4847
4909
  First-time setup: account + API key (+ webhook). Two-step: run once to send OTP,
4848
4910
  re-run with --otp to complete. Prints copy-ready .env entries.
4849
4911
 
4912
+ vessels init --agent-template [dir]
4913
+ Scaffold a runnable, Vessels-native starter agent into <dir> (default ./vessels-agent):
4914
+ a Claude tool-loop wired to Vessels, with stub tools to swap for your real backend.
4915
+ Edit src/role.ts and src/tools.ts, then npm install and npm run dev.
4916
+
4850
4917
  vessels login [--email <email>] [--otp <code>]
4851
4918
  vessels logout
4852
4919
  vessels whoami
package/package.json CHANGED
@@ -1,20 +1,26 @@
1
1
  {
2
2
  "name": "vessels",
3
- "version": "0.6.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": ["ai", "agents", "vessels", "cli"],
18
+ "keywords": [
19
+ "ai",
20
+ "agents",
21
+ "vessels",
22
+ "cli"
23
+ ],
18
24
  "devDependencies": {
19
25
  "tsup": "^8.5.1",
20
26
  "typescript": "^5",
@@ -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,4 @@
1
+ node_modules/
2
+ dist/
3
+ .env
4
+ *.log
@@ -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
+ }