@clocknext/sdk 0.12.1 → 0.14.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
@@ -234,7 +234,9 @@ than drawing on a named entitlement — and cannot be grouped into a composite.
234
234
  ### Outcome runs
235
235
 
236
236
  An outcome is billed once per run, not once per step. Reuse the same `runId` for
237
- every step and set `complete: true` only on the final one.
237
+ every step, then close the run with a separate, dedicated call —
238
+ `signals.completeOutcome()`. `signals.outcome()` accepts ONLY the step shape;
239
+ there is no `complete` flag to set on a step.
238
240
 
239
241
  ```ts
240
242
  const runId = "ticket_9021";
@@ -252,13 +254,18 @@ await cnk.signals.outcome({
252
254
  model: model.modelId,
253
255
  agentKey: "resolved_ticket.resolve",
254
256
  runId,
255
- complete: true,
256
257
  tokens: { input: 500, output: 200 },
257
258
  });
259
+
260
+ // Closing is ALWAYS its own explicit call — never a flag riding along on a
261
+ // step's real usage.
262
+ await cnk.signals.completeOutcome({ customerId, runId });
258
263
  ```
259
264
 
260
265
  A step may repeat within a run — a verify loop firing three times is three
261
- signals and one outcome. `complete: true` twice is a safe no-op.
266
+ signals and one outcome. Sending `completeOutcome()` twice for the same run is
267
+ a safe no-op; a genuinely SECOND, independent completion for an
268
+ already-completed run is a permanent error (it would bill the run again).
262
269
 
263
270
  ### Unit usage
264
271
 
@@ -291,7 +298,7 @@ await cnk.signals.credit({
291
298
  await cnk.signals.completeComposite({ customerId, composite });
292
299
  ```
293
300
 
294
- **An outcome's `complete: true` does not close a composite occurrence.** It
301
+ **`signals.completeOutcome()` does not close a composite occurrence.** It
295
302
  closes the run, and nothing else — whatever mix of credits, outcomes and units
296
303
  the composite wraps. If you tag outcome signals with a `composite`, you must
297
304
  still call `completeComposite()`, or that occurrence stays open and is never
@@ -503,12 +510,20 @@ await cnk.signals.list({ customerId, limit: 50 });
503
510
  await cnk.signals.unitUsage({ customerId, limit: 50 });
504
511
  ```
505
512
 
506
- Adjustments use stable agent keys, not display names:
513
+ Adjustments use stable agent keys, not display names. All four return the
514
+ resulting `{ remaining }` balance; a reduce is never rejected or floored for
515
+ going below 0 — the resulting balance may read negative, same as a real-usage
516
+ overdraft already can.
507
517
 
508
518
  ```ts
509
519
  await cnk.customers.adjustCredit(customerId, { agentKey: "api_credit", delta: 100 });
510
520
  await cnk.customers.adjustOutcome(customerId, { agentKey: "resolved_ticket.triage", delta: 1 });
511
521
  await cnk.customers.adjustUnit(customerId, { agentKey: "seats", delta: 5 });
522
+
523
+ // Composite: `agentKey` here is actually the composite's `refId` — the same
524
+ // value signals tag it with — named `agentKey` on the wire so all four share
525
+ // one request shape. Adjusts the CURRENT billing cycle's ADVANCE slot pool.
526
+ await cnk.customers.adjustComposite(customerId, { agentKey: "voice_ai", delta: 2 });
512
527
  ```
513
528
 
514
529
  ## Customers, plans, and billing
package/dist/index.cjs CHANGED
@@ -328,7 +328,7 @@ function sleep(ms) {
328
328
  }
329
329
 
330
330
  // src/http.ts
331
- var SDK_VERSION = "0.12.1" ;
331
+ var SDK_VERSION = "0.14.0" ;
332
332
  var Transport = class {
333
333
  constructor(cfg) {
334
334
  this.cfg = cfg;
@@ -704,13 +704,15 @@ var Customers = class {
704
704
  // (PATCH …/:id) — there is no separate currency endpoint. Credit-type
705
705
  // assignment has no public API.
706
706
  // --- Balance adjustments -------------------------------------------------
707
- /** Grant or claw back a credit balance. `POST …/:id/credits/adjust`. */
707
+ /** Grant or claw back a credit balance. Returns the remaining balance.
708
+ * `POST …/:id/credits/adjust`. */
708
709
  async adjustCredit(id, input) {
709
- await this.transport.request({
710
+ const res = await this.transport.request({
710
711
  method: "POST",
711
712
  path: `/api/v1/customers/${encodeURIComponent(id)}/credits/adjust`,
712
713
  body: input
713
714
  });
715
+ return { remaining: res.remaining };
714
716
  }
715
717
  /** Grant or claw back an outcome balance. Returns the remaining balance. */
716
718
  async adjustOutcome(id, input) {
@@ -730,6 +732,19 @@ var Customers = class {
730
732
  });
731
733
  return { remaining: res.remaining };
732
734
  }
735
+ /**
736
+ * Grant or claw back slots on a customer's ADVANCE composite pool for the
737
+ * current billing cycle. Returns the resulting `remaining` slot count.
738
+ * `POST …/:id/composites/adjust`.
739
+ */
740
+ async adjustComposite(id, input) {
741
+ const res = await this.transport.request({
742
+ method: "POST",
743
+ path: `/api/v1/customers/${encodeURIComponent(id)}/composites/adjust`,
744
+ body: input
745
+ });
746
+ return { remaining: res.remaining };
747
+ }
733
748
  };
734
749
 
735
750
  // src/resources/invoices.ts
@@ -1015,6 +1030,8 @@ var SIGNAL_PATHS = {
1015
1030
  };
1016
1031
  var UNIT_PATH = "/api/v1/signal/unit";
1017
1032
  var COMPOSITE_COMPLETE_PATH = "/api/v1/signal/composite/complete";
1033
+ var OUTCOME_COMPLETE_PATH = "/api/v1/signal/outcome/complete";
1034
+ var USAGE_LOG_PATH = "/api/v1/signal/usage-log";
1018
1035
  function signalToWire(signal) {
1019
1036
  const t = signal.tokens;
1020
1037
  const body = {
@@ -1035,7 +1052,6 @@ function signalToWire(signal) {
1035
1052
  }
1036
1053
  if (signal.type === "outcome") {
1037
1054
  body.runId = signal.runId;
1038
- if (signal.complete != null) body.complete = signal.complete;
1039
1055
  }
1040
1056
  if (signal.type !== "wallet" && signal.composite) {
1041
1057
  body.composite = { [signal.composite.ref]: signal.composite.value };
@@ -1105,6 +1121,48 @@ var Signals = class {
1105
1121
  outcome(input, opts) {
1106
1122
  return this.track({ ...input, type: "outcome" }, opts);
1107
1123
  }
1124
+ /**
1125
+ * Close ONE outcome run — declares the workflow finished and bills it. The
1126
+ * outcome mirror of {@link Signals.completeComposite}, on its own dedicated
1127
+ * route: `POST /api/v1/signal/outcome/complete`.
1128
+ *
1129
+ * An outcome bills per COMPLETION, never per step, and `signals.outcome()`
1130
+ * never closes one — it accepts only the step shape. Declaring the workflow
1131
+ * finished is always this separate, explicit, contentless call.
1132
+ *
1133
+ * This closes the RUN only. If a step of this run also carried a
1134
+ * `composite`, that occurrence stays open and unbilled until you separately
1135
+ * call {@link Signals.completeComposite} — the two completions are
1136
+ * independent, and neither implies the other.
1137
+ *
1138
+ * Sent immediately, never buffered. A genuinely second, independent call for
1139
+ * an already-closed run is a PERMANENT error (it would bill the run twice),
1140
+ * not a safe no-op — see {@link OutcomeCompletionResult}.
1141
+ */
1142
+ async completeOutcome(input) {
1143
+ const idempotencyKey = input.idempotencyKey ?? newKey();
1144
+ const res = await this.transport.request({
1145
+ method: "POST",
1146
+ path: OUTCOME_COMPLETE_PATH,
1147
+ idempotencyKey,
1148
+ body: {
1149
+ customerId: input.customerId,
1150
+ runId: input.runId,
1151
+ complete: true,
1152
+ ...input.member ? { member: input.member } : {},
1153
+ idempotencyKey
1154
+ },
1155
+ retry: true
1156
+ });
1157
+ const queued = res.queued === true;
1158
+ return {
1159
+ delivery: queued ? "accepted" : "recorded",
1160
+ queued,
1161
+ ...res.status != null ? { status: res.status } : {},
1162
+ usageLog: res.usageLog ?? null,
1163
+ ...handles(res)
1164
+ };
1165
+ }
1108
1166
  /**
1109
1167
  * Recent usage logs + totals for a customer. `GET /api/v1/usage`.
1110
1168
  *
@@ -1177,13 +1235,13 @@ var Signals = class {
1177
1235
  * arrive in any order and the occurrence stays open and unbilled until you
1178
1236
  * say otherwise. Put it on the line before your own `return`.
1179
1237
  *
1180
- * EVERY composite needs this, outcome-backed ones included. An outcome's own
1181
- * `complete: true` closes its RUN and nothing else — it does not close the
1182
- * composite occurrence the run was tagged into, whatever mix of credits,
1183
- * outcomes and units that composite wraps. (Earlier versions of this SDK
1184
- * documented the opposite. Believing it means the occurrence stays ONGOING
1185
- * forever and is never billed, so if you tag outcome signals with a
1186
- * `composite`, you must still call this.)
1238
+ * EVERY composite needs this, outcome-backed ones included. `completeOutcome()`
1239
+ * closes its RUN and nothing else — it does not close the composite
1240
+ * occurrence the run was tagged into, whatever mix of credits, outcomes and
1241
+ * units that composite wraps. (Earlier versions of this SDK documented the
1242
+ * opposite. Believing it means the occurrence stays ONGOING forever and is
1243
+ * never billed, so if you tag outcome signals with a `composite`, you must
1244
+ * still call this.)
1187
1245
  *
1188
1246
  * Closing twice is safe: the second call answers `ALREADY_COMPLETED` and
1189
1247
  * cannot bill again. Calling it EARLY is also safe — before every entitlement
@@ -1232,6 +1290,37 @@ var Signals = class {
1232
1290
  query: { customerId: params.customerId, limit: params.limit }
1233
1291
  });
1234
1292
  }
1293
+ /**
1294
+ * One customer's usage history, merged from every meter kind — credit,
1295
+ * outcome, unit, wallet, and completed composite occurrences — into one
1296
+ * feed sorted by time. `GET /api/v1/signal/usage-log`. Exactly the same
1297
+ * rows the customer portal's own "Usage logs" table shows, so a caller can
1298
+ * build against this instead of the portal UI.
1299
+ *
1300
+ * Cursor-paginated, NOT page-numbered: `limit` + `cursor` in, `nextCursor`
1301
+ * out. Omit `cursor` for the first page; pass the previous page's
1302
+ * `nextCursor` back to fetch the next one, and stop once it comes back
1303
+ * `null`. Unlike offset pagination, the cost of a page never grows with how
1304
+ * deep you've paged — each page only asks for rows strictly before the
1305
+ * cursor's position.
1306
+ *
1307
+ * Read-only; unlike {@link track}/{@link unit}/etc., there is nothing to
1308
+ * buffer or retry here.
1309
+ */
1310
+ usageLog(params) {
1311
+ return this.transport.request({
1312
+ method: "GET",
1313
+ path: USAGE_LOG_PATH,
1314
+ query: {
1315
+ customerId: params.customerId,
1316
+ limit: params.limit,
1317
+ cursor: params.cursor,
1318
+ period: params.period,
1319
+ from: params.from,
1320
+ to: params.to
1321
+ }
1322
+ });
1323
+ }
1235
1324
  };
1236
1325
 
1237
1326
  // src/resources/units.ts