threadwire 0.1.28 → 0.1.30

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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Fix `threadwire capacity --provider kimi` for the current Kimi usages API:
6
+ quota objects may provide either `used` or `remaining` alongside `limit` and
7
+ `resetTime`. Threadwire derives the omitted value while retaining strict
8
+ decimal, bounds, and consistency validation.
9
+
10
+ - Upgrade `release-patch` from 1.0.3 to 1.0.5 so npm registry visibility is
11
+ checked for up to 10 attempts over 150 seconds without repeating the publish.
12
+
5
13
  - Add an optional loopback-only TensorBuzz-to-Telegram queue bridge. It verifies
6
14
  bounded raw-body HMAC-SHA-256 requests before JSON parsing, admits a closed
7
15
  `build_group.completed` schema, builds a bounded canonical `/queue` prompt,
@@ -80,11 +80,13 @@ pin before relying on it.
80
80
  to `short`; every returned window is validated (`window.duration` in
81
81
  `TIME_UNIT_MINUTE`s, `detail` quota) but intermediate windows are ignored,
82
82
  and normalization fails closed if the long duration would not exceed the
83
- short. Quota fields are strict non-negative decimal strings validated with
84
- `used + remaining === limit` and a positive limit, reset times must be
85
- bounded ISO-8601, and remaining percent is floored so normalization never
86
- overstates capacity. Unrelated top-level response fields are neither read
87
- nor propagated into output or errors.
83
+ short. Quota fields are strict non-negative decimal strings with a positive
84
+ limit. Kimi may provide either `used` or `remaining` (or both); Threadwire
85
+ derives the omitted side from `limit` and, when both are present, requires
86
+ `used + remaining === limit`. Reset times must be bounded ISO-8601, and
87
+ remaining percent is floored so normalization never overstates capacity.
88
+ Unrelated top-level response fields are neither read nor propagated into
89
+ output or errors.
88
90
 
89
91
  Both probes are bounded (capped lines/bytes, single absolute timeout) and fail
90
92
  closed on any shape drift: exact keys within the parsed projection, forced
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadwire",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "Stream Codex, Claude, Kimi Code, and OpenCode worker progress to an explicit Telegram destination",
5
5
  "keywords": [
6
6
  "ai-agent",
@@ -68,7 +68,7 @@
68
68
  "eslint-plugin-jsdoc": "63.0.0",
69
69
  "fallow": "3.2.0",
70
70
  "globals": "17.0.0",
71
- "release-patch": "1.0.3",
71
+ "release-patch": "1.0.5",
72
72
  "typescript": "6.0.2"
73
73
  },
74
74
  "license": "AGPL-3.0-only",
@@ -199,21 +199,25 @@ function kimiLimitsWindow(value) {
199
199
 
200
200
  /**
201
201
  * Validate one quota object (`usage` or `detail`): exact keys, strict
202
- * non-negative decimal-string integers with `used + remaining === limit`, a
203
- * positive limit, and a bounded ISO-8601 reset time.
202
+ * non-negative decimal-string integers with either `used` or `remaining` (or
203
+ * both), a positive limit, and a bounded ISO-8601 reset time. Derive the
204
+ * omitted side from the limit and require equality when both are present.
204
205
  * @param {unknown} value
205
206
  * @param {number} windowDurationSeconds
206
207
  * @returns {CapacityWindow}
207
208
  */
208
209
  function kimiQuotaWindow(value, windowDurationSeconds) {
209
- if (!isRecord(value) || !exactKeys(value, ["limit", "used", "remaining", "resetTime"])) {
210
+ if (!isRecord(value) || !kimiQuotaKeys(value)) {
210
211
  throw kimiProtocolError()
211
212
  }
212
213
  const limit = quotaValue(value.limit)
213
- const used = quotaValue(value.used)
214
- const remaining = quotaValue(value.remaining)
215
- if (limit < 1 || used + remaining !== limit) throw kimiProtocolError()
216
- const remainingPercent = Number((BigInt(remaining) * 100n) / BigInt(limit))
214
+ const used = value.used === undefined ? null : quotaValue(value.used)
215
+ const remaining = value.remaining === undefined ? null : quotaValue(value.remaining)
216
+ if (limit < 1 || (used !== null && used > limit) || (remaining !== null && remaining > limit)
217
+ || (used !== null && remaining !== null && used + remaining !== limit)) throw kimiProtocolError()
218
+ if (used === null && remaining === null) throw kimiProtocolError()
219
+ const normalizedRemaining = remaining === null ? limit - /** @type {number} */ (used) : remaining
220
+ const remainingPercent = Number((BigInt(normalizedRemaining) * 100n) / BigInt(limit))
217
221
  return {
218
222
  usedPercent: 100 - remainingPercent,
219
223
  remainingPercent,
@@ -222,6 +226,14 @@ function kimiQuotaWindow(value, windowDurationSeconds) {
222
226
  }
223
227
  }
224
228
 
229
+ /** @param {Record<string, unknown>} value @returns {boolean} */
230
+ function kimiQuotaKeys(value) {
231
+ const keys = Object.keys(value).sort()
232
+ return exactKeys(value, ["limit", "used", "remaining", "resetTime"])
233
+ || (keys.length === 3 && keys.includes("limit") && keys.includes("resetTime")
234
+ && (keys.includes("used") || keys.includes("remaining")))
235
+ }
236
+
225
237
  /**
226
238
  * A strict non-negative decimal-string integer, digit-bounded so the sum of
227
239
  * two values always stays a safe integer.