@soyeht/soyeht 0.1.1 → 0.2.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/src/types.ts CHANGED
@@ -1,190 +1,437 @@
1
- import { Type, type Static } from "@sinclair/typebox";
1
+ type JsonSchema = Record<string, unknown>;
2
2
 
3
3
  // ---------------------------------------------------------------------------
4
4
  // Account config schema (per-account settings under channels.soyeht.accounts.*)
5
5
  // ---------------------------------------------------------------------------
6
6
 
7
- export const SoyehtAccountConfigSchema = Type.Object({
8
- enabled: Type.Optional(Type.Boolean()),
9
- backendBaseUrl: Type.Optional(Type.String()),
10
- pluginAuthToken: Type.Optional(Type.String()),
11
- allowProactive: Type.Optional(Type.Boolean()),
12
- audio: Type.Optional(
13
- Type.Object({
14
- transcribeInbound: Type.Optional(Type.Boolean()),
15
- ttsOutbound: Type.Optional(Type.Boolean()),
16
- }),
17
- ),
18
- files: Type.Optional(
19
- Type.Object({
20
- acceptInbound: Type.Optional(Type.Boolean()),
21
- maxBytes: Type.Optional(Type.Number()),
22
- }, { additionalProperties: false }),
23
- ),
24
- security: Type.Optional(
25
- Type.Object({
26
- enabled: Type.Optional(Type.Boolean()),
27
- timestampToleranceMs: Type.Optional(Type.Number()),
28
- dhRatchetIntervalMessages: Type.Optional(Type.Number()),
29
- dhRatchetIntervalMs: Type.Optional(Type.Number()),
30
- sessionMaxAgeMs: Type.Optional(Type.Number()),
31
- rateLimit: Type.Optional(
32
- Type.Object({
33
- maxRequests: Type.Optional(Type.Number()),
34
- windowMs: Type.Optional(Type.Number()),
35
- }, { additionalProperties: false }),
36
- ),
37
- }, { additionalProperties: false }),
38
- ),
39
- }, { additionalProperties: false });
40
-
41
- export type SoyehtAccountConfig = Static<typeof SoyehtAccountConfigSchema>;
42
-
43
- export const SoyehtChannelConfigSchema = Type.Object({
44
- accounts: Type.Optional(Type.Record(Type.String(), SoyehtAccountConfigSchema)),
45
- }, { additionalProperties: false });
46
-
47
- export type SoyehtChannelConfig = Static<typeof SoyehtChannelConfigSchema>;
7
+ export type SoyehtAccountConfig = {
8
+ enabled?: boolean;
9
+ backendBaseUrl?: string;
10
+ pluginAuthToken?: string;
11
+ gatewayUrl?: string;
12
+ allowProactive?: boolean;
13
+ audio?: {
14
+ transcribeInbound?: boolean;
15
+ ttsOutbound?: boolean;
16
+ };
17
+ files?: {
18
+ acceptInbound?: boolean;
19
+ maxBytes?: number;
20
+ };
21
+ security?: {
22
+ enabled?: boolean;
23
+ timestampToleranceMs?: number;
24
+ dhRatchetIntervalMessages?: number;
25
+ dhRatchetIntervalMs?: number;
26
+ sessionMaxAgeMs?: number;
27
+ rateLimit?: {
28
+ maxRequests?: number;
29
+ windowMs?: number;
30
+ };
31
+ };
32
+ };
33
+
34
+ export const SoyehtAccountConfigSchema: JsonSchema = {
35
+ type: "object",
36
+ additionalProperties: false,
37
+ properties: {
38
+ enabled: { type: "boolean" },
39
+ backendBaseUrl: { type: "string" },
40
+ pluginAuthToken: { type: "string" },
41
+ gatewayUrl: { type: "string" },
42
+ allowProactive: { type: "boolean" },
43
+ audio: {
44
+ type: "object",
45
+ additionalProperties: false,
46
+ properties: {
47
+ transcribeInbound: { type: "boolean" },
48
+ ttsOutbound: { type: "boolean" },
49
+ },
50
+ },
51
+ files: {
52
+ type: "object",
53
+ additionalProperties: false,
54
+ properties: {
55
+ acceptInbound: { type: "boolean" },
56
+ maxBytes: { type: "number" },
57
+ },
58
+ },
59
+ security: {
60
+ type: "object",
61
+ additionalProperties: false,
62
+ properties: {
63
+ enabled: { type: "boolean" },
64
+ timestampToleranceMs: { type: "number" },
65
+ dhRatchetIntervalMessages: { type: "number" },
66
+ dhRatchetIntervalMs: { type: "number" },
67
+ sessionMaxAgeMs: { type: "number" },
68
+ rateLimit: {
69
+ type: "object",
70
+ additionalProperties: false,
71
+ properties: {
72
+ maxRequests: { type: "number" },
73
+ windowMs: { type: "number" },
74
+ },
75
+ },
76
+ },
77
+ },
78
+ },
79
+ };
80
+
81
+ export type SoyehtChannelConfig = {
82
+ accounts?: Record<string, SoyehtAccountConfig>;
83
+ };
84
+
85
+ export const SoyehtChannelConfigSchema: JsonSchema = {
86
+ type: "object",
87
+ additionalProperties: false,
88
+ properties: {
89
+ accounts: {
90
+ type: "object",
91
+ additionalProperties: SoyehtAccountConfigSchema,
92
+ },
93
+ },
94
+ };
48
95
 
49
96
  // ---------------------------------------------------------------------------
50
97
  // Outbound message payloads
51
98
  // ---------------------------------------------------------------------------
52
99
 
53
- export const TextMessagePayloadSchema = Type.Object({
54
- contentType: Type.Literal("text"),
55
- text: Type.String({ minLength: 1 }),
56
- });
57
-
58
- export type TextMessagePayload = Static<typeof TextMessagePayloadSchema>;
59
-
60
- export const AudioMessagePayloadSchema = Type.Object({
61
- contentType: Type.Literal("audio"),
62
- renderStyle: Type.Literal("voice_note"),
63
- mimeType: Type.String(),
64
- filename: Type.String(),
65
- durationMs: Type.Optional(Type.Number()),
66
- url: Type.String(),
67
- transcript: Type.Optional(Type.String()),
68
- });
69
-
70
- export type AudioMessagePayload = Static<typeof AudioMessagePayloadSchema>;
71
-
72
- export const FileMessagePayloadSchema = Type.Object({
73
- contentType: Type.Literal("file"),
74
- mimeType: Type.String(),
75
- filename: Type.String(),
76
- sizeBytes: Type.Optional(Type.Number()),
77
- url: Type.String(),
78
- });
79
-
80
- export type FileMessagePayload = Static<typeof FileMessagePayloadSchema>;
100
+ export type TextMessagePayload = {
101
+ contentType: "text";
102
+ text: string;
103
+ };
104
+
105
+ export const TextMessagePayloadSchema: JsonSchema = {
106
+ type: "object",
107
+ additionalProperties: false,
108
+ required: ["contentType", "text"],
109
+ properties: {
110
+ contentType: { const: "text" },
111
+ text: { type: "string", minLength: 1 },
112
+ },
113
+ };
114
+
115
+ export type AudioMessagePayload = {
116
+ contentType: "audio";
117
+ renderStyle: "voice_note";
118
+ mimeType: string;
119
+ filename: string;
120
+ durationMs?: number;
121
+ url: string;
122
+ transcript?: string;
123
+ };
124
+
125
+ export const AudioMessagePayloadSchema: JsonSchema = {
126
+ type: "object",
127
+ additionalProperties: false,
128
+ required: ["contentType", "renderStyle", "mimeType", "filename", "url"],
129
+ properties: {
130
+ contentType: { const: "audio" },
131
+ renderStyle: { const: "voice_note" },
132
+ mimeType: { type: "string" },
133
+ filename: { type: "string" },
134
+ durationMs: { type: "number" },
135
+ url: { type: "string" },
136
+ transcript: { type: "string" },
137
+ },
138
+ };
139
+
140
+ export type FileMessagePayload = {
141
+ contentType: "file";
142
+ mimeType: string;
143
+ filename: string;
144
+ sizeBytes?: number;
145
+ url: string;
146
+ };
147
+
148
+ export const FileMessagePayloadSchema: JsonSchema = {
149
+ type: "object",
150
+ additionalProperties: false,
151
+ required: ["contentType", "mimeType", "filename", "url"],
152
+ properties: {
153
+ contentType: { const: "file" },
154
+ mimeType: { type: "string" },
155
+ filename: { type: "string" },
156
+ sizeBytes: { type: "number" },
157
+ url: { type: "string" },
158
+ },
159
+ };
81
160
 
82
161
  // ---------------------------------------------------------------------------
83
162
  // Outbound envelope (wraps any payload for backend delivery)
84
163
  // ---------------------------------------------------------------------------
85
164
 
86
- export const OutboundEnvelopeSchema = Type.Object({
87
- accountId: Type.String(),
88
- sessionId: Type.String(),
89
- deliveryId: Type.String(),
90
- timestamp: Type.Number(),
91
- message: Type.Union([
92
- TextMessagePayloadSchema,
93
- AudioMessagePayloadSchema,
94
- FileMessagePayloadSchema,
95
- ]),
96
- });
97
-
98
- export type OutboundEnvelope = Static<typeof OutboundEnvelopeSchema>;
165
+ export type OutboundEnvelope = {
166
+ accountId: string;
167
+ sessionId: string;
168
+ deliveryId: string;
169
+ timestamp: number;
170
+ message: TextMessagePayload | AudioMessagePayload | FileMessagePayload;
171
+ };
172
+
173
+ export const OutboundEnvelopeSchema: JsonSchema = {
174
+ type: "object",
175
+ additionalProperties: false,
176
+ required: ["accountId", "sessionId", "deliveryId", "timestamp", "message"],
177
+ properties: {
178
+ accountId: { type: "string" },
179
+ sessionId: { type: "string" },
180
+ deliveryId: { type: "string" },
181
+ timestamp: { type: "number" },
182
+ message: {
183
+ oneOf: [
184
+ TextMessagePayloadSchema,
185
+ AudioMessagePayloadSchema,
186
+ FileMessagePayloadSchema,
187
+ ],
188
+ },
189
+ },
190
+ };
99
191
 
100
192
  // ---------------------------------------------------------------------------
101
193
  // Delivery ack (webhook response from backend)
102
194
  // ---------------------------------------------------------------------------
103
195
 
104
- export const DeliveryAckSchema = Type.Object({
105
- deliveryId: Type.String(),
106
- status: Type.Union([Type.Literal("delivered"), Type.Literal("failed")]),
107
- error: Type.Optional(Type.String()),
108
- });
109
-
110
- export type DeliveryAck = Static<typeof DeliveryAckSchema>;
196
+ export type DeliveryAck = {
197
+ deliveryId: string;
198
+ status: "delivered" | "failed";
199
+ error?: string;
200
+ };
201
+
202
+ export const DeliveryAckSchema: JsonSchema = {
203
+ type: "object",
204
+ additionalProperties: false,
205
+ required: ["deliveryId", "status"],
206
+ properties: {
207
+ deliveryId: { type: "string" },
208
+ status: { enum: ["delivered", "failed"] },
209
+ error: { type: "string" },
210
+ },
211
+ };
111
212
 
112
213
  // ---------------------------------------------------------------------------
113
214
  // Security schemas
114
215
  // ---------------------------------------------------------------------------
115
216
 
116
- export const HandshakeInitV2Schema = Type.Object({
117
- version: Type.Literal(2),
118
- accountId: Type.String(),
119
- appEphemeralKey: Type.String(), // X25519 pub raw b64url
120
- nonce: Type.String(),
121
- timestamp: Type.Number(),
122
- });
123
-
124
- export type HandshakeInitV2 = Static<typeof HandshakeInitV2Schema>;
125
-
126
- export const HandshakeResponseV2Schema = Type.Object({
127
- version: Type.Literal(2),
128
- phase: Type.Literal("init"),
129
- complete: Type.Boolean(),
130
- pluginEphemeralKey: Type.String(), // X25519 pub raw b64url
131
- nonce: Type.String(),
132
- timestamp: Type.Number(),
133
- serverTimestamp: Type.Number(),
134
- challengeExpiresAt: Type.Number(),
135
- expiresAt: Type.Number(),
136
- pluginSignature: Type.String(), // Ed25519 sig over transcript, b64url
137
- });
138
-
139
- export type HandshakeResponseV2 = Static<typeof HandshakeResponseV2Schema>;
140
-
141
- export const EnvelopeV2Schema = Type.Object({
142
- v: Type.Literal(2),
143
- accountId: Type.String(),
144
- direction: Type.Union([Type.Literal("plugin_to_app"), Type.Literal("app_to_plugin")]),
145
- counter: Type.Number(),
146
- timestamp: Type.Number(),
147
- dhRatchetKey: Type.Optional(Type.String()),
148
- ciphertext: Type.String(),
149
- iv: Type.String(),
150
- tag: Type.String(),
151
- });
152
-
153
- export type EnvelopeV2Type = Static<typeof EnvelopeV2Schema>;
154
-
155
- export const PairRequestSchema = Type.Object({
156
- accountId: Type.String(),
157
- pairingToken: Type.String(),
158
- appIdentityKey: Type.String(), // Ed25519 pub raw b64url
159
- appDhKey: Type.String(), // X25519 pub raw b64url
160
- appSignature: Type.String(), // Ed25519 signature over pairing proof transcript
161
- });
162
-
163
- export type PairRequest = Static<typeof PairRequestSchema>;
164
-
165
- export const PairingQrPayloadSchema = Type.Object({
166
- version: Type.Literal(1),
167
- type: Type.Literal("soyeht_pairing_qr"),
168
- accountId: Type.String(),
169
- pairingToken: Type.String(),
170
- expiresAt: Type.Number(),
171
- allowOverwrite: Type.Boolean(),
172
- pluginIdentityKey: Type.String(),
173
- pluginDhKey: Type.String(),
174
- fingerprint: Type.String(),
175
- signature: Type.String(),
176
- });
177
-
178
- export type PairingQrPayload = Static<typeof PairingQrPayloadSchema>;
179
-
180
- export const HandshakeFinishV2Schema = Type.Object({
181
- version: Type.Literal(2),
182
- accountId: Type.String(),
183
- nonce: Type.String(),
184
- appSignature: Type.String(),
185
- });
186
-
187
- export type HandshakeFinishV2 = Static<typeof HandshakeFinishV2Schema>;
217
+ export type HandshakeInitV2 = {
218
+ version: 2;
219
+ accountId: string;
220
+ appEphemeralKey: string;
221
+ nonce: string;
222
+ timestamp: number;
223
+ };
224
+
225
+ export const HandshakeInitV2Schema: JsonSchema = {
226
+ type: "object",
227
+ additionalProperties: false,
228
+ required: ["version", "accountId", "appEphemeralKey", "nonce", "timestamp"],
229
+ properties: {
230
+ version: { const: 2 },
231
+ accountId: { type: "string" },
232
+ appEphemeralKey: { type: "string" },
233
+ nonce: { type: "string" },
234
+ timestamp: { type: "number" },
235
+ },
236
+ };
237
+
238
+ export type HandshakeResponseV2 = {
239
+ version: 2;
240
+ phase: "init";
241
+ complete: boolean;
242
+ pluginEphemeralKey: string;
243
+ nonce: string;
244
+ timestamp: number;
245
+ serverTimestamp: number;
246
+ challengeExpiresAt: number;
247
+ expiresAt: number;
248
+ pluginSignature: string;
249
+ };
250
+
251
+ export const HandshakeResponseV2Schema: JsonSchema = {
252
+ type: "object",
253
+ additionalProperties: false,
254
+ required: [
255
+ "version",
256
+ "phase",
257
+ "complete",
258
+ "pluginEphemeralKey",
259
+ "nonce",
260
+ "timestamp",
261
+ "serverTimestamp",
262
+ "challengeExpiresAt",
263
+ "expiresAt",
264
+ "pluginSignature",
265
+ ],
266
+ properties: {
267
+ version: { const: 2 },
268
+ phase: { const: "init" },
269
+ complete: { type: "boolean" },
270
+ pluginEphemeralKey: { type: "string" },
271
+ nonce: { type: "string" },
272
+ timestamp: { type: "number" },
273
+ serverTimestamp: { type: "number" },
274
+ challengeExpiresAt: { type: "number" },
275
+ expiresAt: { type: "number" },
276
+ pluginSignature: { type: "string" },
277
+ },
278
+ };
279
+
280
+ export type EnvelopeV2Type = {
281
+ v: 2;
282
+ accountId: string;
283
+ direction: "plugin_to_app" | "app_to_plugin";
284
+ counter: number;
285
+ timestamp: number;
286
+ dhRatchetKey?: string;
287
+ ciphertext: string;
288
+ iv: string;
289
+ tag: string;
290
+ };
291
+
292
+ export const EnvelopeV2Schema: JsonSchema = {
293
+ type: "object",
294
+ additionalProperties: false,
295
+ required: ["v", "accountId", "direction", "counter", "timestamp", "ciphertext", "iv", "tag"],
296
+ properties: {
297
+ v: { const: 2 },
298
+ accountId: { type: "string" },
299
+ direction: { enum: ["plugin_to_app", "app_to_plugin"] },
300
+ counter: { type: "number" },
301
+ timestamp: { type: "number" },
302
+ dhRatchetKey: { type: "string" },
303
+ ciphertext: { type: "string" },
304
+ iv: { type: "string" },
305
+ tag: { type: "string" },
306
+ },
307
+ };
308
+
309
+ export type PairRequest = {
310
+ accountId: string;
311
+ pairingToken: string;
312
+ appIdentityKey: string;
313
+ appDhKey: string;
314
+ appSignature: string;
315
+ };
316
+
317
+ export const PairRequestSchema: JsonSchema = {
318
+ type: "object",
319
+ additionalProperties: false,
320
+ required: ["accountId", "pairingToken", "appIdentityKey", "appDhKey", "appSignature"],
321
+ properties: {
322
+ accountId: { type: "string" },
323
+ pairingToken: { type: "string" },
324
+ appIdentityKey: { type: "string" },
325
+ appDhKey: { type: "string" },
326
+ appSignature: { type: "string" },
327
+ },
328
+ };
329
+
330
+ export type PairingQrPayload = {
331
+ version: 1;
332
+ type: "soyeht_pairing_qr";
333
+ accountId: string;
334
+ pairingToken: string;
335
+ expiresAt: number;
336
+ allowOverwrite: boolean;
337
+ pluginIdentityKey: string;
338
+ pluginDhKey: string;
339
+ fingerprint: string;
340
+ signature: string;
341
+ };
342
+
343
+ export const PairingQrPayloadSchema: JsonSchema = {
344
+ type: "object",
345
+ additionalProperties: false,
346
+ required: [
347
+ "version",
348
+ "type",
349
+ "accountId",
350
+ "pairingToken",
351
+ "expiresAt",
352
+ "allowOverwrite",
353
+ "pluginIdentityKey",
354
+ "pluginDhKey",
355
+ "fingerprint",
356
+ "signature",
357
+ ],
358
+ properties: {
359
+ version: { const: 1 },
360
+ type: { const: "soyeht_pairing_qr" },
361
+ accountId: { type: "string" },
362
+ pairingToken: { type: "string" },
363
+ expiresAt: { type: "number" },
364
+ allowOverwrite: { type: "boolean" },
365
+ pluginIdentityKey: { type: "string" },
366
+ pluginDhKey: { type: "string" },
367
+ fingerprint: { type: "string" },
368
+ signature: { type: "string" },
369
+ },
370
+ };
371
+
372
+ export type PairingQrPayloadV2 = {
373
+ version: 2;
374
+ type: "soyeht_pairing_qr";
375
+ gatewayUrl: string;
376
+ accountId: string;
377
+ pairingToken: string;
378
+ expiresAt: number;
379
+ allowOverwrite: boolean;
380
+ pluginIdentityKey: string;
381
+ pluginDhKey: string;
382
+ fingerprint: string;
383
+ signature: string;
384
+ };
385
+
386
+ export const PairingQrPayloadV2Schema: JsonSchema = {
387
+ type: "object",
388
+ additionalProperties: false,
389
+ required: [
390
+ "version",
391
+ "type",
392
+ "gatewayUrl",
393
+ "accountId",
394
+ "pairingToken",
395
+ "expiresAt",
396
+ "allowOverwrite",
397
+ "pluginIdentityKey",
398
+ "pluginDhKey",
399
+ "fingerprint",
400
+ "signature",
401
+ ],
402
+ properties: {
403
+ version: { const: 2 },
404
+ type: { const: "soyeht_pairing_qr" },
405
+ gatewayUrl: { type: "string", minLength: 1 },
406
+ accountId: { type: "string" },
407
+ pairingToken: { type: "string" },
408
+ expiresAt: { type: "number" },
409
+ allowOverwrite: { type: "boolean" },
410
+ pluginIdentityKey: { type: "string" },
411
+ pluginDhKey: { type: "string" },
412
+ fingerprint: { type: "string" },
413
+ signature: { type: "string" },
414
+ },
415
+ };
416
+
417
+ export type HandshakeFinishV2 = {
418
+ version: 2;
419
+ accountId: string;
420
+ nonce: string;
421
+ appSignature: string;
422
+ };
423
+
424
+ export const HandshakeFinishV2Schema: JsonSchema = {
425
+ type: "object",
426
+ additionalProperties: false,
427
+ required: ["version", "accountId", "nonce", "appSignature"],
428
+ properties: {
429
+ version: { const: 2 },
430
+ accountId: { type: "string" },
431
+ nonce: { type: "string" },
432
+ appSignature: { type: "string" },
433
+ },
434
+ };
188
435
 
189
436
  // ---------------------------------------------------------------------------
190
437
  // Channel capabilities
@@ -197,6 +444,11 @@ export const SOYEHT_CAPABILITIES = {
197
444
  voiceContractVersion: 1,
198
445
  pipeline: "stt->llm->tts" as const,
199
446
  supportedContentTypes: ["text", "audio", "file"] as const,
447
+ transport: {
448
+ direct: true,
449
+ backend: true,
450
+ defaultMode: "direct" as const,
451
+ },
200
452
  security: {
201
453
  version: 2,
202
454
  pairingMode: "qr_token" as const,
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const PLUGIN_VERSION = "0.1.1";
1
+ export const PLUGIN_VERSION = "0.2.0";