vessels-sdk 0.9.0 → 0.11.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/README.md +24 -1
- package/dist/index.cjs +87 -4
- package/dist/index.d.cts +60 -2
- package/dist/index.d.ts +60 -2
- package/dist/index.js +87 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -144,6 +144,29 @@ for (const r of results) {
|
|
|
144
144
|
|
|
145
145
|
---
|
|
146
146
|
|
|
147
|
+
### `vessels.validatePush(payload)` / `vessels.validatePushMany(payload)`
|
|
148
|
+
|
|
149
|
+
Check that a payload complies with the required syntax **without sending anything**. Useful for confirming an agent's message is well-formed in a test or dry run before it reaches a human. This runs the **same schema the server enforces**, so a payload that passes here is exactly one the API will accept.
|
|
150
|
+
|
|
151
|
+
Returns `{ valid: boolean, errors: string[], details? }` — `errors` is a list of readable `field.path: message` lines; `details` is Zod's `flatten()` output (`{ formErrors, fieldErrors }`).
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const check = vessels.validatePush({
|
|
155
|
+
vessel: 'booking-123',
|
|
156
|
+
message: 'New booking',
|
|
157
|
+
interaction: { type: 'approval', prompt: 'Confirm?' },
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (!check.valid) {
|
|
161
|
+
console.error(check.errors);
|
|
162
|
+
// e.g. ['suggestions: Array must contain at most 5 element(s)']
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
You don't have to call this explicitly: `push()` and `pushMany()` run the same check internally and throw a `VesselsValidationError` **before** the network request, so a malformed payload fails fast without burning an API call. Call `validatePush` directly when you want a result object instead of an exception.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
147
170
|
### `vessels.editMessage(messageId, patch)`
|
|
148
171
|
|
|
149
172
|
Edit an existing agent-sent message in place. The message re-renders via Supabase Realtime without reloading.
|
|
@@ -416,7 +439,7 @@ Three typed error classes are exported alongside `Vessels`:
|
|
|
416
439
|
| Class | HTTP status | Description |
|
|
417
440
|
|-------|-------------|-------------|
|
|
418
441
|
| `VesselsAuthError` | 401 | Invalid or revoked API key |
|
|
419
|
-
| `VesselsValidationError` | 400 | Bad request payload. Check `.details` for field-level errors. |
|
|
442
|
+
| `VesselsValidationError` | 400 | Bad request payload. Also thrown **locally, before the request** by `push()`/`pushMany()` when the payload fails client-side validation. Check `.details` for field-level errors. |
|
|
420
443
|
| `VesselsRateLimitError` | 429 | Rate limit exceeded. Check `.retryAfter` (seconds) before retrying. |
|
|
421
444
|
|
|
422
445
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -70,9 +70,17 @@ var InteractionSchema = zod.z.discriminatedUnion("type", [
|
|
|
70
70
|
ConfirmPreviewInteractionSchema
|
|
71
71
|
]);
|
|
72
72
|
var AgentActivityTypeSchema = zod.z.enum(["thinking", "searching", "tool_use", "browsing", "processing"]);
|
|
73
|
+
var AgentTodoStatusSchema = zod.z.enum(["pending", "in_progress", "done"]);
|
|
74
|
+
var AgentTodoInputSchema = zod.z.object({
|
|
75
|
+
label: zod.z.string().min(1).max(200),
|
|
76
|
+
status: AgentTodoStatusSchema.optional()
|
|
77
|
+
});
|
|
73
78
|
var AgentActivitySchema = zod.z.object({
|
|
74
|
-
type: AgentActivityTypeSchema,
|
|
75
|
-
label: zod.z.string().max(200).optional()
|
|
79
|
+
type: AgentActivityTypeSchema.optional(),
|
|
80
|
+
label: zod.z.string().max(200).optional(),
|
|
81
|
+
todos: zod.z.array(AgentTodoInputSchema).max(50).optional()
|
|
82
|
+
}).refine((d) => d.type != null || d.todos != null, {
|
|
83
|
+
message: "agentActivity requires `type` (a step) or `todos` (a plan)"
|
|
76
84
|
});
|
|
77
85
|
var CardFieldSchema = zod.z.object({
|
|
78
86
|
label: zod.z.string().min(1),
|
|
@@ -87,7 +95,7 @@ var AttachmentSchema = zod.z.discriminatedUnion("type", [
|
|
|
87
95
|
zod.z.object({ type: zod.z.literal("file"), url: zod.z.string().url(), filename: zod.z.string().optional() })
|
|
88
96
|
]);
|
|
89
97
|
var VesselStatusSchema = zod.z.enum(["active", "waiting", "resolved"]);
|
|
90
|
-
zod.z.object({
|
|
98
|
+
var PushPayloadSchema = zod.z.object({
|
|
91
99
|
message: zod.z.string().min(1).max(1e4).optional(),
|
|
92
100
|
vessel: zod.z.string().optional(),
|
|
93
101
|
vesselTitle: zod.z.string().optional(),
|
|
@@ -107,7 +115,7 @@ zod.z.object({
|
|
|
107
115
|
}).refine((d) => d.message || d.agentActivity, {
|
|
108
116
|
message: "Either message or agentActivity is required"
|
|
109
117
|
});
|
|
110
|
-
zod.z.object({
|
|
118
|
+
var PushManyPayloadSchema = zod.z.object({
|
|
111
119
|
vessels: zod.z.array(zod.z.string().min(1)).min(1).max(100),
|
|
112
120
|
message: zod.z.string().min(1).max(1e4),
|
|
113
121
|
vesselTitle: zod.z.string().optional(),
|
|
@@ -271,6 +279,13 @@ var VesselsConflictError = class extends Error {
|
|
|
271
279
|
this.name = "VesselsConflictError";
|
|
272
280
|
}
|
|
273
281
|
};
|
|
282
|
+
function formatZodError(error) {
|
|
283
|
+
const errors = error.issues.map((i) => {
|
|
284
|
+
const path = i.path.join(".") || "(payload)";
|
|
285
|
+
return `${path}: ${i.message}`;
|
|
286
|
+
});
|
|
287
|
+
return { errors, details: error.flatten() };
|
|
288
|
+
}
|
|
274
289
|
var Vessels = class {
|
|
275
290
|
apiKey;
|
|
276
291
|
baseUrl;
|
|
@@ -303,8 +318,33 @@ var Vessels = class {
|
|
|
303
318
|
}
|
|
304
319
|
return res;
|
|
305
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* Check a push payload against the schema the server enforces, WITHOUT sending
|
|
323
|
+
* anything. Use this to confirm an agent's message complies before it goes
|
|
324
|
+
* live (e.g. in a test or a dry-run). `push()` runs the same check internally.
|
|
325
|
+
*/
|
|
326
|
+
validatePush(payload) {
|
|
327
|
+
const { idempotencyKey: _ignored, ...body } = payload;
|
|
328
|
+
const result = PushPayloadSchema.safeParse(body);
|
|
329
|
+
if (result.success) return { valid: true, errors: [] };
|
|
330
|
+
const { errors, details } = formatZodError(result.error);
|
|
331
|
+
return { valid: false, errors, details };
|
|
332
|
+
}
|
|
333
|
+
/** Like {@link validatePush}, but for a `pushMany()` broadcast payload. */
|
|
334
|
+
validatePushMany(payload) {
|
|
335
|
+
const { idempotencyKey: _ignored, ...body } = payload;
|
|
336
|
+
const result = PushManyPayloadSchema.safeParse(body);
|
|
337
|
+
if (result.success) return { valid: true, errors: [] };
|
|
338
|
+
const { errors, details } = formatZodError(result.error);
|
|
339
|
+
return { valid: false, errors, details };
|
|
340
|
+
}
|
|
306
341
|
async push(payload) {
|
|
307
342
|
const { idempotencyKey, ...body } = payload;
|
|
343
|
+
const check = PushPayloadSchema.safeParse(body);
|
|
344
|
+
if (!check.success) {
|
|
345
|
+
const { errors, details } = formatZodError(check.error);
|
|
346
|
+
throw new VesselsValidationError(`Invalid push payload \u2014 ${errors.join("; ")}`, details);
|
|
347
|
+
}
|
|
308
348
|
const res = await this._fetch(`${this.baseUrl}/api/v1/push`, {
|
|
309
349
|
method: "POST",
|
|
310
350
|
headers: {
|
|
@@ -330,6 +370,11 @@ var Vessels = class {
|
|
|
330
370
|
}
|
|
331
371
|
async pushMany(payload) {
|
|
332
372
|
const { idempotencyKey, ...body } = payload;
|
|
373
|
+
const check = PushManyPayloadSchema.safeParse(body);
|
|
374
|
+
if (!check.success) {
|
|
375
|
+
const { errors, details } = formatZodError(check.error);
|
|
376
|
+
throw new VesselsValidationError(`Invalid pushMany payload \u2014 ${errors.join("; ")}`, details);
|
|
377
|
+
}
|
|
333
378
|
const res = await this._fetch(`${this.baseUrl}/api/v1/push/many`, {
|
|
334
379
|
method: "POST",
|
|
335
380
|
headers: {
|
|
@@ -373,6 +418,44 @@ var Vessels = class {
|
|
|
373
418
|
if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
|
|
374
419
|
return { ok: true };
|
|
375
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Narrate a working message: declare a plan, file steps under each task as you
|
|
423
|
+
* go, and seal it when done. Sugar over {@link editMessage} — it tracks the
|
|
424
|
+
* todo list locally and PATCHes the full list each update (the server is
|
|
425
|
+
* authoritative and reconciles by label). The message must already exist; get
|
|
426
|
+
* its id from {@link push}.
|
|
427
|
+
*/
|
|
428
|
+
activity(messageId) {
|
|
429
|
+
let todos = [];
|
|
430
|
+
const sendTodos = () => this.editMessage(messageId, { agentActivity: { todos } });
|
|
431
|
+
return {
|
|
432
|
+
plan: async (tasks) => {
|
|
433
|
+
todos = tasks.map(
|
|
434
|
+
(t) => typeof t === "string" ? { label: t, status: "pending" } : { label: t.label, status: t.status ?? "pending" }
|
|
435
|
+
);
|
|
436
|
+
await sendTodos();
|
|
437
|
+
},
|
|
438
|
+
start: async (label) => {
|
|
439
|
+
todos = todos.map(
|
|
440
|
+
(t) => t.label === label ? { ...t, status: "in_progress" } : t.status === "in_progress" ? { ...t, status: "done" } : t
|
|
441
|
+
);
|
|
442
|
+
if (!todos.some((t) => t.label === label)) todos.push({ label, status: "in_progress" });
|
|
443
|
+
await sendTodos();
|
|
444
|
+
},
|
|
445
|
+
step: async (type, label) => {
|
|
446
|
+
await this.editMessage(messageId, { agentActivity: { type, label } });
|
|
447
|
+
},
|
|
448
|
+
complete: async (label) => {
|
|
449
|
+
todos = todos.map(
|
|
450
|
+
(t) => (label ? t.label === label : t.status === "in_progress") ? { ...t, status: "done" } : t
|
|
451
|
+
);
|
|
452
|
+
await sendTodos();
|
|
453
|
+
},
|
|
454
|
+
done: async () => {
|
|
455
|
+
await this.editMessage(messageId, { agentActivity: null });
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
376
459
|
/**
|
|
377
460
|
* Read a vessel's message history — the human-facing record, for re-reading
|
|
378
461
|
* the channel (e.g. a stateless or just-restarted worker reconciling state).
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _vessels_types from '@vessels/types';
|
|
2
|
-
export { AgentActivity, AgentActivityType, ApprovalInteraction, Attachment, Card, ChecklistInteraction, ChoiceInteraction, ConfirmPreviewInteraction, Interaction, MessagePatch, PushManyOptions, PushManyPayload, PushOptions, PushPayload, TextInputInteraction, WebhookInteractionResponsePayload, WebhookInteractionResponsePayloadSchema, WebhookPayload, WebhookPayloadSchema, WebhookUserMessagePayload, WebhookUserMessagePayloadSchema, WebhookVesselCreatedPayload, WebhookVesselCreatedPayloadSchema } from '@vessels/types';
|
|
2
|
+
export { AgentActivity, AgentActivityType, AgentTodoInput, AgentTodoStatus, ApprovalInteraction, Attachment, Card, ChecklistInteraction, ChoiceInteraction, ConfirmPreviewInteraction, Interaction, MessagePatch, PushManyOptions, PushManyPayload, PushOptions, PushPayload, TextInputInteraction, WebhookInteractionResponsePayload, WebhookInteractionResponsePayloadSchema, WebhookPayload, WebhookPayloadSchema, WebhookUserMessagePayload, WebhookUserMessagePayloadSchema, WebhookVesselCreatedPayload, WebhookVesselCreatedPayloadSchema } from '@vessels/types';
|
|
3
3
|
|
|
4
4
|
declare const AgentActivityTypes: {
|
|
5
5
|
readonly thinking: "thinking";
|
|
@@ -23,11 +23,53 @@ declare class VesselsRateLimitError extends Error {
|
|
|
23
23
|
declare class VesselsConflictError extends Error {
|
|
24
24
|
constructor(message: string);
|
|
25
25
|
}
|
|
26
|
+
/** Result of a client-side payload check via {@link Vessels.validatePush}. */
|
|
27
|
+
interface ValidationResult {
|
|
28
|
+
/** True when the payload satisfies the schema the server enforces. */
|
|
29
|
+
valid: boolean;
|
|
30
|
+
/** Human-readable `field.path: message` lines — empty when valid. */
|
|
31
|
+
errors: string[];
|
|
32
|
+
/** Zod `flatten()` output (field + form errors) — present only when invalid. */
|
|
33
|
+
details?: {
|
|
34
|
+
formErrors: string[];
|
|
35
|
+
fieldErrors: Record<string, string[]>;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
26
38
|
interface VesselsConfig {
|
|
27
39
|
apiKey: string;
|
|
28
40
|
baseUrl?: string;
|
|
29
41
|
debug?: boolean;
|
|
30
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* A stateful handle for narrating a working message — the plan and the work it
|
|
45
|
+
* produces resolve into one artifact. Steps emitted while a task is active are
|
|
46
|
+
* filed under it server-side. Obtain one with {@link Vessels.activity}.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* const act = vessels.activity(messageId);
|
|
50
|
+
* await act.plan(['Check availability', 'Draft email', 'Send to customer']);
|
|
51
|
+
* await act.start('Check availability'); // → in_progress, step target
|
|
52
|
+
* await act.step('searching', 'Querying calendar');
|
|
53
|
+
* await act.start('Draft email'); // auto-finishes the prior task
|
|
54
|
+
* await act.step('tool_use', 'Sending via SendGrid');
|
|
55
|
+
* await act.done(); // seals everything
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
interface ActivityHandle {
|
|
59
|
+
/** Declare (or replace) the plan. Tasks default to `pending`. */
|
|
60
|
+
plan(tasks: Array<string | {
|
|
61
|
+
label: string;
|
|
62
|
+
status?: _vessels_types.AgentTodoStatus;
|
|
63
|
+
}>): Promise<void>;
|
|
64
|
+
/** Mark a task in-progress (creating it if new); any other in-progress task is finished. */
|
|
65
|
+
start(label: string): Promise<void>;
|
|
66
|
+
/** Append a step, filed under the active task. */
|
|
67
|
+
step(type: _vessels_types.AgentActivityType, label?: string): Promise<void>;
|
|
68
|
+
/** Finish a task by label, or the current in-progress task if omitted. */
|
|
69
|
+
complete(label?: string): Promise<void>;
|
|
70
|
+
/** Seal the activity — finishes the open step and any in-progress task. */
|
|
71
|
+
done(): Promise<void>;
|
|
72
|
+
}
|
|
31
73
|
interface PushResponse {
|
|
32
74
|
ok: true;
|
|
33
75
|
messageId: string;
|
|
@@ -154,11 +196,27 @@ declare class Vessels {
|
|
|
154
196
|
private _debug;
|
|
155
197
|
constructor(config: VesselsConfig);
|
|
156
198
|
private _fetch;
|
|
199
|
+
/**
|
|
200
|
+
* Check a push payload against the schema the server enforces, WITHOUT sending
|
|
201
|
+
* anything. Use this to confirm an agent's message complies before it goes
|
|
202
|
+
* live (e.g. in a test or a dry-run). `push()` runs the same check internally.
|
|
203
|
+
*/
|
|
204
|
+
validatePush(payload: _vessels_types.PushOptions): ValidationResult;
|
|
205
|
+
/** Like {@link validatePush}, but for a `pushMany()` broadcast payload. */
|
|
206
|
+
validatePushMany(payload: _vessels_types.PushManyOptions): ValidationResult;
|
|
157
207
|
push(payload: _vessels_types.PushOptions): Promise<PushResponse>;
|
|
158
208
|
pushMany(payload: _vessels_types.PushManyOptions): Promise<PushManyResult>;
|
|
159
209
|
editMessage(messageId: string, patch: _vessels_types.MessagePatch): Promise<{
|
|
160
210
|
ok: true;
|
|
161
211
|
}>;
|
|
212
|
+
/**
|
|
213
|
+
* Narrate a working message: declare a plan, file steps under each task as you
|
|
214
|
+
* go, and seal it when done. Sugar over {@link editMessage} — it tracks the
|
|
215
|
+
* todo list locally and PATCHes the full list each update (the server is
|
|
216
|
+
* authoritative and reconciles by label). The message must already exist; get
|
|
217
|
+
* its id from {@link push}.
|
|
218
|
+
*/
|
|
219
|
+
activity(messageId: string): ActivityHandle;
|
|
162
220
|
/**
|
|
163
221
|
* Read a vessel's message history — the human-facing record, for re-reading
|
|
164
222
|
* the channel (e.g. a stateless or just-restarted worker reconciling state).
|
|
@@ -224,4 +282,4 @@ declare class Vessels {
|
|
|
224
282
|
parseWebhookEvent(body: string, signature: string, webhookSecret: string): Promise<InteractionResponseEvent | UserMessageEvent | VesselCreatedEvent | MessageCancelledEvent | null>;
|
|
225
283
|
}
|
|
226
284
|
|
|
227
|
-
export { AgentActivityTypes, type InteractionResponseEvent, type Message, type MessageCancelledEvent, type OriginMessage, type PollEvent, type PollOptions, type PollResponse, type PushManyResult, type PushResponse, type UserMessageEvent, type VesselContext, type VesselCreatedEvent, Vessels, VesselsAuthError, type VesselsConfig, VesselsConflictError, VesselsRateLimitError, VesselsValidationError };
|
|
285
|
+
export { type ActivityHandle, AgentActivityTypes, type InteractionResponseEvent, type Message, type MessageCancelledEvent, type OriginMessage, type PollEvent, type PollOptions, type PollResponse, type PushManyResult, type PushResponse, type UserMessageEvent, type ValidationResult, type VesselContext, type VesselCreatedEvent, Vessels, VesselsAuthError, type VesselsConfig, VesselsConflictError, VesselsRateLimitError, VesselsValidationError };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _vessels_types from '@vessels/types';
|
|
2
|
-
export { AgentActivity, AgentActivityType, ApprovalInteraction, Attachment, Card, ChecklistInteraction, ChoiceInteraction, ConfirmPreviewInteraction, Interaction, MessagePatch, PushManyOptions, PushManyPayload, PushOptions, PushPayload, TextInputInteraction, WebhookInteractionResponsePayload, WebhookInteractionResponsePayloadSchema, WebhookPayload, WebhookPayloadSchema, WebhookUserMessagePayload, WebhookUserMessagePayloadSchema, WebhookVesselCreatedPayload, WebhookVesselCreatedPayloadSchema } from '@vessels/types';
|
|
2
|
+
export { AgentActivity, AgentActivityType, AgentTodoInput, AgentTodoStatus, ApprovalInteraction, Attachment, Card, ChecklistInteraction, ChoiceInteraction, ConfirmPreviewInteraction, Interaction, MessagePatch, PushManyOptions, PushManyPayload, PushOptions, PushPayload, TextInputInteraction, WebhookInteractionResponsePayload, WebhookInteractionResponsePayloadSchema, WebhookPayload, WebhookPayloadSchema, WebhookUserMessagePayload, WebhookUserMessagePayloadSchema, WebhookVesselCreatedPayload, WebhookVesselCreatedPayloadSchema } from '@vessels/types';
|
|
3
3
|
|
|
4
4
|
declare const AgentActivityTypes: {
|
|
5
5
|
readonly thinking: "thinking";
|
|
@@ -23,11 +23,53 @@ declare class VesselsRateLimitError extends Error {
|
|
|
23
23
|
declare class VesselsConflictError extends Error {
|
|
24
24
|
constructor(message: string);
|
|
25
25
|
}
|
|
26
|
+
/** Result of a client-side payload check via {@link Vessels.validatePush}. */
|
|
27
|
+
interface ValidationResult {
|
|
28
|
+
/** True when the payload satisfies the schema the server enforces. */
|
|
29
|
+
valid: boolean;
|
|
30
|
+
/** Human-readable `field.path: message` lines — empty when valid. */
|
|
31
|
+
errors: string[];
|
|
32
|
+
/** Zod `flatten()` output (field + form errors) — present only when invalid. */
|
|
33
|
+
details?: {
|
|
34
|
+
formErrors: string[];
|
|
35
|
+
fieldErrors: Record<string, string[]>;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
26
38
|
interface VesselsConfig {
|
|
27
39
|
apiKey: string;
|
|
28
40
|
baseUrl?: string;
|
|
29
41
|
debug?: boolean;
|
|
30
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* A stateful handle for narrating a working message — the plan and the work it
|
|
45
|
+
* produces resolve into one artifact. Steps emitted while a task is active are
|
|
46
|
+
* filed under it server-side. Obtain one with {@link Vessels.activity}.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* const act = vessels.activity(messageId);
|
|
50
|
+
* await act.plan(['Check availability', 'Draft email', 'Send to customer']);
|
|
51
|
+
* await act.start('Check availability'); // → in_progress, step target
|
|
52
|
+
* await act.step('searching', 'Querying calendar');
|
|
53
|
+
* await act.start('Draft email'); // auto-finishes the prior task
|
|
54
|
+
* await act.step('tool_use', 'Sending via SendGrid');
|
|
55
|
+
* await act.done(); // seals everything
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
interface ActivityHandle {
|
|
59
|
+
/** Declare (or replace) the plan. Tasks default to `pending`. */
|
|
60
|
+
plan(tasks: Array<string | {
|
|
61
|
+
label: string;
|
|
62
|
+
status?: _vessels_types.AgentTodoStatus;
|
|
63
|
+
}>): Promise<void>;
|
|
64
|
+
/** Mark a task in-progress (creating it if new); any other in-progress task is finished. */
|
|
65
|
+
start(label: string): Promise<void>;
|
|
66
|
+
/** Append a step, filed under the active task. */
|
|
67
|
+
step(type: _vessels_types.AgentActivityType, label?: string): Promise<void>;
|
|
68
|
+
/** Finish a task by label, or the current in-progress task if omitted. */
|
|
69
|
+
complete(label?: string): Promise<void>;
|
|
70
|
+
/** Seal the activity — finishes the open step and any in-progress task. */
|
|
71
|
+
done(): Promise<void>;
|
|
72
|
+
}
|
|
31
73
|
interface PushResponse {
|
|
32
74
|
ok: true;
|
|
33
75
|
messageId: string;
|
|
@@ -154,11 +196,27 @@ declare class Vessels {
|
|
|
154
196
|
private _debug;
|
|
155
197
|
constructor(config: VesselsConfig);
|
|
156
198
|
private _fetch;
|
|
199
|
+
/**
|
|
200
|
+
* Check a push payload against the schema the server enforces, WITHOUT sending
|
|
201
|
+
* anything. Use this to confirm an agent's message complies before it goes
|
|
202
|
+
* live (e.g. in a test or a dry-run). `push()` runs the same check internally.
|
|
203
|
+
*/
|
|
204
|
+
validatePush(payload: _vessels_types.PushOptions): ValidationResult;
|
|
205
|
+
/** Like {@link validatePush}, but for a `pushMany()` broadcast payload. */
|
|
206
|
+
validatePushMany(payload: _vessels_types.PushManyOptions): ValidationResult;
|
|
157
207
|
push(payload: _vessels_types.PushOptions): Promise<PushResponse>;
|
|
158
208
|
pushMany(payload: _vessels_types.PushManyOptions): Promise<PushManyResult>;
|
|
159
209
|
editMessage(messageId: string, patch: _vessels_types.MessagePatch): Promise<{
|
|
160
210
|
ok: true;
|
|
161
211
|
}>;
|
|
212
|
+
/**
|
|
213
|
+
* Narrate a working message: declare a plan, file steps under each task as you
|
|
214
|
+
* go, and seal it when done. Sugar over {@link editMessage} — it tracks the
|
|
215
|
+
* todo list locally and PATCHes the full list each update (the server is
|
|
216
|
+
* authoritative and reconciles by label). The message must already exist; get
|
|
217
|
+
* its id from {@link push}.
|
|
218
|
+
*/
|
|
219
|
+
activity(messageId: string): ActivityHandle;
|
|
162
220
|
/**
|
|
163
221
|
* Read a vessel's message history — the human-facing record, for re-reading
|
|
164
222
|
* the channel (e.g. a stateless or just-restarted worker reconciling state).
|
|
@@ -224,4 +282,4 @@ declare class Vessels {
|
|
|
224
282
|
parseWebhookEvent(body: string, signature: string, webhookSecret: string): Promise<InteractionResponseEvent | UserMessageEvent | VesselCreatedEvent | MessageCancelledEvent | null>;
|
|
225
283
|
}
|
|
226
284
|
|
|
227
|
-
export { AgentActivityTypes, type InteractionResponseEvent, type Message, type MessageCancelledEvent, type OriginMessage, type PollEvent, type PollOptions, type PollResponse, type PushManyResult, type PushResponse, type UserMessageEvent, type VesselContext, type VesselCreatedEvent, Vessels, VesselsAuthError, type VesselsConfig, VesselsConflictError, VesselsRateLimitError, VesselsValidationError };
|
|
285
|
+
export { type ActivityHandle, AgentActivityTypes, type InteractionResponseEvent, type Message, type MessageCancelledEvent, type OriginMessage, type PollEvent, type PollOptions, type PollResponse, type PushManyResult, type PushResponse, type UserMessageEvent, type ValidationResult, type VesselContext, type VesselCreatedEvent, Vessels, VesselsAuthError, type VesselsConfig, VesselsConflictError, VesselsRateLimitError, VesselsValidationError };
|
package/dist/index.js
CHANGED
|
@@ -68,9 +68,17 @@ var InteractionSchema = z.discriminatedUnion("type", [
|
|
|
68
68
|
ConfirmPreviewInteractionSchema
|
|
69
69
|
]);
|
|
70
70
|
var AgentActivityTypeSchema = z.enum(["thinking", "searching", "tool_use", "browsing", "processing"]);
|
|
71
|
+
var AgentTodoStatusSchema = z.enum(["pending", "in_progress", "done"]);
|
|
72
|
+
var AgentTodoInputSchema = z.object({
|
|
73
|
+
label: z.string().min(1).max(200),
|
|
74
|
+
status: AgentTodoStatusSchema.optional()
|
|
75
|
+
});
|
|
71
76
|
var AgentActivitySchema = z.object({
|
|
72
|
-
type: AgentActivityTypeSchema,
|
|
73
|
-
label: z.string().max(200).optional()
|
|
77
|
+
type: AgentActivityTypeSchema.optional(),
|
|
78
|
+
label: z.string().max(200).optional(),
|
|
79
|
+
todos: z.array(AgentTodoInputSchema).max(50).optional()
|
|
80
|
+
}).refine((d) => d.type != null || d.todos != null, {
|
|
81
|
+
message: "agentActivity requires `type` (a step) or `todos` (a plan)"
|
|
74
82
|
});
|
|
75
83
|
var CardFieldSchema = z.object({
|
|
76
84
|
label: z.string().min(1),
|
|
@@ -85,7 +93,7 @@ var AttachmentSchema = z.discriminatedUnion("type", [
|
|
|
85
93
|
z.object({ type: z.literal("file"), url: z.string().url(), filename: z.string().optional() })
|
|
86
94
|
]);
|
|
87
95
|
var VesselStatusSchema = z.enum(["active", "waiting", "resolved"]);
|
|
88
|
-
z.object({
|
|
96
|
+
var PushPayloadSchema = z.object({
|
|
89
97
|
message: z.string().min(1).max(1e4).optional(),
|
|
90
98
|
vessel: z.string().optional(),
|
|
91
99
|
vesselTitle: z.string().optional(),
|
|
@@ -105,7 +113,7 @@ z.object({
|
|
|
105
113
|
}).refine((d) => d.message || d.agentActivity, {
|
|
106
114
|
message: "Either message or agentActivity is required"
|
|
107
115
|
});
|
|
108
|
-
z.object({
|
|
116
|
+
var PushManyPayloadSchema = z.object({
|
|
109
117
|
vessels: z.array(z.string().min(1)).min(1).max(100),
|
|
110
118
|
message: z.string().min(1).max(1e4),
|
|
111
119
|
vesselTitle: z.string().optional(),
|
|
@@ -269,6 +277,13 @@ var VesselsConflictError = class extends Error {
|
|
|
269
277
|
this.name = "VesselsConflictError";
|
|
270
278
|
}
|
|
271
279
|
};
|
|
280
|
+
function formatZodError(error) {
|
|
281
|
+
const errors = error.issues.map((i) => {
|
|
282
|
+
const path = i.path.join(".") || "(payload)";
|
|
283
|
+
return `${path}: ${i.message}`;
|
|
284
|
+
});
|
|
285
|
+
return { errors, details: error.flatten() };
|
|
286
|
+
}
|
|
272
287
|
var Vessels = class {
|
|
273
288
|
apiKey;
|
|
274
289
|
baseUrl;
|
|
@@ -301,8 +316,33 @@ var Vessels = class {
|
|
|
301
316
|
}
|
|
302
317
|
return res;
|
|
303
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Check a push payload against the schema the server enforces, WITHOUT sending
|
|
321
|
+
* anything. Use this to confirm an agent's message complies before it goes
|
|
322
|
+
* live (e.g. in a test or a dry-run). `push()` runs the same check internally.
|
|
323
|
+
*/
|
|
324
|
+
validatePush(payload) {
|
|
325
|
+
const { idempotencyKey: _ignored, ...body } = payload;
|
|
326
|
+
const result = PushPayloadSchema.safeParse(body);
|
|
327
|
+
if (result.success) return { valid: true, errors: [] };
|
|
328
|
+
const { errors, details } = formatZodError(result.error);
|
|
329
|
+
return { valid: false, errors, details };
|
|
330
|
+
}
|
|
331
|
+
/** Like {@link validatePush}, but for a `pushMany()` broadcast payload. */
|
|
332
|
+
validatePushMany(payload) {
|
|
333
|
+
const { idempotencyKey: _ignored, ...body } = payload;
|
|
334
|
+
const result = PushManyPayloadSchema.safeParse(body);
|
|
335
|
+
if (result.success) return { valid: true, errors: [] };
|
|
336
|
+
const { errors, details } = formatZodError(result.error);
|
|
337
|
+
return { valid: false, errors, details };
|
|
338
|
+
}
|
|
304
339
|
async push(payload) {
|
|
305
340
|
const { idempotencyKey, ...body } = payload;
|
|
341
|
+
const check = PushPayloadSchema.safeParse(body);
|
|
342
|
+
if (!check.success) {
|
|
343
|
+
const { errors, details } = formatZodError(check.error);
|
|
344
|
+
throw new VesselsValidationError(`Invalid push payload \u2014 ${errors.join("; ")}`, details);
|
|
345
|
+
}
|
|
306
346
|
const res = await this._fetch(`${this.baseUrl}/api/v1/push`, {
|
|
307
347
|
method: "POST",
|
|
308
348
|
headers: {
|
|
@@ -328,6 +368,11 @@ var Vessels = class {
|
|
|
328
368
|
}
|
|
329
369
|
async pushMany(payload) {
|
|
330
370
|
const { idempotencyKey, ...body } = payload;
|
|
371
|
+
const check = PushManyPayloadSchema.safeParse(body);
|
|
372
|
+
if (!check.success) {
|
|
373
|
+
const { errors, details } = formatZodError(check.error);
|
|
374
|
+
throw new VesselsValidationError(`Invalid pushMany payload \u2014 ${errors.join("; ")}`, details);
|
|
375
|
+
}
|
|
331
376
|
const res = await this._fetch(`${this.baseUrl}/api/v1/push/many`, {
|
|
332
377
|
method: "POST",
|
|
333
378
|
headers: {
|
|
@@ -371,6 +416,44 @@ var Vessels = class {
|
|
|
371
416
|
if (!res.ok) throw new Error(data.error ?? `HTTP ${res.status}`);
|
|
372
417
|
return { ok: true };
|
|
373
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Narrate a working message: declare a plan, file steps under each task as you
|
|
421
|
+
* go, and seal it when done. Sugar over {@link editMessage} — it tracks the
|
|
422
|
+
* todo list locally and PATCHes the full list each update (the server is
|
|
423
|
+
* authoritative and reconciles by label). The message must already exist; get
|
|
424
|
+
* its id from {@link push}.
|
|
425
|
+
*/
|
|
426
|
+
activity(messageId) {
|
|
427
|
+
let todos = [];
|
|
428
|
+
const sendTodos = () => this.editMessage(messageId, { agentActivity: { todos } });
|
|
429
|
+
return {
|
|
430
|
+
plan: async (tasks) => {
|
|
431
|
+
todos = tasks.map(
|
|
432
|
+
(t) => typeof t === "string" ? { label: t, status: "pending" } : { label: t.label, status: t.status ?? "pending" }
|
|
433
|
+
);
|
|
434
|
+
await sendTodos();
|
|
435
|
+
},
|
|
436
|
+
start: async (label) => {
|
|
437
|
+
todos = todos.map(
|
|
438
|
+
(t) => t.label === label ? { ...t, status: "in_progress" } : t.status === "in_progress" ? { ...t, status: "done" } : t
|
|
439
|
+
);
|
|
440
|
+
if (!todos.some((t) => t.label === label)) todos.push({ label, status: "in_progress" });
|
|
441
|
+
await sendTodos();
|
|
442
|
+
},
|
|
443
|
+
step: async (type, label) => {
|
|
444
|
+
await this.editMessage(messageId, { agentActivity: { type, label } });
|
|
445
|
+
},
|
|
446
|
+
complete: async (label) => {
|
|
447
|
+
todos = todos.map(
|
|
448
|
+
(t) => (label ? t.label === label : t.status === "in_progress") ? { ...t, status: "done" } : t
|
|
449
|
+
);
|
|
450
|
+
await sendTodos();
|
|
451
|
+
},
|
|
452
|
+
done: async () => {
|
|
453
|
+
await this.editMessage(messageId, { agentActivity: null });
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
}
|
|
374
457
|
/**
|
|
375
458
|
* Read a vessel's message history — the human-facing record, for re-reading
|
|
376
459
|
* the channel (e.g. a stateless or just-restarted worker reconciling state).
|