thinkpool-pair 0.7.181 → 0.7.182

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.
Files changed (2) hide show
  1. package/bridge.mjs +7 -47
  2. package/package.json +1 -1
package/bridge.mjs CHANGED
@@ -744,18 +744,11 @@ let myServeUid = null // this bridge's authed uid (null = anon)
744
744
  refreshOwnerPlan() // fire-and-forget on boot (codeAuthToken now set from env OR the stored login)
745
745
 
746
746
  // ── Pre-flight budget caps — spec docs/specs/2026-07-01-budget-caps.md (nexos steal #1).
747
- // The bridge calls the durable ledger RPCs as the OWNER (codeAuthToken); is_session_member
748
- // passes for this room, so the room-scope cap enforces. Member-scope attribution needs the
749
- // SENDER's uid (not available here — a turn carries a display label + cid, not a uuid), so
750
- // the baseline records/enforces ROOM scope only; per-member caps wait on the quota-fairness
751
- // work. TP_BUDGET_OFF disables the whole path. `roomCapped` caches whether a cap exists so a
752
- // transient ledger error fails CLOSED only for a room we KNOW is capped, and open otherwise.
747
+ // TP_BUDGET_OFF disables the spend-ledger path entirely (record_spend). The per-room output-
748
+ // token CAP was removed (2026-07) — redundant for BYOK (the provider enforces spend limits),
749
+ // a per-turn check_budget round-trip on every authed turn, and a fail-closed hazard. The
750
+ // spend LEDGER stays: it powers the Session-info "Session tokens" stat.
753
751
  const BUDGET_OFF = process.env.TP_BUDGET_OFF === '1'
754
- let roomCapped = false
755
- function monthResetLabel () {
756
- const d = new Date()
757
- return new Date(d.getFullYear(), d.getMonth() + 1, 1).toISOString().slice(0, 10)
758
- }
759
752
  async function budgetRpc (fn, args) {
760
753
  const r = await fetch(`${SUPABASE_URL}/rest/v1/rpc/${fn}`, {
761
754
  method: 'POST',
@@ -828,36 +821,6 @@ function recordCodeUsage (model, usage) {
828
821
  budgetRpc('record_code_usage', { p_room: room, p_model: model, p_input: input, p_output: output, p_cached: cached })
829
822
  .catch(() => { /* best-effort — usage accounting is not load-bearing for the turn */ })
830
823
  }
831
- // Pre-flight gate. Returns true if the turn must be REFUSED (room cap reached). Refusal is
832
- // legible (a ◆ control line both readers + late-joiners see), never a silent stall.
833
- async function budgetBlocked (s, term) {
834
- if (BUDGET_OFF || !codeAuthToken) return false
835
- const refuse = (text) => {
836
- const evt = { kind: 'control', text }
837
- try { pushLog(s, evt) } catch { /* no session log */ }
838
- bcast('code-event', { term, evt })
839
- return true
840
- }
841
- try {
842
- const rows = await budgetRpc('check_budget', { p_room: room })
843
- const r = Array.isArray(rows) ? rows[0] : rows
844
- roomCapped = !!(r && r.remaining_tokens != null)
845
- if (r && r.ok === false) {
846
- process.stderr.write(`\n ${A.yel}◆ turn refused — ${r.tightest_scope} budget cap reached (room ${room}).${A.rst}\n`)
847
- return refuse(`◆ Turn blocked — this room hit its ${r.tightest_scope || 'budget'} cap. Resets ${monthResetLabel()}, or raise the cap.`)
848
- }
849
- return false
850
- } catch (e) {
851
- // Fail-closed ONLY where we know a cap exists — a transient ledger/DNS blip must never
852
- // brick an UNCAPPED room (that would be a worse outage than the cap it's guarding).
853
- if (roomCapped) {
854
- process.stderr.write(`\n ${A.yel}◆ turn refused — budget check failed, room is capped (fail-closed).${A.rst}\n`)
855
- return refuse('◆ Turn blocked — budget check is unavailable and this room has a cap set. Try again in a moment.')
856
- }
857
- process.stderr.write(`\n ${A.dim}◆ budget check skipped (${e.message}) — room uncapped, proceeding.${A.rst}\n`)
858
- return false
859
- }
860
- }
861
824
  // Throttled presence track() (presence.mjs) — a reconnect storm re-fires SUBSCRIBED and
862
825
  // the handler re-track()s; without throttle that spirals into the per-client presence
863
826
  // rate limit (ClientPresenceRateLimitReached). Lazy so it binds `channel` (the chain
@@ -2244,8 +2207,9 @@ function openStructured({ id, model, resume, log, commands, mode, spawnedBy, rol
2244
2207
  const b = flowBudgets.get(entry.flowSessionId)
2245
2208
  if (b) flowBudgets.set(entry.flowSessionId, recordSpend(b, evt.usage.output_tokens || 0))
2246
2209
  }
2247
- // Budget caps — fold EVERY completed turn's output tokens into the room's durable
2248
- // monthly ledger (lanes + normal turns), so check_budget can pre-flight the next one.
2210
+ // Fold EVERY completed turn's output tokens into the room's durable monthly ledger
2211
+ // (lanes + normal turns) — powers the Session-info "Session tokens" stat. (The cap that
2212
+ // consumed this ledger was removed; the accounting stays.)
2249
2213
  if (evt.kind === 'result' && evt.usage) { recordRoomSpend(evt.usage.output_tokens || 0); recordCodeUsage(evt.model, evt.usage) }
2250
2214
  // FL-B1 (lane) — a flow lane that ENDS ITS TURN is done: a bypass lane runs its slice
2251
2215
  // to completion in one turn, then narrates "done" and stops. Models often skip the
@@ -2870,10 +2834,6 @@ channel
2870
2834
  // control lines (like /model) so both members AND late-joiners see it. Scope is honest —
2871
2835
  // the Agent SDK exposes tokens + cost, not plan/rate-limit meters, and the card says so.
2872
2836
  if (/^\/usage\s*$/.test(text)) { ctlLine(usageReportLine(s.model, s.log)); return }
2873
- // Pre-flight budget gate (room scope) — refuse the billable agent turn if the room hit
2874
- // its cap. The slash controls above (/model, /clear, /compact) are intentionally exempt:
2875
- // they let a user manage or REDUCE spend even when capped. Legible refusal, no silent stall.
2876
- if (await budgetBlocked(s, payload.term)) return
2877
2837
  // Context-carry (2026-07-08) point 4: a real human turn arrived BEFORE the post-switch/
2878
2838
  // restore recap fired on init-`system`. Prepend the recap to THIS turn (one turn, not two
2879
2839
  // — a separate recap turn would race + double-fire) and clear the flag so the init handler
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.181",
3
+ "version": "0.7.182",
4
4
  "description": "Share a local coding-agent CLI (Claude Code, Codex, Gemini, Aider, …) into a ThinkPool Code room, live.",
5
5
  "type": "module",
6
6
  "bin": {