@band-ai/band-sdk-core 2.0.0 → 2.1.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 CHANGED
@@ -148,6 +148,51 @@ native `Error` with the same `.issues`/`.traceContext` contract as
148
148
  `type` are independent issues, both reported when both strings are
149
149
  invalid.
150
150
 
151
+ ### One-shot delivery lifecycle
152
+
153
+ `evaluateDeliveryEvent`, `evaluateNextMessage`, `evaluateDrainCandidate`, and
154
+ `evaluateAdapterResult` are the stateless one-shot delivery lifecycle
155
+ decisions — design decisions live as rustdoc in
156
+ `crates/core/src/runtime/delivery.rs`. Each returns a plain tagged object;
157
+ there is no cross-invocation state, so there is no class here to construct.
158
+ Decision fields stay snake_case (message-wire vocabulary: `room_id`,
159
+ `sender_id`, `sender_type`, …), unlike this module's own camelCase
160
+ function/argument names, so the same shape returns identically from both
161
+ bindings.
162
+
163
+ `evaluateDeliveryEvent(eventType, roomId, payload, agentId, traceContext?)`
164
+ routes one inbound event: `"ignored"` for an unrecognized or out-of-scope
165
+ `eventType`; `"cleanup"` for `room_removed`/`room_deleted`; `"skip_self"` or
166
+ `"invocation"` for `message_created`, depending on whether the sender is
167
+ this agent (`sender_type === "Agent"` and `sender_id === agentId`).
168
+ `message_created` delegates to `validateEventPayload` for payload-shape
169
+ validation, so a malformed `payload` throws the same `Error`, and
170
+ additionally rejects an empty `payload.id` — a message with no identity
171
+ cannot be claimed or acknowledged. `room_removed`/`room_deleted` need no
172
+ payload shape and read `id` straight off the raw payload.
173
+
174
+ `roomId` resolves from the caller-supplied `roomId` first, else the payload's
175
+ own `chat_room_id` (message events) or `id` (room events). An empty string
176
+ counts as absent at both steps. Unresolvable throws an `Error` with one issue
177
+ on path `room_id`: code `missing` when the fallback field is absent or empty,
178
+ `wrong_type` when it is present but not a string.
179
+
180
+ `evaluateNextMessage(triggeringMessageId, nextMessageId)` compares the
181
+ triggering message against the platform's authoritative "next open message"
182
+ for a room (already fetched by the caller): `"no_pending"`,
183
+ `"already_processed"`, or `"ready_to_claim"`.
184
+
185
+ `evaluateDrainCandidate(candidate, seenIds, agentId)` classifies one
186
+ already-fetched drain candidate — `candidate` is `{id, sender_id,
187
+ sender_type}`, or `null`/`undefined` when the fetch returned nothing:
188
+ `"no_candidate"`, `"self_echo"` (checked before the snapshot, so an echo
189
+ never halts a drain), `"out_of_snapshot"` (stop), or `"drain"` (continue).
190
+ The caller's own bounded or unbounded loop owns the cap; this function
191
+ classifies one candidate per call.
192
+
193
+ `evaluateAdapterResult(roomId, messageId, succeeded)` maps an adapter
194
+ outcome to `"processed"` or `"failed"`.
195
+
151
196
  The public declaration is
152
197
  [`index.d.ts`](https://github.com/band-ai/band-sdk-core/blob/main/crates/wasm/index.d.ts).
153
198
 
package/band_sdk_core.js CHANGED
@@ -1206,6 +1206,82 @@ function classifyUpgrade(status) {
1206
1206
  }
1207
1207
  exports.classifyUpgrade = classifyUpgrade;
1208
1208
 
1209
+ /**
1210
+ * Adapter outcome -> ack decision. Throws a plain `Error` if `roomId`/
1211
+ * `messageId` is not a string.
1212
+ * @param {any} roomId
1213
+ * @param {any} messageId
1214
+ * @param {boolean} succeeded
1215
+ * @returns {any}
1216
+ */
1217
+ function evaluateAdapterResult(roomId, messageId, succeeded) {
1218
+ const ret = wasm.evaluateAdapterResult(roomId, messageId, succeeded);
1219
+ if (ret[2]) {
1220
+ throw takeFromExternrefTable0(ret[1]);
1221
+ }
1222
+ return takeFromExternrefTable0(ret[0]);
1223
+ }
1224
+ exports.evaluateAdapterResult = evaluateAdapterResult;
1225
+
1226
+ /**
1227
+ * Evaluate the routing decision for one inbound platform event.
1228
+ *
1229
+ * On failure throws a native `Error` with `.issues` (an array of
1230
+ * `{path, code, message}` objects) and `.traceContext`. A non-string
1231
+ * `eventType`/`roomId`/`agentId`/`traceContext`, or a `payload` that
1232
+ * cannot be converted, throws a plain `Error` without `.issues`.
1233
+ * @param {any} eventType
1234
+ * @param {any} roomId
1235
+ * @param {any} payload
1236
+ * @param {any} agentId
1237
+ * @param {any} traceContext
1238
+ * @returns {any}
1239
+ */
1240
+ function evaluateDeliveryEvent(eventType, roomId, payload, agentId, traceContext) {
1241
+ const ret = wasm.evaluateDeliveryEvent(eventType, roomId, payload, agentId, traceContext);
1242
+ if (ret[2]) {
1243
+ throw takeFromExternrefTable0(ret[1]);
1244
+ }
1245
+ return takeFromExternrefTable0(ret[0]);
1246
+ }
1247
+ exports.evaluateDeliveryEvent = evaluateDeliveryEvent;
1248
+
1249
+ /**
1250
+ * One step of the drain loop. `candidate` is `{id, sender_id,
1251
+ * sender_type}` or `null`/`undefined` when the host's fetch returned no
1252
+ * candidate; a malformed object throws a plain `Error`.
1253
+ * @param {any} candidate
1254
+ * @param {any} seenIds
1255
+ * @param {any} agentId
1256
+ * @returns {any}
1257
+ */
1258
+ function evaluateDrainCandidate(candidate, seenIds, agentId) {
1259
+ const ret = wasm.evaluateDrainCandidate(candidate, seenIds, agentId);
1260
+ if (ret[2]) {
1261
+ throw takeFromExternrefTable0(ret[1]);
1262
+ }
1263
+ return takeFromExternrefTable0(ret[0]);
1264
+ }
1265
+ exports.evaluateDrainCandidate = evaluateDrainCandidate;
1266
+
1267
+ /**
1268
+ * Compares the triggering message against the platform's authoritative
1269
+ * "next open message" for a room. Throws a plain `Error` if
1270
+ * `triggeringMessageId`/`nextMessageId` is not a string (or `null`/
1271
+ * `undefined` for `nextMessageId`).
1272
+ * @param {any} triggeringMessageId
1273
+ * @param {any} nextMessageId
1274
+ * @returns {any}
1275
+ */
1276
+ function evaluateNextMessage(triggeringMessageId, nextMessageId) {
1277
+ const ret = wasm.evaluateNextMessage(triggeringMessageId, nextMessageId);
1278
+ if (ret[2]) {
1279
+ throw takeFromExternrefTable0(ret[1]);
1280
+ }
1281
+ return takeFromExternrefTable0(ret[0]);
1282
+ }
1283
+ exports.evaluateNextMessage = evaluateNextMessage;
1284
+
1209
1285
  /**
1210
1286
  * This platform's Phoenix Channels topic for a room's `room_participants`
1211
1287
  * channel.
@@ -1386,6 +1462,10 @@ function __wbg_get_imports() {
1386
1462
  const ret = arg0[arg1 >>> 0];
1387
1463
  return ret;
1388
1464
  },
1465
+ __wbg_get_with_ref_key_6412cf3094599694: function(arg0, arg1) {
1466
+ const ret = arg0[arg1];
1467
+ return ret;
1468
+ },
1389
1469
  __wbg_instanceof_ArrayBuffer_993d02d2d254cad1: function(arg0) {
1390
1470
  let result;
1391
1471
  try {
Binary file
package/index.d.ts CHANGED
@@ -364,3 +364,139 @@ export class RoomRoster {
364
364
  reconcile(currentRoomIds: string[]): RoomReconciliation;
365
365
  clear(): string[];
366
366
  }
367
+
368
+ // The one-shot delivery lifecycle decisions: evaluateDeliveryEvent,
369
+ // evaluateNextMessage, evaluateDrainCandidate, evaluateAdapterResult.
370
+ // Each returns a plain tagged object -- these functions carry no
371
+ // cross-invocation state, so there is no class here to construct. Decision
372
+ // fields stay snake_case (message-wire vocabulary: `room_id`, `sender_id`,
373
+ // `sender_type`, ...), unlike this module's own camelCase function/argument
374
+ // names, so the same shape is returned identically from both bindings.
375
+
376
+ export interface EventIgnored {
377
+ decision: "ignored";
378
+ event_type: string;
379
+ }
380
+ export interface EventCleanup {
381
+ decision: "cleanup";
382
+ room_id: string;
383
+ }
384
+ export interface EventSkipSelf {
385
+ decision: "skip_self";
386
+ room_id: string;
387
+ message_id: string;
388
+ }
389
+ export interface EventInvocation {
390
+ decision: "invocation";
391
+ room_id: string;
392
+ message_id: string;
393
+ sender_id: string;
394
+ sender_type: string;
395
+ }
396
+ export type EventDecision = EventIgnored | EventCleanup | EventSkipSelf | EventInvocation;
397
+
398
+ /**
399
+ * Evaluate the routing decision for one inbound platform event.
400
+ *
401
+ * `message_created` delegates to {@link validateEventPayload}'s own
402
+ * validation; a rejection there throws the same native `Error` with
403
+ * `.issues`/`.traceContext`, as does an empty `payload.id`.
404
+ * `room_removed`/`room_deleted` need no payload shape and read `id` straight
405
+ * off the raw payload. Both event kinds throw that same `Error` shape when
406
+ * `roomId` resolves from neither the envelope nor the payload — an empty
407
+ * string counting as absent — with issue code `missing` when the fallback
408
+ * field is absent or empty and `wrong_type` when it is present but not a
409
+ * string. Throws a plain `Error` without `.issues` if
410
+ * `eventType`/`roomId`/`agentId`/`traceContext` is not a string (or not
411
+ * `null`/`undefined` for `roomId`/`traceContext`), or if `payload` cannot be
412
+ * converted.
413
+ */
414
+ export function evaluateDeliveryEvent(
415
+ eventType: string,
416
+ roomId: string | null | undefined,
417
+ payload: unknown,
418
+ agentId: string,
419
+ traceContext?: string | null,
420
+ ): EventDecision;
421
+
422
+ export interface NextMessageNoPending {
423
+ decision: "no_pending";
424
+ }
425
+ export interface NextMessageAlreadyProcessed {
426
+ decision: "already_processed";
427
+ next_open_id: string;
428
+ }
429
+ export interface NextMessageReadyToClaim {
430
+ decision: "ready_to_claim";
431
+ }
432
+ export type NextMessageDecision =
433
+ | NextMessageNoPending
434
+ | NextMessageAlreadyProcessed
435
+ | NextMessageReadyToClaim;
436
+
437
+ /**
438
+ * Compares the triggering message against the platform's authoritative
439
+ * "next open message" for a room. Throws a plain `Error` if
440
+ * `triggeringMessageId` is not a string, or `nextMessageId` is not a
441
+ * string, `null`, or `undefined`.
442
+ */
443
+ export function evaluateNextMessage(
444
+ triggeringMessageId: string,
445
+ nextMessageId: string | null | undefined,
446
+ ): NextMessageDecision;
447
+
448
+ /** One already-fetched drain candidate, matching `evaluateDeliveryEvent`'s own field names. */
449
+ export interface DrainCandidate {
450
+ id: string;
451
+ sender_id: string;
452
+ sender_type: string;
453
+ }
454
+ export interface DrainNoCandidate {
455
+ decision: "no_candidate";
456
+ }
457
+ export interface DrainOutOfSnapshot {
458
+ decision: "out_of_snapshot";
459
+ message_id: string;
460
+ }
461
+ export interface DrainSelfEcho {
462
+ decision: "self_echo";
463
+ message_id: string;
464
+ }
465
+ export interface DrainMessage {
466
+ decision: "drain";
467
+ message_id: string;
468
+ }
469
+ export type DrainStep = DrainNoCandidate | DrainOutOfSnapshot | DrainSelfEcho | DrainMessage;
470
+
471
+ /**
472
+ * One step of the drain loop. `candidate` is `null`/`undefined` when the
473
+ * host's fetch returned no candidate. Throws a plain `Error` if
474
+ * `candidate` is present but malformed, or if `agentId` is not a string.
475
+ */
476
+ export function evaluateDrainCandidate(
477
+ candidate: DrainCandidate | null | undefined,
478
+ seenIds: string[],
479
+ agentId: string,
480
+ ): DrainStep;
481
+
482
+ export interface AckProcessed {
483
+ decision: "processed";
484
+ room_id: string;
485
+ message_id: string;
486
+ }
487
+ export interface AckFailed {
488
+ decision: "failed";
489
+ room_id: string;
490
+ message_id: string;
491
+ }
492
+ export type AckDecision = AckProcessed | AckFailed;
493
+
494
+ /**
495
+ * Adapter outcome -> ack decision. Throws a plain `Error` if `roomId`/
496
+ * `messageId` is not a string.
497
+ */
498
+ export function evaluateAdapterResult(
499
+ roomId: string,
500
+ messageId: string,
501
+ succeeded: boolean,
502
+ ): AckDecision;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@band-ai/band-sdk-core",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Shared Band event-payload validation",
5
5
  "license": "MIT",
6
6
  "repository": {