vessels-sdk 0.11.0 → 0.12.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 +11 -13
- package/dist/index.cjs +90 -27
- package/dist/index.d.cts +44 -11
- package/dist/index.d.ts +44 -11
- package/dist/index.js +90 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,11 +82,11 @@ Returns `{ ok: true, messageId: string, vesselId: string, createdAt: string }`.
|
|
|
82
82
|
| `labels` | `string[]` | Tags for filtering in the dashboard. Max 10, 50 chars each. Replaces the existing set on every push — send all labels you want, not just new ones. |
|
|
83
83
|
| `metadata` | `object` | Arbitrary JSON stored on the vessel, passed through in webhook callbacks. |
|
|
84
84
|
| `card` | `Card` | Structured key-value info attached to this message. `{ title: string, fields: [{ label, value }] }` |
|
|
85
|
-
| `interaction` | `Interaction` | Interactive prompt for the human (one of
|
|
85
|
+
| `interaction` | `Interaction` | Interactive prompt for the human (one of 4 types — see helpers below). Max one per message; immutable after the human responds. |
|
|
86
86
|
| `pinCard` | `Card \| null` | Persistent card pinned to the vessel header. Always visible above the message stream. Replaces any existing pinned card. Pass `null` to clear. |
|
|
87
87
|
| `attachments` | `Attachment[]` | Images or files to show in the message. Max 10. You host the files; Vessels renders them. `[{ type: 'image' \| 'file', url: string, filename?: string }]` |
|
|
88
88
|
| `suggestions` | `string[]` | Quick-reply chips shown below the message. Max 5. Tapping fills the text input. Disappear after the user sends any message. |
|
|
89
|
-
| `previewUrl` | `string` | URL
|
|
89
|
+
| `previewUrl` | `string` | A single URL rendered as a tappable link card below the message. Presentation only — no response. Compose with any interaction. |
|
|
90
90
|
| `notify` | `boolean` | Whether to send a push notification. Default `true`. |
|
|
91
91
|
|
|
92
92
|
```typescript
|
|
@@ -273,19 +273,18 @@ vessels.textInput({
|
|
|
273
273
|
|
|
274
274
|
Response shape: `{ text: string }`
|
|
275
275
|
|
|
276
|
-
#### `
|
|
276
|
+
#### Review-and-decide (the old `confirmPreview`)
|
|
277
277
|
|
|
278
|
-
|
|
278
|
+
There is no separate preview interaction. To have the human review something then decide,
|
|
279
|
+
attach a [`previewUrl`](#push) (a link card) to the message and use `approval`:
|
|
279
280
|
|
|
280
281
|
```typescript
|
|
281
|
-
vessels.
|
|
282
|
-
|
|
282
|
+
await vessels.push({
|
|
283
|
+
vessel: 'draft-123',
|
|
284
|
+
message: 'Draft email ready for review.',
|
|
283
285
|
previewUrl: 'https://your-app.com/drafts/123',
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
rejectLabel: 'Edit',
|
|
287
|
-
reasonRequiredOnReject: true,
|
|
288
|
-
})
|
|
286
|
+
interaction: vessels.approval({ prompt: 'Send this email?', approveLabel: 'Send', rejectLabel: 'Edit' }),
|
|
287
|
+
});
|
|
289
288
|
```
|
|
290
289
|
|
|
291
290
|
Response shape: `{ action: 'approved' | 'rejected', reason?: string }`
|
|
@@ -311,7 +310,7 @@ const { events, hasMore } = await vessels.poll({ ack: true });
|
|
|
311
310
|
|
|
312
311
|
for (const event of events) {
|
|
313
312
|
if (event.type === 'interaction.response') {
|
|
314
|
-
// event.interactionType — 'approval' | 'choice' | 'checklist' | 'text_input'
|
|
313
|
+
// event.interactionType — 'approval' | 'choice' | 'checklist' | 'text_input'
|
|
315
314
|
// event.response — response shape depends on interactionType (see above)
|
|
316
315
|
// event.interactionMetadata — metadata object you passed when creating the interaction, or null
|
|
317
316
|
// event.messageId — UUID of the message containing the interaction
|
|
@@ -490,7 +489,6 @@ import type {
|
|
|
490
489
|
ChoiceInteraction,
|
|
491
490
|
ChecklistInteraction,
|
|
492
491
|
TextInputInteraction,
|
|
493
|
-
ConfirmPreviewInteraction,
|
|
494
492
|
} from 'vessels-sdk';
|
|
495
493
|
```
|
|
496
494
|
|
package/dist/index.cjs
CHANGED
|
@@ -8,8 +8,7 @@ var InteractionTypeSchema = zod.z.enum([
|
|
|
8
8
|
"approval",
|
|
9
9
|
"choice",
|
|
10
10
|
"checklist",
|
|
11
|
-
"text_input"
|
|
12
|
-
"confirm_preview"
|
|
11
|
+
"text_input"
|
|
13
12
|
]);
|
|
14
13
|
var ApprovalInteractionSchema = zod.z.object({
|
|
15
14
|
type: zod.z.literal("approval"),
|
|
@@ -52,22 +51,11 @@ var TextInputInteractionSchema = zod.z.object({
|
|
|
52
51
|
submitLabel: zod.z.string().optional(),
|
|
53
52
|
metadata: zod.z.record(zod.z.unknown()).optional()
|
|
54
53
|
});
|
|
55
|
-
var ConfirmPreviewInteractionSchema = zod.z.object({
|
|
56
|
-
type: zod.z.literal("confirm_preview"),
|
|
57
|
-
prompt: zod.z.string().min(1),
|
|
58
|
-
previewUrl: zod.z.string().url(),
|
|
59
|
-
previewLabel: zod.z.string().optional(),
|
|
60
|
-
approveLabel: zod.z.string().optional(),
|
|
61
|
-
rejectLabel: zod.z.string().optional(),
|
|
62
|
-
reasonRequiredOnReject: zod.z.boolean().optional(),
|
|
63
|
-
metadata: zod.z.record(zod.z.unknown()).optional()
|
|
64
|
-
});
|
|
65
54
|
var InteractionSchema = zod.z.discriminatedUnion("type", [
|
|
66
55
|
ApprovalInteractionSchema,
|
|
67
56
|
ChoiceInteractionSchema,
|
|
68
57
|
ChecklistInteractionSchema,
|
|
69
|
-
TextInputInteractionSchema
|
|
70
|
-
ConfirmPreviewInteractionSchema
|
|
58
|
+
TextInputInteractionSchema
|
|
71
59
|
]);
|
|
72
60
|
var AgentActivityTypeSchema = zod.z.enum(["thinking", "searching", "tool_use", "browsing", "processing"]);
|
|
73
61
|
var AgentTodoStatusSchema = zod.z.enum(["pending", "in_progress", "done"]);
|
|
@@ -111,9 +99,17 @@ var PushPayloadSchema = zod.z.object({
|
|
|
111
99
|
labels: zod.z.array(zod.z.string().min(1).max(50)).max(10).optional(),
|
|
112
100
|
attachments: zod.z.array(AttachmentSchema).max(10).optional(),
|
|
113
101
|
suggestions: zod.z.array(zod.z.string().min(1).max(500)).max(5).optional(),
|
|
114
|
-
agentActivity: AgentActivitySchema.optional()
|
|
115
|
-
|
|
116
|
-
|
|
102
|
+
agentActivity: AgentActivitySchema.optional(),
|
|
103
|
+
/**
|
|
104
|
+
* Live token-stream buffer — an ephemeral monospace block the human watches
|
|
105
|
+
* fill in real time (set it on the message you create, then keep replacing it
|
|
106
|
+
* via `PATCH /messages/:id`, and clear it with `null` when done). It is a live
|
|
107
|
+
* window, not a transcript: send the tail you want shown (the SDK trims to the
|
|
108
|
+
* last 8000 chars). Plaintext, like agentActivity. Vanishes when cleared.
|
|
109
|
+
*/
|
|
110
|
+
tokenStream: zod.z.string().max(8e3).optional()
|
|
111
|
+
}).refine((d) => d.message || d.agentActivity || d.tokenStream, {
|
|
112
|
+
message: "One of message, agentActivity, or tokenStream is required"
|
|
117
113
|
});
|
|
118
114
|
var PushManyPayloadSchema = zod.z.object({
|
|
119
115
|
vessels: zod.z.array(zod.z.string().min(1)).min(1).max(100),
|
|
@@ -135,7 +131,9 @@ zod.z.object({
|
|
|
135
131
|
card: CardSchema.nullable().optional(),
|
|
136
132
|
attachments: zod.z.array(AttachmentSchema).max(10).nullable().optional(),
|
|
137
133
|
suggestions: zod.z.array(zod.z.string().min(1).max(500)).max(5).nullable().optional(),
|
|
138
|
-
agentActivity: AgentActivitySchema.nullable().optional()
|
|
134
|
+
agentActivity: AgentActivitySchema.nullable().optional(),
|
|
135
|
+
/** Replace the live token-stream window, or `null` to clear it (block vanishes). */
|
|
136
|
+
tokenStream: zod.z.string().max(8e3).nullable().optional()
|
|
139
137
|
}).refine((d) => Object.values(d).some((v) => v !== void 0), {
|
|
140
138
|
message: "At least one field required"
|
|
141
139
|
});
|
|
@@ -153,16 +151,11 @@ var ChecklistResponseSchema = zod.z.object({
|
|
|
153
151
|
var TextInputResponseSchema = zod.z.object({
|
|
154
152
|
text: zod.z.string()
|
|
155
153
|
});
|
|
156
|
-
var ConfirmPreviewResponseSchema = zod.z.object({
|
|
157
|
-
action: zod.z.enum(["approved", "rejected"]),
|
|
158
|
-
reason: zod.z.string().optional()
|
|
159
|
-
});
|
|
160
154
|
zod.z.discriminatedUnion("interactionType", [
|
|
161
155
|
zod.z.object({ interactionType: zod.z.literal("approval"), response: ApprovalResponseSchema }),
|
|
162
156
|
zod.z.object({ interactionType: zod.z.literal("choice"), response: ChoiceResponseSchema }),
|
|
163
157
|
zod.z.object({ interactionType: zod.z.literal("checklist"), response: ChecklistResponseSchema }),
|
|
164
|
-
zod.z.object({ interactionType: zod.z.literal("text_input"), response: TextInputResponseSchema })
|
|
165
|
-
zod.z.object({ interactionType: zod.z.literal("confirm_preview"), response: ConfirmPreviewResponseSchema })
|
|
158
|
+
zod.z.object({ interactionType: zod.z.literal("text_input"), response: TextInputResponseSchema })
|
|
166
159
|
]);
|
|
167
160
|
var WebhookVesselSchema = zod.z.object({
|
|
168
161
|
id: zod.z.string(),
|
|
@@ -456,6 +449,79 @@ var Vessels = class {
|
|
|
456
449
|
}
|
|
457
450
|
};
|
|
458
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* Stream live tokens into a message: the human watches a monospace block fill
|
|
454
|
+
* in real time. Sugar over {@link editMessage} — it keeps the buffer locally
|
|
455
|
+
* and PATCHes a throttled, tail-trimmed window (replace-semantics, so a lost
|
|
456
|
+
* flush self-heals on the next one). Seal with `done()` to clear the block.
|
|
457
|
+
*
|
|
458
|
+
* @param messageId the message to stream into (create it first via `push`).
|
|
459
|
+
* @param opts.throttleMs minimum gap between server flushes (default 120ms).
|
|
460
|
+
* @param opts.maxChars longest window kept; older text scrolls off (default 8000).
|
|
461
|
+
*/
|
|
462
|
+
stream(messageId, opts) {
|
|
463
|
+
const throttleMs = opts?.throttleMs ?? 120;
|
|
464
|
+
const maxChars = opts?.maxChars ?? 8e3;
|
|
465
|
+
let buffer = "";
|
|
466
|
+
let lastSent = null;
|
|
467
|
+
let timer = null;
|
|
468
|
+
let pending = Promise.resolve();
|
|
469
|
+
const windowed = () => buffer.length > maxChars ? buffer.slice(-maxChars) : buffer;
|
|
470
|
+
const flushNow = () => {
|
|
471
|
+
const text = windowed();
|
|
472
|
+
if (text === lastSent) return pending;
|
|
473
|
+
lastSent = text;
|
|
474
|
+
pending = this.editMessage(messageId, { tokenStream: text }).catch(() => {
|
|
475
|
+
lastSent = null;
|
|
476
|
+
});
|
|
477
|
+
return pending;
|
|
478
|
+
};
|
|
479
|
+
const cancelTimer = () => {
|
|
480
|
+
if (timer) {
|
|
481
|
+
clearTimeout(timer);
|
|
482
|
+
timer = null;
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
const schedule = () => {
|
|
486
|
+
if (timer) return;
|
|
487
|
+
timer = setTimeout(() => {
|
|
488
|
+
timer = null;
|
|
489
|
+
void flushNow();
|
|
490
|
+
}, throttleMs);
|
|
491
|
+
timer?.unref?.();
|
|
492
|
+
};
|
|
493
|
+
const settle = async () => {
|
|
494
|
+
cancelTimer();
|
|
495
|
+
await pending.catch(() => {
|
|
496
|
+
});
|
|
497
|
+
};
|
|
498
|
+
return {
|
|
499
|
+
write: (text) => {
|
|
500
|
+
buffer += text;
|
|
501
|
+
schedule();
|
|
502
|
+
},
|
|
503
|
+
set: async (text) => {
|
|
504
|
+
buffer = text;
|
|
505
|
+
cancelTimer();
|
|
506
|
+
await flushNow();
|
|
507
|
+
},
|
|
508
|
+
// Clear the stream AND seal any working card (agentActivity: null is a no-op
|
|
509
|
+
// when there's none), so the final content actually renders.
|
|
510
|
+
done: async (finalContent) => {
|
|
511
|
+
await settle();
|
|
512
|
+
await this.editMessage(messageId, {
|
|
513
|
+
tokenStream: null,
|
|
514
|
+
agentActivity: null,
|
|
515
|
+
...finalContent != null ? { content: finalContent } : {}
|
|
516
|
+
});
|
|
517
|
+
},
|
|
518
|
+
// Remove only the stream; leave the working card alone.
|
|
519
|
+
clear: async () => {
|
|
520
|
+
await settle();
|
|
521
|
+
await this.editMessage(messageId, { tokenStream: null });
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
}
|
|
459
525
|
/**
|
|
460
526
|
* Read a vessel's message history — the human-facing record, for re-reading
|
|
461
527
|
* the channel (e.g. a stateless or just-restarted worker reconciling state).
|
|
@@ -501,9 +567,6 @@ var Vessels = class {
|
|
|
501
567
|
textInput(opts) {
|
|
502
568
|
return { type: "text_input", ...opts };
|
|
503
569
|
}
|
|
504
|
-
confirmPreview(opts) {
|
|
505
|
-
return { type: "confirm_preview", ...opts };
|
|
506
|
-
}
|
|
507
570
|
async poll(options = {}) {
|
|
508
571
|
const { since, limit = 50, ack = true } = options;
|
|
509
572
|
const params = new URLSearchParams();
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _vessels_types from '@vessels/types';
|
|
2
|
-
export { AgentActivity, AgentActivityType, AgentTodoInput, AgentTodoStatus, ApprovalInteraction, Attachment, Card, ChecklistInteraction, ChoiceInteraction,
|
|
2
|
+
export { AgentActivity, AgentActivityType, AgentTodoInput, AgentTodoStatus, ApprovalInteraction, Attachment, Card, ChecklistInteraction, ChoiceInteraction, 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";
|
|
@@ -70,6 +70,34 @@ interface ActivityHandle {
|
|
|
70
70
|
/** Seal the activity — finishes the open step and any in-progress task. */
|
|
71
71
|
done(): Promise<void>;
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* A handle for streaming live tokens into a message — the human watches a
|
|
75
|
+
* monospace block fill in real time, and it vanishes when you seal it. Obtain
|
|
76
|
+
* one with {@link Vessels.stream}; the message must already exist (get its id
|
|
77
|
+
* from {@link Vessels.push}). It is a live WINDOW, not a transcript: write all
|
|
78
|
+
* you like, only the last `maxChars` (default 8000) are shown.
|
|
79
|
+
*
|
|
80
|
+
* ```ts
|
|
81
|
+
* const { messageId } = await vessels.push({ vessel, agentActivity: { type: 'thinking' } });
|
|
82
|
+
* const out = vessels.stream(messageId);
|
|
83
|
+
* for await (const tok of llm) out.write(tok); // throttled PATCHes under the hood
|
|
84
|
+
* await out.done('Booked you in for 2pm Thursday.'); // clears the stream, sets the final reply
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
interface StreamHandle {
|
|
88
|
+
/** Append text to the live buffer; flushed to the server on a throttle. */
|
|
89
|
+
write(text: string): void;
|
|
90
|
+
/** Replace the whole buffer and flush immediately. */
|
|
91
|
+
set(text: string): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Finish the turn: flush, clear the block, and seal any working agent-activity
|
|
94
|
+
* card (a no-op if there is none) — optionally setting the message's final
|
|
95
|
+
* content. Use this when the stream was the last thing you were doing.
|
|
96
|
+
*/
|
|
97
|
+
done(finalContent?: string): Promise<void>;
|
|
98
|
+
/** Just remove the block, leaving any working card untouched (you're still working). */
|
|
99
|
+
clear(): Promise<void>;
|
|
100
|
+
}
|
|
73
101
|
interface PushResponse {
|
|
74
102
|
ok: true;
|
|
75
103
|
messageId: string;
|
|
@@ -217,6 +245,20 @@ declare class Vessels {
|
|
|
217
245
|
* its id from {@link push}.
|
|
218
246
|
*/
|
|
219
247
|
activity(messageId: string): ActivityHandle;
|
|
248
|
+
/**
|
|
249
|
+
* Stream live tokens into a message: the human watches a monospace block fill
|
|
250
|
+
* in real time. Sugar over {@link editMessage} — it keeps the buffer locally
|
|
251
|
+
* and PATCHes a throttled, tail-trimmed window (replace-semantics, so a lost
|
|
252
|
+
* flush self-heals on the next one). Seal with `done()` to clear the block.
|
|
253
|
+
*
|
|
254
|
+
* @param messageId the message to stream into (create it first via `push`).
|
|
255
|
+
* @param opts.throttleMs minimum gap between server flushes (default 120ms).
|
|
256
|
+
* @param opts.maxChars longest window kept; older text scrolls off (default 8000).
|
|
257
|
+
*/
|
|
258
|
+
stream(messageId: string, opts?: {
|
|
259
|
+
throttleMs?: number;
|
|
260
|
+
maxChars?: number;
|
|
261
|
+
}): StreamHandle;
|
|
220
262
|
/**
|
|
221
263
|
* Read a vessel's message history — the human-facing record, for re-reading
|
|
222
264
|
* the channel (e.g. a stateless or just-restarted worker reconciling state).
|
|
@@ -268,18 +310,9 @@ declare class Vessels {
|
|
|
268
310
|
submitLabel?: string;
|
|
269
311
|
metadata?: Record<string, unknown>;
|
|
270
312
|
}): _vessels_types.TextInputInteraction;
|
|
271
|
-
confirmPreview(opts: {
|
|
272
|
-
prompt: string;
|
|
273
|
-
previewUrl: string;
|
|
274
|
-
previewLabel?: string;
|
|
275
|
-
approveLabel?: string;
|
|
276
|
-
rejectLabel?: string;
|
|
277
|
-
reasonRequiredOnReject?: boolean;
|
|
278
|
-
metadata?: Record<string, unknown>;
|
|
279
|
-
}): _vessels_types.ConfirmPreviewInteraction;
|
|
280
313
|
poll(options?: PollOptions): Promise<PollResponse>;
|
|
281
314
|
verifyWebhook(body: string, signature: string, webhookSecret: string): Promise<boolean>;
|
|
282
315
|
parseWebhookEvent(body: string, signature: string, webhookSecret: string): Promise<InteractionResponseEvent | UserMessageEvent | VesselCreatedEvent | MessageCancelledEvent | null>;
|
|
283
316
|
}
|
|
284
317
|
|
|
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 };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _vessels_types from '@vessels/types';
|
|
2
|
-
export { AgentActivity, AgentActivityType, AgentTodoInput, AgentTodoStatus, ApprovalInteraction, Attachment, Card, ChecklistInteraction, ChoiceInteraction,
|
|
2
|
+
export { AgentActivity, AgentActivityType, AgentTodoInput, AgentTodoStatus, ApprovalInteraction, Attachment, Card, ChecklistInteraction, ChoiceInteraction, 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";
|
|
@@ -70,6 +70,34 @@ interface ActivityHandle {
|
|
|
70
70
|
/** Seal the activity — finishes the open step and any in-progress task. */
|
|
71
71
|
done(): Promise<void>;
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* A handle for streaming live tokens into a message — the human watches a
|
|
75
|
+
* monospace block fill in real time, and it vanishes when you seal it. Obtain
|
|
76
|
+
* one with {@link Vessels.stream}; the message must already exist (get its id
|
|
77
|
+
* from {@link Vessels.push}). It is a live WINDOW, not a transcript: write all
|
|
78
|
+
* you like, only the last `maxChars` (default 8000) are shown.
|
|
79
|
+
*
|
|
80
|
+
* ```ts
|
|
81
|
+
* const { messageId } = await vessels.push({ vessel, agentActivity: { type: 'thinking' } });
|
|
82
|
+
* const out = vessels.stream(messageId);
|
|
83
|
+
* for await (const tok of llm) out.write(tok); // throttled PATCHes under the hood
|
|
84
|
+
* await out.done('Booked you in for 2pm Thursday.'); // clears the stream, sets the final reply
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
interface StreamHandle {
|
|
88
|
+
/** Append text to the live buffer; flushed to the server on a throttle. */
|
|
89
|
+
write(text: string): void;
|
|
90
|
+
/** Replace the whole buffer and flush immediately. */
|
|
91
|
+
set(text: string): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Finish the turn: flush, clear the block, and seal any working agent-activity
|
|
94
|
+
* card (a no-op if there is none) — optionally setting the message's final
|
|
95
|
+
* content. Use this when the stream was the last thing you were doing.
|
|
96
|
+
*/
|
|
97
|
+
done(finalContent?: string): Promise<void>;
|
|
98
|
+
/** Just remove the block, leaving any working card untouched (you're still working). */
|
|
99
|
+
clear(): Promise<void>;
|
|
100
|
+
}
|
|
73
101
|
interface PushResponse {
|
|
74
102
|
ok: true;
|
|
75
103
|
messageId: string;
|
|
@@ -217,6 +245,20 @@ declare class Vessels {
|
|
|
217
245
|
* its id from {@link push}.
|
|
218
246
|
*/
|
|
219
247
|
activity(messageId: string): ActivityHandle;
|
|
248
|
+
/**
|
|
249
|
+
* Stream live tokens into a message: the human watches a monospace block fill
|
|
250
|
+
* in real time. Sugar over {@link editMessage} — it keeps the buffer locally
|
|
251
|
+
* and PATCHes a throttled, tail-trimmed window (replace-semantics, so a lost
|
|
252
|
+
* flush self-heals on the next one). Seal with `done()` to clear the block.
|
|
253
|
+
*
|
|
254
|
+
* @param messageId the message to stream into (create it first via `push`).
|
|
255
|
+
* @param opts.throttleMs minimum gap between server flushes (default 120ms).
|
|
256
|
+
* @param opts.maxChars longest window kept; older text scrolls off (default 8000).
|
|
257
|
+
*/
|
|
258
|
+
stream(messageId: string, opts?: {
|
|
259
|
+
throttleMs?: number;
|
|
260
|
+
maxChars?: number;
|
|
261
|
+
}): StreamHandle;
|
|
220
262
|
/**
|
|
221
263
|
* Read a vessel's message history — the human-facing record, for re-reading
|
|
222
264
|
* the channel (e.g. a stateless or just-restarted worker reconciling state).
|
|
@@ -268,18 +310,9 @@ declare class Vessels {
|
|
|
268
310
|
submitLabel?: string;
|
|
269
311
|
metadata?: Record<string, unknown>;
|
|
270
312
|
}): _vessels_types.TextInputInteraction;
|
|
271
|
-
confirmPreview(opts: {
|
|
272
|
-
prompt: string;
|
|
273
|
-
previewUrl: string;
|
|
274
|
-
previewLabel?: string;
|
|
275
|
-
approveLabel?: string;
|
|
276
|
-
rejectLabel?: string;
|
|
277
|
-
reasonRequiredOnReject?: boolean;
|
|
278
|
-
metadata?: Record<string, unknown>;
|
|
279
|
-
}): _vessels_types.ConfirmPreviewInteraction;
|
|
280
313
|
poll(options?: PollOptions): Promise<PollResponse>;
|
|
281
314
|
verifyWebhook(body: string, signature: string, webhookSecret: string): Promise<boolean>;
|
|
282
315
|
parseWebhookEvent(body: string, signature: string, webhookSecret: string): Promise<InteractionResponseEvent | UserMessageEvent | VesselCreatedEvent | MessageCancelledEvent | null>;
|
|
283
316
|
}
|
|
284
317
|
|
|
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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -6,8 +6,7 @@ var InteractionTypeSchema = z.enum([
|
|
|
6
6
|
"approval",
|
|
7
7
|
"choice",
|
|
8
8
|
"checklist",
|
|
9
|
-
"text_input"
|
|
10
|
-
"confirm_preview"
|
|
9
|
+
"text_input"
|
|
11
10
|
]);
|
|
12
11
|
var ApprovalInteractionSchema = z.object({
|
|
13
12
|
type: z.literal("approval"),
|
|
@@ -50,22 +49,11 @@ var TextInputInteractionSchema = z.object({
|
|
|
50
49
|
submitLabel: z.string().optional(),
|
|
51
50
|
metadata: z.record(z.unknown()).optional()
|
|
52
51
|
});
|
|
53
|
-
var ConfirmPreviewInteractionSchema = z.object({
|
|
54
|
-
type: z.literal("confirm_preview"),
|
|
55
|
-
prompt: z.string().min(1),
|
|
56
|
-
previewUrl: z.string().url(),
|
|
57
|
-
previewLabel: z.string().optional(),
|
|
58
|
-
approveLabel: z.string().optional(),
|
|
59
|
-
rejectLabel: z.string().optional(),
|
|
60
|
-
reasonRequiredOnReject: z.boolean().optional(),
|
|
61
|
-
metadata: z.record(z.unknown()).optional()
|
|
62
|
-
});
|
|
63
52
|
var InteractionSchema = z.discriminatedUnion("type", [
|
|
64
53
|
ApprovalInteractionSchema,
|
|
65
54
|
ChoiceInteractionSchema,
|
|
66
55
|
ChecklistInteractionSchema,
|
|
67
|
-
TextInputInteractionSchema
|
|
68
|
-
ConfirmPreviewInteractionSchema
|
|
56
|
+
TextInputInteractionSchema
|
|
69
57
|
]);
|
|
70
58
|
var AgentActivityTypeSchema = z.enum(["thinking", "searching", "tool_use", "browsing", "processing"]);
|
|
71
59
|
var AgentTodoStatusSchema = z.enum(["pending", "in_progress", "done"]);
|
|
@@ -109,9 +97,17 @@ var PushPayloadSchema = z.object({
|
|
|
109
97
|
labels: z.array(z.string().min(1).max(50)).max(10).optional(),
|
|
110
98
|
attachments: z.array(AttachmentSchema).max(10).optional(),
|
|
111
99
|
suggestions: z.array(z.string().min(1).max(500)).max(5).optional(),
|
|
112
|
-
agentActivity: AgentActivitySchema.optional()
|
|
113
|
-
|
|
114
|
-
|
|
100
|
+
agentActivity: AgentActivitySchema.optional(),
|
|
101
|
+
/**
|
|
102
|
+
* Live token-stream buffer — an ephemeral monospace block the human watches
|
|
103
|
+
* fill in real time (set it on the message you create, then keep replacing it
|
|
104
|
+
* via `PATCH /messages/:id`, and clear it with `null` when done). It is a live
|
|
105
|
+
* window, not a transcript: send the tail you want shown (the SDK trims to the
|
|
106
|
+
* last 8000 chars). Plaintext, like agentActivity. Vanishes when cleared.
|
|
107
|
+
*/
|
|
108
|
+
tokenStream: z.string().max(8e3).optional()
|
|
109
|
+
}).refine((d) => d.message || d.agentActivity || d.tokenStream, {
|
|
110
|
+
message: "One of message, agentActivity, or tokenStream is required"
|
|
115
111
|
});
|
|
116
112
|
var PushManyPayloadSchema = z.object({
|
|
117
113
|
vessels: z.array(z.string().min(1)).min(1).max(100),
|
|
@@ -133,7 +129,9 @@ z.object({
|
|
|
133
129
|
card: CardSchema.nullable().optional(),
|
|
134
130
|
attachments: z.array(AttachmentSchema).max(10).nullable().optional(),
|
|
135
131
|
suggestions: z.array(z.string().min(1).max(500)).max(5).nullable().optional(),
|
|
136
|
-
agentActivity: AgentActivitySchema.nullable().optional()
|
|
132
|
+
agentActivity: AgentActivitySchema.nullable().optional(),
|
|
133
|
+
/** Replace the live token-stream window, or `null` to clear it (block vanishes). */
|
|
134
|
+
tokenStream: z.string().max(8e3).nullable().optional()
|
|
137
135
|
}).refine((d) => Object.values(d).some((v) => v !== void 0), {
|
|
138
136
|
message: "At least one field required"
|
|
139
137
|
});
|
|
@@ -151,16 +149,11 @@ var ChecklistResponseSchema = z.object({
|
|
|
151
149
|
var TextInputResponseSchema = z.object({
|
|
152
150
|
text: z.string()
|
|
153
151
|
});
|
|
154
|
-
var ConfirmPreviewResponseSchema = z.object({
|
|
155
|
-
action: z.enum(["approved", "rejected"]),
|
|
156
|
-
reason: z.string().optional()
|
|
157
|
-
});
|
|
158
152
|
z.discriminatedUnion("interactionType", [
|
|
159
153
|
z.object({ interactionType: z.literal("approval"), response: ApprovalResponseSchema }),
|
|
160
154
|
z.object({ interactionType: z.literal("choice"), response: ChoiceResponseSchema }),
|
|
161
155
|
z.object({ interactionType: z.literal("checklist"), response: ChecklistResponseSchema }),
|
|
162
|
-
z.object({ interactionType: z.literal("text_input"), response: TextInputResponseSchema })
|
|
163
|
-
z.object({ interactionType: z.literal("confirm_preview"), response: ConfirmPreviewResponseSchema })
|
|
156
|
+
z.object({ interactionType: z.literal("text_input"), response: TextInputResponseSchema })
|
|
164
157
|
]);
|
|
165
158
|
var WebhookVesselSchema = z.object({
|
|
166
159
|
id: z.string(),
|
|
@@ -454,6 +447,79 @@ var Vessels = class {
|
|
|
454
447
|
}
|
|
455
448
|
};
|
|
456
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* Stream live tokens into a message: the human watches a monospace block fill
|
|
452
|
+
* in real time. Sugar over {@link editMessage} — it keeps the buffer locally
|
|
453
|
+
* and PATCHes a throttled, tail-trimmed window (replace-semantics, so a lost
|
|
454
|
+
* flush self-heals on the next one). Seal with `done()` to clear the block.
|
|
455
|
+
*
|
|
456
|
+
* @param messageId the message to stream into (create it first via `push`).
|
|
457
|
+
* @param opts.throttleMs minimum gap between server flushes (default 120ms).
|
|
458
|
+
* @param opts.maxChars longest window kept; older text scrolls off (default 8000).
|
|
459
|
+
*/
|
|
460
|
+
stream(messageId, opts) {
|
|
461
|
+
const throttleMs = opts?.throttleMs ?? 120;
|
|
462
|
+
const maxChars = opts?.maxChars ?? 8e3;
|
|
463
|
+
let buffer = "";
|
|
464
|
+
let lastSent = null;
|
|
465
|
+
let timer = null;
|
|
466
|
+
let pending = Promise.resolve();
|
|
467
|
+
const windowed = () => buffer.length > maxChars ? buffer.slice(-maxChars) : buffer;
|
|
468
|
+
const flushNow = () => {
|
|
469
|
+
const text = windowed();
|
|
470
|
+
if (text === lastSent) return pending;
|
|
471
|
+
lastSent = text;
|
|
472
|
+
pending = this.editMessage(messageId, { tokenStream: text }).catch(() => {
|
|
473
|
+
lastSent = null;
|
|
474
|
+
});
|
|
475
|
+
return pending;
|
|
476
|
+
};
|
|
477
|
+
const cancelTimer = () => {
|
|
478
|
+
if (timer) {
|
|
479
|
+
clearTimeout(timer);
|
|
480
|
+
timer = null;
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
const schedule = () => {
|
|
484
|
+
if (timer) return;
|
|
485
|
+
timer = setTimeout(() => {
|
|
486
|
+
timer = null;
|
|
487
|
+
void flushNow();
|
|
488
|
+
}, throttleMs);
|
|
489
|
+
timer?.unref?.();
|
|
490
|
+
};
|
|
491
|
+
const settle = async () => {
|
|
492
|
+
cancelTimer();
|
|
493
|
+
await pending.catch(() => {
|
|
494
|
+
});
|
|
495
|
+
};
|
|
496
|
+
return {
|
|
497
|
+
write: (text) => {
|
|
498
|
+
buffer += text;
|
|
499
|
+
schedule();
|
|
500
|
+
},
|
|
501
|
+
set: async (text) => {
|
|
502
|
+
buffer = text;
|
|
503
|
+
cancelTimer();
|
|
504
|
+
await flushNow();
|
|
505
|
+
},
|
|
506
|
+
// Clear the stream AND seal any working card (agentActivity: null is a no-op
|
|
507
|
+
// when there's none), so the final content actually renders.
|
|
508
|
+
done: async (finalContent) => {
|
|
509
|
+
await settle();
|
|
510
|
+
await this.editMessage(messageId, {
|
|
511
|
+
tokenStream: null,
|
|
512
|
+
agentActivity: null,
|
|
513
|
+
...finalContent != null ? { content: finalContent } : {}
|
|
514
|
+
});
|
|
515
|
+
},
|
|
516
|
+
// Remove only the stream; leave the working card alone.
|
|
517
|
+
clear: async () => {
|
|
518
|
+
await settle();
|
|
519
|
+
await this.editMessage(messageId, { tokenStream: null });
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
}
|
|
457
523
|
/**
|
|
458
524
|
* Read a vessel's message history — the human-facing record, for re-reading
|
|
459
525
|
* the channel (e.g. a stateless or just-restarted worker reconciling state).
|
|
@@ -499,9 +565,6 @@ var Vessels = class {
|
|
|
499
565
|
textInput(opts) {
|
|
500
566
|
return { type: "text_input", ...opts };
|
|
501
567
|
}
|
|
502
|
-
confirmPreview(opts) {
|
|
503
|
-
return { type: "confirm_preview", ...opts };
|
|
504
|
-
}
|
|
505
568
|
async poll(options = {}) {
|
|
506
569
|
const { since, limit = 50, ack = true } = options;
|
|
507
570
|
const params = new URLSearchParams();
|