vessels-sdk 0.12.0 → 0.13.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.cjs CHANGED
@@ -75,7 +75,10 @@ var CardFieldSchema = zod.z.object({
75
75
  value: zod.z.string()
76
76
  });
77
77
  var CardSchema = zod.z.object({
78
- title: zod.z.string().min(1),
78
+ // Optional: a glance-facts card under a surface takes its heading from the
79
+ // surface `title`, so a card title is redundant there. Still allowed (e.g. a
80
+ // standalone card on a bubble, or a pinned card).
81
+ title: zod.z.string().min(1).optional(),
79
82
  fields: zod.z.array(CardFieldSchema)
80
83
  });
81
84
  var AttachmentSchema = zod.z.discriminatedUnion("type", [
@@ -83,6 +86,8 @@ var AttachmentSchema = zod.z.discriminatedUnion("type", [
83
86
  zod.z.object({ type: zod.z.literal("file"), url: zod.z.string().url(), filename: zod.z.string().optional() })
84
87
  ]);
85
88
  var VesselStatusSchema = zod.z.enum(["active", "waiting", "resolved"]);
89
+ var DisplaySchema = zod.z.enum(["bubble", "document"]);
90
+ var KindSchema = zod.z.enum(["bubble", "surface"]);
86
91
  var PushPayloadSchema = zod.z.object({
87
92
  message: zod.z.string().min(1).max(1e4).optional(),
88
93
  vessel: zod.z.string().optional(),
@@ -107,7 +112,13 @@ var PushPayloadSchema = zod.z.object({
107
112
  * window, not a transcript: send the tail you want shown (the SDK trims to the
108
113
  * last 8000 chars). Plaintext, like agentActivity. Vanishes when cleared.
109
114
  */
110
- tokenStream: zod.z.string().max(8e3).optional()
115
+ tokenStream: zod.z.string().max(8e3).optional(),
116
+ /** Bubble (chat) vs surface (composed artifact). Defaults from interaction/card. */
117
+ kind: KindSchema.optional(),
118
+ /** Surface heading. Ignored on bubbles. */
119
+ title: zod.z.string().max(200).optional(),
120
+ /** @deprecated legacy presentation hint — use `kind`. 'document' → surface. */
121
+ display: DisplaySchema.optional()
111
122
  }).refine((d) => d.message || d.agentActivity || d.tokenStream, {
112
123
  message: "One of message, agentActivity, or tokenStream is required"
113
124
  });
@@ -124,7 +135,11 @@ var PushManyPayloadSchema = zod.z.object({
124
135
  metadata: zod.z.record(zod.z.unknown()).refine(
125
136
  (v) => JSON.stringify(v).length < 16e3,
126
137
  "metadata exceeds 16KB limit"
127
- ).optional()
138
+ ).optional(),
139
+ kind: KindSchema.optional(),
140
+ title: zod.z.string().max(200).optional(),
141
+ /** @deprecated use `kind`. */
142
+ display: DisplaySchema.optional()
128
143
  });
129
144
  zod.z.object({
130
145
  content: zod.z.string().min(1).max(1e4).optional(),
@@ -133,7 +148,13 @@ zod.z.object({
133
148
  suggestions: zod.z.array(zod.z.string().min(1).max(500)).max(5).nullable().optional(),
134
149
  agentActivity: AgentActivitySchema.nullable().optional(),
135
150
  /** Replace the live token-stream window, or `null` to clear it (block vanishes). */
136
- tokenStream: zod.z.string().max(8e3).nullable().optional()
151
+ tokenStream: zod.z.string().max(8e3).nullable().optional(),
152
+ /** Switch kind: 'surface' = full-width artifact, 'bubble' or null = chat bubble. */
153
+ kind: KindSchema.nullable().optional(),
154
+ /** Update the surface heading, or `null` to clear it. */
155
+ title: zod.z.string().max(200).nullable().optional(),
156
+ /** @deprecated use `kind`. */
157
+ display: DisplaySchema.nullable().optional()
137
158
  }).refine((d) => Object.values(d).some((v) => v !== void 0), {
138
159
  message: "At least one field required"
139
160
  });
@@ -361,6 +382,31 @@ var Vessels = class {
361
382
  replayed: res.headers.get("Idempotent-Replayed") === "true"
362
383
  };
363
384
  }
385
+ /**
386
+ * Create a **surface** — a full-width composed artifact the human reviews and
387
+ * optionally acts on, rendered as one piece: a `title` heading, an optional
388
+ * `card` of glance-facts, a block-markdown `body` (tables, bullet/numbered
389
+ * lists, blockquotes, bold headings, links), and an optional `interaction`
390
+ * (the action bar). Use this for an email/message draft, a quote, an invoice
391
+ * review, a proposal, a report — anything substantial and structured.
392
+ *
393
+ * Sugar over {@link push}: it sets `kind: 'surface'` and maps `body` to the
394
+ * message content. For conversation, status updates and quick questions, use
395
+ * {@link push} instead (a chat **bubble** — the human just replies).
396
+ *
397
+ * @example
398
+ * await vessels.surface({
399
+ * vessel: 'inv-42',
400
+ * title: 'GreenTurf Invoice #GTL-2025-042',
401
+ * card: { fields: [{ label: 'Amount Due', value: '$7,400' }] },
402
+ * body: 'Invoice is **$1,200 over** the usual rate.\n\n| Item | Amount |\n|---|---:|\n| Overage | +$1,200 |',
403
+ * interaction: vessels.approval({ prompt: 'Approve the $7,400 payment?' }),
404
+ * });
405
+ */
406
+ async surface(options) {
407
+ const { body, ...rest } = options;
408
+ return this.push({ ...rest, message: body, kind: "surface" });
409
+ }
364
410
  async pushMany(payload) {
365
411
  const { idempotencyKey, ...body } = payload;
366
412
  const check = PushManyPayloadSchema.safeParse(body);
package/dist/index.d.cts CHANGED
@@ -106,6 +106,14 @@ interface PushResponse {
106
106
  /** True when this response is a replay of an earlier request with the same idempotency key. */
107
107
  replayed: boolean;
108
108
  }
109
+ /**
110
+ * Options for {@link Vessels.surface}. Same as a push, but the artifact text is
111
+ * `body` (not `message`) and `kind` is forced to `'surface'`.
112
+ */
113
+ type SurfaceOptions = Omit<_vessels_types.PushOptions, 'message' | 'kind'> & {
114
+ /** The artifact body — block markdown (tables, lists, blockquotes, bold headings, links). */
115
+ body: string;
116
+ };
109
117
  interface PushManyResult {
110
118
  ok: true;
111
119
  results: Array<{
@@ -233,6 +241,28 @@ declare class Vessels {
233
241
  /** Like {@link validatePush}, but for a `pushMany()` broadcast payload. */
234
242
  validatePushMany(payload: _vessels_types.PushManyOptions): ValidationResult;
235
243
  push(payload: _vessels_types.PushOptions): Promise<PushResponse>;
244
+ /**
245
+ * Create a **surface** — a full-width composed artifact the human reviews and
246
+ * optionally acts on, rendered as one piece: a `title` heading, an optional
247
+ * `card` of glance-facts, a block-markdown `body` (tables, bullet/numbered
248
+ * lists, blockquotes, bold headings, links), and an optional `interaction`
249
+ * (the action bar). Use this for an email/message draft, a quote, an invoice
250
+ * review, a proposal, a report — anything substantial and structured.
251
+ *
252
+ * Sugar over {@link push}: it sets `kind: 'surface'` and maps `body` to the
253
+ * message content. For conversation, status updates and quick questions, use
254
+ * {@link push} instead (a chat **bubble** — the human just replies).
255
+ *
256
+ * @example
257
+ * await vessels.surface({
258
+ * vessel: 'inv-42',
259
+ * title: 'GreenTurf Invoice #GTL-2025-042',
260
+ * card: { fields: [{ label: 'Amount Due', value: '$7,400' }] },
261
+ * body: 'Invoice is **$1,200 over** the usual rate.\n\n| Item | Amount |\n|---|---:|\n| Overage | +$1,200 |',
262
+ * interaction: vessels.approval({ prompt: 'Approve the $7,400 payment?' }),
263
+ * });
264
+ */
265
+ surface(options: SurfaceOptions): Promise<PushResponse>;
236
266
  pushMany(payload: _vessels_types.PushManyOptions): Promise<PushManyResult>;
237
267
  editMessage(messageId: string, patch: _vessels_types.MessagePatch): Promise<{
238
268
  ok: true;
@@ -315,4 +345,4 @@ declare class Vessels {
315
345
  parseWebhookEvent(body: string, signature: string, webhookSecret: string): Promise<InteractionResponseEvent | UserMessageEvent | VesselCreatedEvent | MessageCancelledEvent | null>;
316
346
  }
317
347
 
318
- export { type ActivityHandle, AgentActivityTypes, type InteractionResponseEvent, type Message, type MessageCancelledEvent, type OriginMessage, type PollEvent, type PollOptions, type PollResponse, type PushManyResult, type PushResponse, type StreamHandle, type UserMessageEvent, type ValidationResult, type VesselContext, type VesselCreatedEvent, Vessels, VesselsAuthError, type VesselsConfig, VesselsConflictError, VesselsRateLimitError, VesselsValidationError };
348
+ export { type ActivityHandle, AgentActivityTypes, type InteractionResponseEvent, type Message, type MessageCancelledEvent, type OriginMessage, type PollEvent, type PollOptions, type PollResponse, type PushManyResult, type PushResponse, type StreamHandle, type SurfaceOptions, type UserMessageEvent, type ValidationResult, type VesselContext, type VesselCreatedEvent, Vessels, VesselsAuthError, type VesselsConfig, VesselsConflictError, VesselsRateLimitError, VesselsValidationError };
package/dist/index.d.ts CHANGED
@@ -106,6 +106,14 @@ interface PushResponse {
106
106
  /** True when this response is a replay of an earlier request with the same idempotency key. */
107
107
  replayed: boolean;
108
108
  }
109
+ /**
110
+ * Options for {@link Vessels.surface}. Same as a push, but the artifact text is
111
+ * `body` (not `message`) and `kind` is forced to `'surface'`.
112
+ */
113
+ type SurfaceOptions = Omit<_vessels_types.PushOptions, 'message' | 'kind'> & {
114
+ /** The artifact body — block markdown (tables, lists, blockquotes, bold headings, links). */
115
+ body: string;
116
+ };
109
117
  interface PushManyResult {
110
118
  ok: true;
111
119
  results: Array<{
@@ -233,6 +241,28 @@ declare class Vessels {
233
241
  /** Like {@link validatePush}, but for a `pushMany()` broadcast payload. */
234
242
  validatePushMany(payload: _vessels_types.PushManyOptions): ValidationResult;
235
243
  push(payload: _vessels_types.PushOptions): Promise<PushResponse>;
244
+ /**
245
+ * Create a **surface** — a full-width composed artifact the human reviews and
246
+ * optionally acts on, rendered as one piece: a `title` heading, an optional
247
+ * `card` of glance-facts, a block-markdown `body` (tables, bullet/numbered
248
+ * lists, blockquotes, bold headings, links), and an optional `interaction`
249
+ * (the action bar). Use this for an email/message draft, a quote, an invoice
250
+ * review, a proposal, a report — anything substantial and structured.
251
+ *
252
+ * Sugar over {@link push}: it sets `kind: 'surface'` and maps `body` to the
253
+ * message content. For conversation, status updates and quick questions, use
254
+ * {@link push} instead (a chat **bubble** — the human just replies).
255
+ *
256
+ * @example
257
+ * await vessels.surface({
258
+ * vessel: 'inv-42',
259
+ * title: 'GreenTurf Invoice #GTL-2025-042',
260
+ * card: { fields: [{ label: 'Amount Due', value: '$7,400' }] },
261
+ * body: 'Invoice is **$1,200 over** the usual rate.\n\n| Item | Amount |\n|---|---:|\n| Overage | +$1,200 |',
262
+ * interaction: vessels.approval({ prompt: 'Approve the $7,400 payment?' }),
263
+ * });
264
+ */
265
+ surface(options: SurfaceOptions): Promise<PushResponse>;
236
266
  pushMany(payload: _vessels_types.PushManyOptions): Promise<PushManyResult>;
237
267
  editMessage(messageId: string, patch: _vessels_types.MessagePatch): Promise<{
238
268
  ok: true;
@@ -315,4 +345,4 @@ declare class Vessels {
315
345
  parseWebhookEvent(body: string, signature: string, webhookSecret: string): Promise<InteractionResponseEvent | UserMessageEvent | VesselCreatedEvent | MessageCancelledEvent | null>;
316
346
  }
317
347
 
318
- export { type ActivityHandle, AgentActivityTypes, type InteractionResponseEvent, type Message, type MessageCancelledEvent, type OriginMessage, type PollEvent, type PollOptions, type PollResponse, type PushManyResult, type PushResponse, type StreamHandle, type UserMessageEvent, type ValidationResult, type VesselContext, type VesselCreatedEvent, Vessels, VesselsAuthError, type VesselsConfig, VesselsConflictError, VesselsRateLimitError, VesselsValidationError };
348
+ export { type ActivityHandle, AgentActivityTypes, type InteractionResponseEvent, type Message, type MessageCancelledEvent, type OriginMessage, type PollEvent, type PollOptions, type PollResponse, type PushManyResult, type PushResponse, type StreamHandle, type SurfaceOptions, type UserMessageEvent, type ValidationResult, type VesselContext, type VesselCreatedEvent, Vessels, VesselsAuthError, type VesselsConfig, VesselsConflictError, VesselsRateLimitError, VesselsValidationError };
package/dist/index.js CHANGED
@@ -73,7 +73,10 @@ var CardFieldSchema = z.object({
73
73
  value: z.string()
74
74
  });
75
75
  var CardSchema = z.object({
76
- title: z.string().min(1),
76
+ // Optional: a glance-facts card under a surface takes its heading from the
77
+ // surface `title`, so a card title is redundant there. Still allowed (e.g. a
78
+ // standalone card on a bubble, or a pinned card).
79
+ title: z.string().min(1).optional(),
77
80
  fields: z.array(CardFieldSchema)
78
81
  });
79
82
  var AttachmentSchema = z.discriminatedUnion("type", [
@@ -81,6 +84,8 @@ var AttachmentSchema = z.discriminatedUnion("type", [
81
84
  z.object({ type: z.literal("file"), url: z.string().url(), filename: z.string().optional() })
82
85
  ]);
83
86
  var VesselStatusSchema = z.enum(["active", "waiting", "resolved"]);
87
+ var DisplaySchema = z.enum(["bubble", "document"]);
88
+ var KindSchema = z.enum(["bubble", "surface"]);
84
89
  var PushPayloadSchema = z.object({
85
90
  message: z.string().min(1).max(1e4).optional(),
86
91
  vessel: z.string().optional(),
@@ -105,7 +110,13 @@ var PushPayloadSchema = z.object({
105
110
  * window, not a transcript: send the tail you want shown (the SDK trims to the
106
111
  * last 8000 chars). Plaintext, like agentActivity. Vanishes when cleared.
107
112
  */
108
- tokenStream: z.string().max(8e3).optional()
113
+ tokenStream: z.string().max(8e3).optional(),
114
+ /** Bubble (chat) vs surface (composed artifact). Defaults from interaction/card. */
115
+ kind: KindSchema.optional(),
116
+ /** Surface heading. Ignored on bubbles. */
117
+ title: z.string().max(200).optional(),
118
+ /** @deprecated legacy presentation hint — use `kind`. 'document' → surface. */
119
+ display: DisplaySchema.optional()
109
120
  }).refine((d) => d.message || d.agentActivity || d.tokenStream, {
110
121
  message: "One of message, agentActivity, or tokenStream is required"
111
122
  });
@@ -122,7 +133,11 @@ var PushManyPayloadSchema = z.object({
122
133
  metadata: z.record(z.unknown()).refine(
123
134
  (v) => JSON.stringify(v).length < 16e3,
124
135
  "metadata exceeds 16KB limit"
125
- ).optional()
136
+ ).optional(),
137
+ kind: KindSchema.optional(),
138
+ title: z.string().max(200).optional(),
139
+ /** @deprecated use `kind`. */
140
+ display: DisplaySchema.optional()
126
141
  });
127
142
  z.object({
128
143
  content: z.string().min(1).max(1e4).optional(),
@@ -131,7 +146,13 @@ z.object({
131
146
  suggestions: z.array(z.string().min(1).max(500)).max(5).nullable().optional(),
132
147
  agentActivity: AgentActivitySchema.nullable().optional(),
133
148
  /** Replace the live token-stream window, or `null` to clear it (block vanishes). */
134
- tokenStream: z.string().max(8e3).nullable().optional()
149
+ tokenStream: z.string().max(8e3).nullable().optional(),
150
+ /** Switch kind: 'surface' = full-width artifact, 'bubble' or null = chat bubble. */
151
+ kind: KindSchema.nullable().optional(),
152
+ /** Update the surface heading, or `null` to clear it. */
153
+ title: z.string().max(200).nullable().optional(),
154
+ /** @deprecated use `kind`. */
155
+ display: DisplaySchema.nullable().optional()
135
156
  }).refine((d) => Object.values(d).some((v) => v !== void 0), {
136
157
  message: "At least one field required"
137
158
  });
@@ -359,6 +380,31 @@ var Vessels = class {
359
380
  replayed: res.headers.get("Idempotent-Replayed") === "true"
360
381
  };
361
382
  }
383
+ /**
384
+ * Create a **surface** — a full-width composed artifact the human reviews and
385
+ * optionally acts on, rendered as one piece: a `title` heading, an optional
386
+ * `card` of glance-facts, a block-markdown `body` (tables, bullet/numbered
387
+ * lists, blockquotes, bold headings, links), and an optional `interaction`
388
+ * (the action bar). Use this for an email/message draft, a quote, an invoice
389
+ * review, a proposal, a report — anything substantial and structured.
390
+ *
391
+ * Sugar over {@link push}: it sets `kind: 'surface'` and maps `body` to the
392
+ * message content. For conversation, status updates and quick questions, use
393
+ * {@link push} instead (a chat **bubble** — the human just replies).
394
+ *
395
+ * @example
396
+ * await vessels.surface({
397
+ * vessel: 'inv-42',
398
+ * title: 'GreenTurf Invoice #GTL-2025-042',
399
+ * card: { fields: [{ label: 'Amount Due', value: '$7,400' }] },
400
+ * body: 'Invoice is **$1,200 over** the usual rate.\n\n| Item | Amount |\n|---|---:|\n| Overage | +$1,200 |',
401
+ * interaction: vessels.approval({ prompt: 'Approve the $7,400 payment?' }),
402
+ * });
403
+ */
404
+ async surface(options) {
405
+ const { body, ...rest } = options;
406
+ return this.push({ ...rest, message: body, kind: "surface" });
407
+ }
362
408
  async pushMany(payload) {
363
409
  const { idempotencyKey, ...body } = payload;
364
410
  const check = PushManyPayloadSchema.safeParse(body);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vessels-sdk",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Let your agent reach you. Official Vessels SDK.",
5
5
  "type": "module",
6
6
  "exports": {