clixad 0.0.1-beta.25 → 0.0.1-beta.27

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
@@ -15,6 +15,12 @@ cover it, for example `task ≈ 560–3.1k · free` or `task ≈ 7.5k–47k · n
15
15
  estimate from the size of the conversation and how tasks usually run; what is charged is what the
16
16
  model actually used.
17
17
 
18
+ `/effort` sets how long the model thinks before it answers, saved separately for each model.
19
+ Thinking is billed as output tokens like the answer, so a higher setting costs more credits per turn
20
+ and a lower one costs less. `auto` leaves the model at its own default, and a model with nothing to
21
+ adjust says so. The cost preview is not shown while a setting above the model's default is active,
22
+ because it is measured at the defaults.
23
+
18
24
  **Early beta.** Commands, interfaces and credit mechanics may change between beta versions. Run the
19
25
  most recent published build with:
20
26
 
package/dist/clixad.mjs CHANGED
@@ -302,6 +302,23 @@ import { gzipSync } from "node:zlib";
302
302
  function encodeChatBody(json) {
303
303
  return gzipSync(Buffer.from(json, "utf8"));
304
304
  }
305
+ function accessColumn(m, premium) {
306
+ if (m.access === void 0) return m.tier.padEnd(8);
307
+ if (m.access === "budget") return "budget ";
308
+ return premiumLocked(premium) ? "locked " : "premium ";
309
+ }
310
+ function premiumLocked(premium) {
311
+ return premium !== void 0 && !premium.unlocked && premium.credits <= 0;
312
+ }
313
+ function premiumUnlockHint(unlockUsd) {
314
+ return typeof unlockUsd === "number" && unlockUsd > 0 ? `premium: a survey or ${usd(unlockUsd)} unlocks it` : "premium: a survey unlocks it";
315
+ }
316
+ function premiumHeadline(unlockUsd) {
317
+ return typeof unlockUsd === "number" && unlockUsd > 0 ? `Premium: complete a survey or unlock from ${usd(unlockUsd)}.` : "Premium: complete a survey to unlock it.";
318
+ }
319
+ function usd(v) {
320
+ return Number.isInteger(v) ? `$${v}` : `$${v.toFixed(2)}`;
321
+ }
305
322
  function adsPerTaskLabel(estAdsPerTask) {
306
323
  const effort = estAdsPerTask >= 1 ? `~${estAdsPerTask} ads/task` : "<1 ad/task";
307
324
  return `${effort} on completion (median)`;
@@ -405,16 +422,35 @@ function asSignupClosed(status, data) {
405
422
  const d = data;
406
423
  return d && d.error === "signup_closed" && typeof d.message === "string" ? { error: d.error, message: d.message, waitlist_url: d.waitlist_url ?? "https://clixad.io" } : void 0;
407
424
  }
425
+ function premiumLockedError(message, model, body) {
426
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
427
+ return new PremiumLockedError(
428
+ message,
429
+ typeof body?.model === "string" ? body.model : model,
430
+ num(body?.needed_credits),
431
+ num(body?.premium_credits),
432
+ typeof body?.unlock_usd === "number" && body.unlock_usd > 0 ? body.unlock_usd : null,
433
+ num(body?.grant_per_ad),
434
+ freeAlternatives(body?.free_models)
435
+ );
436
+ }
408
437
  function shortfallHeadline(err, model) {
409
438
  const n = (v) => Math.max(0, Math.round(v)).toLocaleString("en-US");
410
439
  if (err.balance <= 0) return `Out of credits (this needs about ${n(err.estimatedCost)}).`;
411
440
  return `Not enough credits for this turn${model ? ` on ${model}` : ""}: it needs about ${n(err.estimatedCost)}, and the balance is ${n(err.balance)}.`;
412
441
  }
413
442
  function paywallError(body) {
414
- const free = Array.isArray(body.free_models) ? body.free_models.filter(
443
+ return new PaywallError(body.balance, body.estimated_cost, body.grant_per_ad, freeAlternatives(body.free_models));
444
+ }
445
+ function freeAlternatives(list) {
446
+ return Array.isArray(list) ? list.filter(
415
447
  (m) => !!m && typeof m.id === "string" && m.id.length > 0 && Number.isFinite(m.turns_left)
416
448
  ).map((m) => ({ id: m.id, turnsLeft: Math.max(0, Math.floor(m.turns_left)) })).filter((m) => m.turnsLeft > 0) : [];
417
- return new PaywallError(body.balance, body.estimated_cost, body.grant_per_ad, free);
449
+ }
450
+ async function refusal402(res, model) {
451
+ const body = await res.json();
452
+ if (body.error === "premium_locked") return premiumLockedError(body.message ?? "", model, body.premium);
453
+ return paywallError(body);
418
454
  }
419
455
  function neverConnected(code) {
420
456
  return code !== void 0 && NEVER_CONNECTED.has(code);
@@ -540,7 +576,7 @@ async function* parseSSE(body) {
540
576
  }
541
577
  }
542
578
  }
543
- var CHAT_ENCODED_CONTENT_TYPE, LEDGER_REASONS, SignupClosedError, PaywallError, GatewayUnreachableError, NEVER_CONNECTED, AuthError, GatewayClient;
579
+ var CHAT_ENCODED_CONTENT_TYPE, LEDGER_REASONS, SignupClosedError, PaywallError, PremiumLockedError, GatewayUnreachableError, NEVER_CONNECTED, AuthError, GatewayClient;
544
580
  var init_client = __esm({
545
581
  "src/client.ts"() {
546
582
  "use strict";
@@ -590,6 +626,24 @@ var init_client = __esm({
590
626
  grantPerAd;
591
627
  freeModels;
592
628
  };
629
+ PremiumLockedError = class extends Error {
630
+ constructor(message, model, neededCredits, premiumCredits, unlockUsd, grantPerAd, freeModels) {
631
+ super(message);
632
+ this.model = model;
633
+ this.neededCredits = neededCredits;
634
+ this.premiumCredits = premiumCredits;
635
+ this.unlockUsd = unlockUsd;
636
+ this.grantPerAd = grantPerAd;
637
+ this.freeModels = freeModels;
638
+ this.name = "PremiumLockedError";
639
+ }
640
+ model;
641
+ neededCredits;
642
+ premiumCredits;
643
+ unlockUsd;
644
+ grantPerAd;
645
+ freeModels;
646
+ };
593
647
  GatewayUnreachableError = class extends Error {
594
648
  constructor(host, code) {
595
649
  const where = `${host}${code ? ` (${code})` : ""}`;
@@ -886,6 +940,7 @@ var init_client = __esm({
886
940
  if (!res.ok) throw new Error(`models failed: ${res.status}`);
887
941
  const data = await res.json();
888
942
  this.seen = { web_search: data.capabilities?.web_search === true };
943
+ this.unlockUsd = typeof data.premium_unlock_usd === "number" && data.premium_unlock_usd > 0 ? data.premium_unlock_usd : null;
889
944
  this.shape = parseTaskShape(data.task_shape);
890
945
  this.latest = typeof data.cli_latest === "string" ? data.cli_latest : void 0;
891
946
  const live = new Set(data.data.map((m) => m.id));
@@ -936,8 +991,23 @@ var init_client = __esm({
936
991
  return this.seen;
937
992
  }
938
993
  seen = { web_search: false };
939
- async wallet(signal) {
940
- const res = await gatewayFetch(`${this.config.gatewayUrl}/v1/wallet`, { headers: this.headers(), signal });
994
+ /**
995
+ * What unlocks premium for every credit on the account (the cheapest pack,
996
+ * USD), as of the last catalog fetch. Null where the gateway sells none or
997
+ * did not say, which the hints read as "a survey unlocks it".
998
+ */
999
+ get premiumUnlockUsd() {
1000
+ return this.unlockUsd;
1001
+ }
1002
+ unlockUsd = null;
1003
+ /**
1004
+ * `premium: true` asks for premium access too, which costs the gateway a read
1005
+ * of this account's whole ledger. Asked for where it is shown (`/model`,
1006
+ * `clixad models`), never by the polls `/earn` and a purchase run.
1007
+ */
1008
+ async wallet(signal, opts = {}) {
1009
+ const url = `${this.config.gatewayUrl}/v1/wallet${opts.premium ? "?premium=1" : ""}`;
1010
+ const res = await gatewayFetch(url, { headers: this.headers(), signal });
941
1011
  if (res.status === 401) throw await authFailure(res, "wallet");
942
1012
  if (!res.ok) throw new Error(`wallet failed: ${res.status}`);
943
1013
  return res.json();
@@ -1228,15 +1298,13 @@ var init_client = __esm({
1228
1298
  return retry;
1229
1299
  }
1230
1300
  /** Non-streaming chat. Kept for scripted/one-shot paths; the REPL streams. */
1231
- async chat(messages, model, tools, signal) {
1301
+ async chat(messages, model, tools, signal, effort) {
1232
1302
  const res = await this.postChat(
1233
- { model, messages, stream: false, ...tools ? { tools } : {} },
1303
+ { model, messages, stream: false, ...tools ? { tools } : {}, ...effort ? { reasoning_effort: effort } : {} },
1234
1304
  signal
1235
1305
  );
1236
1306
  if (res.status === 401) throw await authFailure(res, "chat");
1237
- if (res.status === 402) {
1238
- throw paywallError(await res.json());
1239
- }
1307
+ if (res.status === 402) throw await refusal402(res, model);
1240
1308
  if (!res.ok) throw new Error(await httpFailure(res, "chat"));
1241
1309
  const data = await res.json();
1242
1310
  const choice = data.choices[0];
@@ -1257,13 +1325,17 @@ var init_client = __esm({
1257
1325
  */
1258
1326
  async chatStream(messages, model, opts = {}) {
1259
1327
  const res = await this.postChat(
1260
- { model, messages, stream: true, ...opts.tools ? { tools: opts.tools } : {} },
1328
+ {
1329
+ model,
1330
+ messages,
1331
+ stream: true,
1332
+ ...opts.tools ? { tools: opts.tools } : {},
1333
+ ...opts.effort ? { reasoning_effort: opts.effort } : {}
1334
+ },
1261
1335
  opts.signal
1262
1336
  );
1263
1337
  if (res.status === 401) throw await authFailure(res, "chat");
1264
- if (res.status === 402) {
1265
- throw paywallError(await res.json());
1266
- }
1338
+ if (res.status === 402) throw await refusal402(res, model);
1267
1339
  if (!res.ok || !res.body) throw new Error(await httpFailure(res, "chat"));
1268
1340
  let content = "";
1269
1341
  let finishReason = "stop";
@@ -1271,6 +1343,7 @@ var init_client = __esm({
1271
1343
  let free = {};
1272
1344
  let balance = 0;
1273
1345
  let streamError;
1346
+ let premiumRefusal;
1274
1347
  const calls = /* @__PURE__ */ new Map();
1275
1348
  const reasoning = [];
1276
1349
  for await (const evt of parseSSE(res.body)) {
@@ -1293,12 +1366,16 @@ var init_client = __esm({
1293
1366
  mergeReasoningDetails(reasoning, choice?.delta?.reasoning_details);
1294
1367
  if (choice?.finish_reason) finishReason = choice.finish_reason;
1295
1368
  if (json.error) streamError = json.error.message || "the model stream failed";
1369
+ if (json.error?.type === "premium_locked") {
1370
+ premiumRefusal = premiumLockedError(streamError ?? "", model, json.clixad?.premium);
1371
+ }
1296
1372
  if (json.clixad) {
1297
1373
  creditsCharged = json.clixad.credits_charged;
1298
1374
  balance = json.clixad.balance;
1299
1375
  free = freeMeta(json.clixad);
1300
1376
  }
1301
1377
  }
1378
+ if (premiumRefusal) throw premiumRefusal;
1302
1379
  if (streamError) throw new Error(streamError);
1303
1380
  const message = { role: "assistant", content: content || null };
1304
1381
  if (calls.size > 0) {
@@ -1460,6 +1537,18 @@ function saveConfig(config) {
1460
1537
  } catch {
1461
1538
  }
1462
1539
  }
1540
+ function saveEffort(config, model, level) {
1541
+ const current = readStored();
1542
+ const onDisk = current.problem ? void 0 : current.stored.effort;
1543
+ const next = {};
1544
+ for (const [id, value] of Object.entries(onDisk && typeof onDisk === "object" ? onDisk : {})) {
1545
+ if (typeof value === "string") next[id] = value;
1546
+ }
1547
+ if (level === void 0) delete next[model];
1548
+ else next[model] = level;
1549
+ config.effort = Object.keys(next).length ? next : void 0;
1550
+ saveConfig(config);
1551
+ }
1463
1552
  function configPath() {
1464
1553
  return CONFIG_PATH;
1465
1554
  }
@@ -4173,6 +4262,9 @@ async function runAgent(client, task, opts) {
4173
4262
  onProgress?.(`${description}: starting`);
4174
4263
  const sub = await runAgent(client, prompt, {
4175
4264
  model: opts.model,
4265
+ // The setting somebody chose for this model, the same as the parent's
4266
+ // calls: a sub-agent's credits land on the same turn.
4267
+ effort: opts.effort,
4176
4268
  root: opts.root,
4177
4269
  // A sub-agent that was told it can search the web, on a gateway that
4178
4270
  // cannot, spends its whole step budget discovering that.
@@ -4226,6 +4318,7 @@ ${NO_SEARCH_INSTRUCTION}`,
4226
4318
  let turn;
4227
4319
  try {
4228
4320
  turn = await client.chatStream(messages, opts.model, {
4321
+ effort: opts.effort,
4229
4322
  tools: opts.tools ? opts.tools() : TOOL_SCHEMA,
4230
4323
  signal: opts.signal,
4231
4324
  onDelta: (text) => emit({ type: "delta", text })
@@ -5097,6 +5190,50 @@ var init_version = __esm({
5097
5190
  }
5098
5191
  });
5099
5192
 
5193
+ // src/effort.ts
5194
+ function isLevel(value) {
5195
+ return typeof value === "string" && EFFORT_LEVELS.includes(value);
5196
+ }
5197
+ function effortsOf(info) {
5198
+ return (info?.efforts ?? []).filter(isLevel);
5199
+ }
5200
+ function parseEffortArg(arg) {
5201
+ const v = arg.trim().toLowerCase();
5202
+ if (v === "auto" || v === "default") return "auto";
5203
+ if (v === "none") return "off";
5204
+ return isLevel(v) ? v : void 0;
5205
+ }
5206
+ function resolveEffort(info, requested) {
5207
+ if (!isLevel(requested)) return void 0;
5208
+ const efforts = effortsOf(info);
5209
+ if (efforts.length === 0) return void 0;
5210
+ if (!isLevel(info?.default_effort)) return requested === "off" && efforts.includes("off") ? "off" : void 0;
5211
+ if (efforts.includes(requested)) return requested;
5212
+ const below = efforts.filter((e) => rank(e) <= rank(requested));
5213
+ return below.length ? below[below.length - 1] : efforts[0];
5214
+ }
5215
+ function savedEffort(config, model) {
5216
+ const value = config.effort?.[model];
5217
+ return typeof value === "string" ? value : void 0;
5218
+ }
5219
+ function effortRaised(info, level) {
5220
+ if (!level || !isLevel(info?.default_effort)) return false;
5221
+ return rank(level) > rank(info.default_effort);
5222
+ }
5223
+ function effortLabel(level) {
5224
+ if (level === "off") return "thinking off";
5225
+ if (level === "on") return "thinking on";
5226
+ return `${level} effort`;
5227
+ }
5228
+ var EFFORT_LEVELS, rank;
5229
+ var init_effort = __esm({
5230
+ "src/effort.ts"() {
5231
+ "use strict";
5232
+ EFFORT_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
5233
+ rank = (level) => EFFORT_LEVELS.indexOf(level);
5234
+ }
5235
+ });
5236
+
5100
5237
  // src/mentions.ts
5101
5238
  function findMentions(text) {
5102
5239
  const out = [];
@@ -5187,7 +5324,9 @@ async function compact(client, model, messages, opts = {}) {
5187
5324
  const turn = await client.chatStream(
5188
5325
  [...messages.slice(0, cut), { role: "user", content: SUMMARY_PROMPT }],
5189
5326
  model,
5190
- { signal: opts.signal }
5327
+ // The `/effort` setting applies to every call on the model, a summary
5328
+ // included: somebody who turned it down to save credits saves them here too.
5329
+ { signal: opts.signal, effort: opts.effort }
5191
5330
  );
5192
5331
  const summary = messageText(turn.message).trim() || "(no summary returned)";
5193
5332
  const compacted = [
@@ -5460,6 +5599,10 @@ var init_commands = __esm({
5460
5599
  { name: "help", desc: "list the commands" },
5461
5600
  { name: "model", desc: "switch model" },
5462
5601
  { name: "models", desc: "list models + prices" },
5602
+ // Deliberately *no* `args`, for `/model`'s reason: an arg hint makes enter
5603
+ // complete the command instead of running it, and the picker is the point.
5604
+ // `/effort low` still works when typed in full.
5605
+ { name: "effort", desc: "how long the model thinks (more thinking, more credits)" },
5463
5606
  { name: "mode", args: "[name]", desc: "permission mode (or press shift+tab)" },
5464
5607
  { name: "wallet", desc: "balance & ads today" },
5465
5608
  // Not "watch an ad": nothing on the wall is a video. CPX serves sign-up
@@ -6024,6 +6167,7 @@ function App({
6024
6167
  const [spent, setSpent] = useState(0);
6025
6168
  const [freeTier, setFreeTier] = useState(wallet?.free_tier);
6026
6169
  const [model, setModel2] = useState(config.model);
6170
+ const [effortBy, setEffortBy] = useState(() => config.effort ?? {});
6027
6171
  const modelRef = useRef(config.model);
6028
6172
  const [mode, setMode] = useState("normal");
6029
6173
  const switchModel = useCallback((id) => {
@@ -6077,6 +6221,16 @@ function App({
6077
6221
  const pendingFreeRef = useRef([]);
6078
6222
  const pendingGrantRef = useRef(0);
6079
6223
  const pendingBalanceRef = useRef(void 0);
6224
+ const pendingPremiumRef = useRef(null);
6225
+ const [premium, setPremiumState] = useState(wallet?.premium);
6226
+ const premiumRef = useRef(wallet?.premium);
6227
+ const setPremium = useCallback((next) => {
6228
+ premiumRef.current = next;
6229
+ setPremiumState(next);
6230
+ }, []);
6231
+ const refreshPremium = useCallback(() => {
6232
+ void client.wallet(void 0, { premium: true }).then((w) => setPremium(w.premium)).catch(() => void 0);
6233
+ }, [client, setPremium]);
6080
6234
  const mcpRef = useRef(McpHub.empty());
6081
6235
  const todosRef = useRef(new TodoList());
6082
6236
  const lastTurnRef = useRef(null);
@@ -6186,9 +6340,10 @@ function App({
6186
6340
  useEffect(() => {
6187
6341
  if (wallet) return;
6188
6342
  const ctl = new AbortController();
6189
- void client.wallet(ctl.signal).then((w) => {
6343
+ void client.wallet(ctl.signal, { premium: true }).then((w) => {
6190
6344
  setBalance((current) => current ?? w.balance);
6191
6345
  setFreeTier((current) => current ?? w.free_tier);
6346
+ if (premiumRef.current === void 0) setPremium(w.premium);
6192
6347
  }).catch(() => void 0);
6193
6348
  return () => ctl.abort();
6194
6349
  }, []);
@@ -6483,6 +6638,13 @@ function App({
6483
6638
  return;
6484
6639
  case "usage":
6485
6640
  setBalance(event.balance);
6641
+ if (event.creditsCharged > 0 && premiumRef.current) {
6642
+ const p = premiumRef.current;
6643
+ setPremium({
6644
+ unlocked: p.unlocked,
6645
+ credits: Math.max(0, p.unlocked ? event.balance : Math.min(event.balance, p.credits - event.creditsCharged))
6646
+ });
6647
+ }
6486
6648
  setSpent((s) => s + event.creditsCharged);
6487
6649
  if (event.free) {
6488
6650
  setFreeTier(
@@ -6494,11 +6656,12 @@ function App({
6494
6656
  return;
6495
6657
  }
6496
6658
  },
6497
- [cols, flushDelta, push]
6659
+ [cols, flushDelta, push, setPremium]
6498
6660
  );
6499
6661
  const runTurn2 = useCallback(
6500
6662
  async (task, images = []) => {
6501
6663
  pendingFreeRef.current = [];
6664
+ pendingPremiumRef.current = null;
6502
6665
  const model2 = modelRef.current;
6503
6666
  const ac = new AbortController();
6504
6667
  abortRef.current = ac;
@@ -6513,7 +6676,7 @@ function App({
6513
6676
  const res = await compact(client, model2, [
6514
6677
  { role: "system", content: contextRef.current.systemPrompt },
6515
6678
  ...history
6516
- ], { signal: ac.signal });
6679
+ ], { signal: ac.signal, effort: savedEffort(config, model2) });
6517
6680
  if (res) {
6518
6681
  history = res.messages.slice(1);
6519
6682
  setMessages(history);
@@ -6527,6 +6690,10 @@ function App({
6527
6690
  }
6528
6691
  const result = await runAgent(client, task, {
6529
6692
  model: model2,
6693
+ // Read now rather than from the render, for `modelRef`'s reason: a
6694
+ // setting chosen in the same tick as this turn starts is in `config`
6695
+ // already and not yet in any state.
6696
+ effort: savedEffort(config, model2),
6530
6697
  root,
6531
6698
  history,
6532
6699
  attachments: images,
@@ -6626,7 +6793,20 @@ ${NO_SEARCH_INSTRUCTION}`),
6626
6793
  }
6627
6794
  }
6628
6795
  } catch (err) {
6629
- if (err instanceof PaywallError) {
6796
+ if (err instanceof PremiumLockedError) {
6797
+ pendingTaskRef.current = { task, images };
6798
+ pendingPremiumRef.current = err;
6799
+ setPremium({ unlocked: false, credits: err.premiumCredits });
6800
+ const short2 = err.premiumCredits > 0 ? ` This turn needs about ${err.neededCredits.toLocaleString("en-US")} of those credits, and you have ${err.premiumCredits.toLocaleString("en-US")}.
6801
+ ` : "";
6802
+ push({
6803
+ kind: "notice",
6804
+ tone: "warn",
6805
+ text: ` ${premiumHeadline(err.unlockUsd)}
6806
+ ${err.model} is a premium model. It runs on credits you earned on the offer walls or bought.
6807
+ ` + short2 + " Your bonus credits (the signup bonus, our own surveys) still work on budget models.\n Press enter to unlock it or to switch to a budget model. Your request is kept."
6808
+ });
6809
+ } else if (err instanceof PaywallError) {
6630
6810
  pendingTaskRef.current = { task, images };
6631
6811
  pendingFreeRef.current = err.freeModels;
6632
6812
  pendingGrantRef.current = err.grantPerAd;
@@ -6821,8 +7001,9 @@ ${NO_SEARCH_INSTRUCTION}`),
6821
7001
  text: " no credit yet \u2014 offers take a while to confirm, and a screenout pays nothing.\n That is normal: start another with /earn, or check /wallet later."
6822
7002
  });
6823
7003
  push({ kind: "notice", tone: "good", text: " credits added \u2014 continuing" });
7004
+ refreshPremium();
6824
7005
  if (pending) await runTurn2(pending.task, pending.images);
6825
- }, [balance, client, config.dashboardUrl, push, runBusy, runTurn2]);
7006
+ }, [balance, client, config.dashboardUrl, push, refreshPremium, runBusy, runTurn2]);
6826
7007
  const runPaywallChoice = useCallback(() => {
6827
7008
  const free = pendingFreeRef.current.filter((m) => m.id !== EARN_ROW);
6828
7009
  if (free.length === 0) return void runAdWall();
@@ -6892,6 +7073,47 @@ ${NO_SEARCH_INSTRUCTION}`),
6892
7073
  });
6893
7074
  });
6894
7075
  }, [client, push, runBusy]);
7076
+ const runPremiumChoice = useCallback(() => {
7077
+ const refusal = pendingPremiumRef.current;
7078
+ if (!refusal) return void runPaywallChoice();
7079
+ const items = [
7080
+ {
7081
+ value: EARN_ROW,
7082
+ label: "complete a survey",
7083
+ hint: `opens the offer walls, offers pay ${grantLabel(refusal.grantPerAd)}`
7084
+ },
7085
+ ...refusal.unlockUsd ? [
7086
+ {
7087
+ value: BUY_ROW,
7088
+ label: `unlock from ${usd(refusal.unlockUsd)}`,
7089
+ hint: "any pack, and premium then takes all your credits"
7090
+ }
7091
+ ] : [],
7092
+ ...refusal.freeModels.filter((m) => m.id !== EARN_ROW && m.id !== BUY_ROW).map((m) => ({
7093
+ value: m.id,
7094
+ label: m.id,
7095
+ hint: `budget, free: ~${m.turnsLeft} turn${m.turnsLeft === 1 ? "" : "s"} left today`
7096
+ }))
7097
+ ];
7098
+ setPickerSel(0);
7099
+ setPicker({
7100
+ title: premiumHeadline(refusal.unlockUsd),
7101
+ subtitle: "Unlock premium, or carry on with a budget model.",
7102
+ items,
7103
+ onPick: (choice) => {
7104
+ if (choice.value === EARN_ROW) return void runAdWall();
7105
+ if (choice.value === BUY_ROW) return void runBuy();
7106
+ const pending = pendingTaskRef.current;
7107
+ pendingTaskRef.current = null;
7108
+ pendingPremiumRef.current = null;
7109
+ config.model = choice.value;
7110
+ saveConfig(config);
7111
+ switchModel(choice.value);
7112
+ push({ kind: "notice", tone: "good", text: ` model \u2192 ${choice.value} (budget) \xB7 continuing where you left off` });
7113
+ if (pending) void runTurn2(pending.task, pending.images);
7114
+ }
7115
+ });
7116
+ }, [config, push, runAdWall, runBuy, runPaywallChoice, runTurn2, switchModel]);
6895
7117
  const runCheckout = useCallback(
6896
7118
  async (pack) => {
6897
7119
  let before = balance ?? (await client.wallet().catch(() => void 0))?.balance;
@@ -6929,11 +7151,20 @@ ${NO_SEARCH_INSTRUCTION}`),
6929
7151
  stopped = signal.aborted;
6930
7152
  });
6931
7153
  if (credited) {
6932
- return push({
7154
+ push({
6933
7155
  kind: "notice",
6934
7156
  tone: "good",
6935
7157
  text: " credits added \u2014 an invoice is on its way to the address on the payment."
6936
7158
  });
7159
+ refreshPremium();
7160
+ const pending = pendingPremiumRef.current ? pendingTaskRef.current : null;
7161
+ if (pending) {
7162
+ pendingTaskRef.current = null;
7163
+ pendingPremiumRef.current = null;
7164
+ push({ kind: "notice", tone: "good", text: " premium is unlocked \xB7 continuing where you left off" });
7165
+ await runTurn2(pending.task, pending.images);
7166
+ }
7167
+ return;
6937
7168
  }
6938
7169
  push({
6939
7170
  kind: "notice",
@@ -6941,7 +7172,7 @@ ${NO_SEARCH_INSTRUCTION}`),
6941
7172
  text: stopped ? " stopped waiting. Credits still land when the payment clears \u2014 /wallet checks the balance." : " no credits yet \u2014 the payment may still be clearing. /wallet checks the balance."
6942
7173
  });
6943
7174
  },
6944
- [balance, client, push, runBusy]
7175
+ [balance, client, push, refreshPremium, runBusy, runTurn2]
6945
7176
  );
6946
7177
  const runCommand = useCallback(
6947
7178
  async (line2) => {
@@ -6996,7 +7227,7 @@ ${NO_SEARCH_INSTRUCTION}`),
6996
7227
  client,
6997
7228
  model,
6998
7229
  [{ role: "system", content: contextRef.current.systemPrompt }, ...messagesRef.current],
6999
- { signal }
7230
+ { signal, effort: savedEffort(config, model) }
7000
7231
  );
7001
7232
  if (!res) return void push({ kind: "notice", text: " nothing to compact yet" });
7002
7233
  setMessages(res.messages.slice(1));
@@ -7022,15 +7253,21 @@ ${NO_SEARCH_INSTRUCTION}`),
7022
7253
  if (arg && !models.some((m) => m.id === arg)) {
7023
7254
  push({ kind: "notice", tone: "warn", text: ` unknown model: ${arg}` });
7024
7255
  }
7025
- const free = await client.wallet(signal).then((w) => {
7256
+ const free = await client.wallet(signal, { premium: true }).then((w) => {
7026
7257
  setBalance(w.balance);
7027
7258
  setFreeTier(w.free_tier);
7259
+ setPremium(w.premium);
7028
7260
  return w.free_tier;
7029
- }).catch(() => freeTier);
7261
+ }).catch(() => {
7262
+ setPremium(void 0);
7263
+ return freeTier;
7264
+ });
7265
+ const premium2 = premiumRef.current;
7266
+ const unlockUsd = client.premiumUnlockUsd;
7030
7267
  const items = models.map((m) => ({
7031
7268
  value: m.id,
7032
7269
  label: m.id,
7033
- hint: `${m.tier.padEnd(8)}${modelHint(m, free)}`,
7270
+ hint: accessColumn(m, premium2) + (m.access === "premium" && premiumLocked(premium2) ? premiumUnlockHint(unlockUsd) : modelHint(m, free)),
7034
7271
  current: m.id === model
7035
7272
  }));
7036
7273
  const preselect = items.findIndex((i) => i.value === arg);
@@ -7044,17 +7281,85 @@ ${NO_SEARCH_INSTRUCTION}`),
7044
7281
  saveConfig(config);
7045
7282
  switchModel(choice.value);
7046
7283
  push({ kind: "notice", text: ` model \u2192 ${choice.value}` });
7284
+ const picked = models.find((m) => m.id === choice.value);
7285
+ if (picked?.access === "premium" && premiumLocked(premium2)) {
7286
+ push({
7287
+ kind: "notice",
7288
+ tone: "warn",
7289
+ text: ` ${premiumHeadline(unlockUsd)}
7290
+ ${choice.value} runs on credits you earned on the offer walls or bought.
7291
+ /earn opens the offer walls` + (unlockUsd ? `, /buy unlocks premium from ${usd(unlockUsd)}.` : ".") + " Your bonus credits work on budget models."
7292
+ });
7293
+ }
7047
7294
  }
7048
7295
  });
7049
7296
  });
7050
7297
  return;
7298
+ // How long the model thinks, saved per model. The picker offers only
7299
+ // what the current model takes, so a stored setting is always one it
7300
+ // has; a typed level it does not have moves to the nearest one below,
7301
+ // the gateway's rule, and says so. Every figure about cost stays in
7302
+ // words: what a setting costs depends on the task, and the meta line
7303
+ // under each turn already shows what it did cost.
7304
+ case "effort":
7305
+ await runBusy(LOADING, async (signal) => {
7306
+ const models = catalogRef.current.length ? catalogRef.current : await client.models(signal);
7307
+ catalogRef.current = models;
7308
+ const info2 = models.find((m) => m.id === model);
7309
+ const name = info2?.label ?? model;
7310
+ const levels = effortsOf(info2);
7311
+ const fallback = info2?.default_effort;
7312
+ if (!info2 || levels.length === 0) {
7313
+ const why = info2?.efforts === void 0 ? "this gateway does not offer effort settings" : fallback === "on" ? `${name} always thinks before it answers, and has no setting for how long` : `${name} has no effort setting`;
7314
+ return void push({ kind: "notice", text: ` ${why}` });
7315
+ }
7316
+ const defaultWords = fallback ? effortLabel(fallback) : "its default";
7317
+ const apply2 = (choice, note = "") => {
7318
+ saveEffort(config, model, choice);
7319
+ setEffortBy({ ...config.effort ?? {} });
7320
+ const now = choice ? effortLabel(choice) : `auto (${defaultWords})`;
7321
+ push({ kind: "notice", text: ` ${name}: ${now}${note}` });
7322
+ };
7323
+ if (arg) {
7324
+ const wanted = parseEffortArg(arg);
7325
+ if (!wanted) {
7326
+ return void push({
7327
+ kind: "notice",
7328
+ tone: "warn",
7329
+ text: ` unknown effort: ${arg}. ${name} takes ${["auto", ...levels].join(", ")}`
7330
+ });
7331
+ }
7332
+ if (wanted === "auto") return apply2(void 0);
7333
+ const resolved = resolveEffort(info2, wanted);
7334
+ if (!resolved) return apply2(void 0, `. ${name} can only switch its thinking off`);
7335
+ return apply2(resolved, resolved === wanted ? "" : `. It has no ${wanted}, so this is the nearest below`);
7336
+ }
7337
+ const current = resolveEffort(info2, savedEffort(config, model));
7338
+ const items = [
7339
+ { value: "auto", label: "auto", hint: `the model's default (${defaultWords})`, current: !current },
7340
+ ...levels.map((level) => ({
7341
+ value: level,
7342
+ label: level,
7343
+ hint: level === fallback ? "same as auto" : effortRaised(info2, level) ? "more thinking, more credits" : "less thinking, fewer credits",
7344
+ current: current === level
7345
+ }))
7346
+ ];
7347
+ setPickerSel(Math.max(0, items.findIndex((i) => i.current)));
7348
+ setPicker({
7349
+ title: `Effort for ${name}`,
7350
+ subtitle: "Saved for this model. Thinking is billed as output, like the answer.",
7351
+ items,
7352
+ onPick: (choice) => apply2(choice.value === "auto" ? void 0 : choice.value)
7353
+ });
7354
+ });
7355
+ return;
7051
7356
  case "models":
7052
7357
  await runBusy(LOADING, async (signal) => {
7053
7358
  const ms = await client.models(signal);
7054
7359
  catalogRef.current = ms;
7055
7360
  push({
7056
7361
  kind: "notice",
7057
- text: ms.map((m) => ` ${m.id.padEnd(24)} ${modelHint(m, freeTier)}`).join("\n")
7362
+ text: ms.map((m) => ` ${m.id.padEnd(24)} ${accessColumn(m, premium)}${modelHint(m, freeTier)}`).join("\n")
7058
7363
  });
7059
7364
  });
7060
7365
  return;
@@ -7216,7 +7521,7 @@ ${NO_SEARCH_INSTRUCTION}`),
7216
7521
  push({ kind: "notice", tone: "warn", text: ` unknown command: /${cmd} \u2014 try /help` });
7217
7522
  }
7218
7523
  },
7219
- [client, cols, config, cycleMode, exit, model, push, root, runAdWall, runBusy, runBuy, runLogin, runTurn2, writeToStdout]
7524
+ [client, cols, config, cycleMode, exit, model, premium, push, root, runAdWall, runBusy, runBuy, runLogin, runTurn2, setPremium, writeToStdout]
7220
7525
  );
7221
7526
  const runShell = useCallback(
7222
7527
  async (command) => {
@@ -7546,7 +7851,9 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
7546
7851
  if (key.return) {
7547
7852
  if (key.meta || key.shift) return setEditor(newline(editor));
7548
7853
  if (endsWithContinuation(editor)) return setEditor(continueLine(editor));
7549
- if (!busy && pendingTaskRef.current && isEmpty(editor)) return void runPaywallChoice();
7854
+ if (!busy && pendingTaskRef.current && isEmpty(editor)) {
7855
+ return void (pendingPremiumRef.current ? runPremiumChoice() : runPaywallChoice());
7856
+ }
7550
7857
  return void submit(toText(editor));
7551
7858
  }
7552
7859
  if (key.ctrl && ch === "j") return setEditor(newline(editor));
@@ -7722,11 +8029,16 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
7722
8029
  const typed = toText(editor);
7723
8030
  let estimate;
7724
8031
  const info = catalogRef.current.find((m) => m.id === model);
8032
+ const effortNow = info ? resolveEffort(info, effortBy[model]) : effortBy[model];
7725
8033
  const shape = client.taskShape;
7726
- if (!busy && info && shape && balance !== void 0 && typed.trim() !== "" && !/^[/!]/.test(typed.trimStart())) {
8034
+ if (!busy && info && shape && balance !== void 0 && !effortRaised(info, resolveEffort(info, effortBy[model])) && typed.trim() !== "" && !/^[/!]/.test(typed.trimStart())) {
7727
8035
  const range = estimateTask(info, conversationUsed + schemaTokens + estimateTokens(typed), shape);
7728
8036
  const onFree = Boolean(info.free && freeTier?.models.includes(model));
7729
- const verdict = taskVerdict(range, { balance, ...onFree ? { freeLeft: freeTier.credits_left } : {} });
8037
+ const spendable = info.access === "premium" && premium && !premium.unlocked ? Math.min(balance, premium.credits) : balance;
8038
+ const verdict = taskVerdict(range, {
8039
+ balance: spendable,
8040
+ ...onFree ? { freeLeft: freeTier.credits_left } : {}
8041
+ });
7730
8042
  estimate = estimateClause(range, verdict, !onFree && (freeTier?.credits_left ?? 0) > 0);
7731
8043
  }
7732
8044
  const modeText = ` ${MODE_STYLE[mode].glyph} ${MODE_LABEL[mode]}`;
@@ -7734,6 +8046,7 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
7734
8046
  ` ${statusLine(
7735
8047
  {
7736
8048
  model,
8049
+ ...effortNow ? { effort: effortNow } : {},
7737
8050
  balance,
7738
8051
  spent,
7739
8052
  contextPct,
@@ -7932,6 +8245,8 @@ function statusRow(o, estimate, cols) {
7932
8245
  const burn = o.spent > 0 && o.minutes >= 1 ? `${Math.round(o.spent / o.minutes).toLocaleString("en-US")} cr/min` : void 0;
7933
8246
  const clauses = [
7934
8247
  { text: o.model, rank: KEEP_ALWAYS },
8248
+ // Beside the model it qualifies, and given up with the free allowance.
8249
+ { text: o.effort === void 0 ? void 0 : effortLabel(o.effort), rank: 4 },
7935
8250
  {
7936
8251
  // "? cr" rather than "0 cr": unknown is not empty, and an empty-looking
7937
8252
  // wallet is the one misreading on this row that frightens somebody.
@@ -8008,7 +8323,7 @@ function tailLines(text, max) {
8008
8323
  const lines = text.split("\n");
8009
8324
  return lines.length <= max ? text : lines.slice(-max).join("\n");
8010
8325
  }
8011
- var WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, SIGNING_IN, RUNNING, SENDING_FEEDBACK, WAITING_FOR_PAYMENT, FEEDBACK_TURN_INTERVAL, NOT_SIGNED_IN, LIVE_OUTPUT_LINES, COMMITTED_OUTPUT_LINES, DELTA_FLUSH_MS, MAX_INPUT_ROWS, MAX_PICKER_ROWS, MAX_QUEUE_ROWS, QUEUE_MARKER, PICKER_CURRENT, MIN_PICKER_LABEL_COLS, PICKER_CHROME, PICKER_CHROME_COMPACT, ASK_CHROME, PLAN_CHROME, BUY_CHROME, BUY_CHROME_COMPACT, ASK_HINT, PLAN_HINT, BUY_HINT, BUY_CONFIRM_WORD, BUY_TYPED_MAX, BUY_TOO_SHORT, EXPAND_MAX_LINES, FALLBACK_ROWS, FALLBACK_COLS, INIT_HINT_AFTER_TURNS, EARN_ROW, CONTEXT_NOTICE_AT, KEEP_ALWAYS;
8326
+ var WORKING, WAITING_FOR_REWARD, LOADING, COMPACTING, SIGNING_IN, RUNNING, SENDING_FEEDBACK, WAITING_FOR_PAYMENT, FEEDBACK_TURN_INTERVAL, NOT_SIGNED_IN, LIVE_OUTPUT_LINES, COMMITTED_OUTPUT_LINES, DELTA_FLUSH_MS, MAX_INPUT_ROWS, MAX_PICKER_ROWS, MAX_QUEUE_ROWS, QUEUE_MARKER, PICKER_CURRENT, MIN_PICKER_LABEL_COLS, PICKER_CHROME, PICKER_CHROME_COMPACT, ASK_CHROME, PLAN_CHROME, BUY_CHROME, BUY_CHROME_COMPACT, ASK_HINT, PLAN_HINT, BUY_HINT, BUY_CONFIRM_WORD, BUY_TYPED_MAX, BUY_TOO_SHORT, EXPAND_MAX_LINES, FALLBACK_ROWS, FALLBACK_COLS, INIT_HINT_AFTER_TURNS, EARN_ROW, BUY_ROW, CONTEXT_NOTICE_AT, KEEP_ALWAYS;
8012
8327
  var init_app = __esm({
8013
8328
  "src/tui/app.tsx"() {
8014
8329
  "use strict";
@@ -8017,6 +8332,7 @@ var init_app = __esm({
8017
8332
  init_todos();
8018
8333
  init_shells();
8019
8334
  init_config();
8335
+ init_effort();
8020
8336
  init_login();
8021
8337
  init_banner();
8022
8338
  init_agent();
@@ -8091,6 +8407,7 @@ var init_app = __esm({
8091
8407
  FALLBACK_COLS = 80;
8092
8408
  INIT_HINT_AFTER_TURNS = 3;
8093
8409
  EARN_ROW = "\0earn";
8410
+ BUY_ROW = "\0buy";
8094
8411
  CONTEXT_NOTICE_AT = 0.6;
8095
8412
  KEEP_ALWAYS = Number.POSITIVE_INFINITY;
8096
8413
  }
@@ -8165,6 +8482,7 @@ function clearTerminalTitle(out = titleStream()) {
8165
8482
 
8166
8483
  // src/main.ts
8167
8484
  init_version();
8485
+ init_effort();
8168
8486
  var c = palette({ isTTY: process.stdout.isTTY, env: process.env });
8169
8487
  async function main() {
8170
8488
  setTerminalTitle();
@@ -8328,17 +8646,40 @@ function earnedToday(w) {
8328
8646
  }
8329
8647
  async function listModels(client) {
8330
8648
  const models = await client.models();
8331
- const free = await client.wallet().then((w) => w.free_tier).catch(() => void 0);
8649
+ const wallet = await client.wallet(void 0, { premium: true }).catch(() => void 0);
8650
+ const free = wallet?.free_tier;
8651
+ const premium = wallet?.premium;
8332
8652
  console.log(c.bold("Models (credit prices per 1M tokens)"));
8333
8653
  for (const m of models) {
8334
8654
  const tag = m.ad_fundable ? "" : c.dim(" [paid/BYOK]");
8335
8655
  const turns2 = free?.models.includes(m.id) ? free.turns_left[m.id] ?? 0 : void 0;
8336
8656
  const freeTag = !m.free ? "" : turns2 === void 0 ? c.green(" [free]") : turns2 > 0 ? c.green(` [free: ~${turns2} turn${turns2 === 1 ? "" : "s"} left today]`) : c.dim(" [free: none left today]");
8657
+ const lockTag = m.access === "premium" && premiumLocked(premium) ? c.yellow(` [${premiumHeadline(client.premiumUnlockUsd).replace(/\.$/, "")}]`) : "";
8337
8658
  console.log(
8338
- ` ${c.cyan(m.id.padEnd(22))} ${m.tier.padEnd(8)} in ${String(m.credits_per_mtoken_input).padStart(9)} out ${String(m.credits_per_mtoken_output).padStart(9)} ${adsPerTaskLabel(m.est_ads_per_task)}${tag}${freeTag}`
8659
+ ` ${c.cyan(m.id.padEnd(22))} ${accessColumn(m, premium)} in ${String(m.credits_per_mtoken_input).padStart(9)} out ${String(m.credits_per_mtoken_output).padStart(9)} ${adsPerTaskLabel(m.est_ads_per_task)}${tag}${freeTag}${lockTag}`
8339
8660
  );
8340
8661
  }
8341
8662
  }
8663
+ function printPremiumRefusal(err) {
8664
+ console.log(c.yellow(premiumHeadline(err.unlockUsd)));
8665
+ console.log(
8666
+ c.yellow(`${err.model} is a premium model. It runs on credits you earned on the offer walls or bought.`)
8667
+ );
8668
+ if (err.premiumCredits > 0) {
8669
+ console.log(
8670
+ c.yellow(
8671
+ `This turn needs about ${err.neededCredits.toLocaleString("en-US")} of those credits, and you have ${err.premiumCredits.toLocaleString("en-US")}.`
8672
+ )
8673
+ );
8674
+ }
8675
+ console.log(c.yellow(`Run \`clixad earn\` for a survey on the offer walls. Offers pay ${grantLabel(err.grantPerAd)}.`));
8676
+ if (err.unlockUsd) {
8677
+ console.log(c.yellow(`Or unlock premium from ${usd(err.unlockUsd)}: start \`clixad\` and type /buy.`));
8678
+ }
8679
+ const freeLine = freeAlternativeLine(err.freeModels);
8680
+ console.log(c.green(freeLine ?? "Your bonus credits still work on budget models: `clixad models` lists them."));
8681
+ console.log(c.dim(" Nothing was charged for this request."));
8682
+ }
8342
8683
  async function setModel(client, config, model) {
8343
8684
  if (!model) return console.log(`current model: ${c.cyan(config.model)}`);
8344
8685
  const known = await client.models().catch(() => void 0);
@@ -8352,6 +8693,18 @@ async function setModel(client, config, model) {
8352
8693
  config.model = model;
8353
8694
  saveConfig(config);
8354
8695
  console.log(c.green(`\u2713 model set to ${model}`));
8696
+ const picked = known?.find((m) => m.id === model);
8697
+ if (picked?.access === "premium") {
8698
+ const w = await client.wallet(void 0, { premium: true }).catch(() => void 0);
8699
+ if (premiumLocked(w?.premium)) {
8700
+ console.log(c.yellow(premiumHeadline(client.premiumUnlockUsd)));
8701
+ console.log(
8702
+ c.yellow(
8703
+ `${model} runs on credits you earned on the offer walls or bought. Run \`clixad earn\` for a survey` + (client.premiumUnlockUsd ? `, or start \`clixad\` and type /buy.` : ".")
8704
+ )
8705
+ );
8706
+ }
8707
+ }
8355
8708
  }
8356
8709
  async function showWallet(client) {
8357
8710
  const streak = await client.claimStreak();
@@ -8461,6 +8814,7 @@ async function printCmd(client, config, task) {
8461
8814
  try {
8462
8815
  const res = await runAgent(client, task, {
8463
8816
  model: config.model,
8817
+ effort: savedEffort(config, config.model),
8464
8818
  root: process.cwd(),
8465
8819
  systemPrompt: collectContext(process.cwd()).systemPrompt + (capabilities?.web_search ? "" : `
8466
8820
 
@@ -8484,7 +8838,10 @@ ${NO_SEARCH_INSTRUCTION}`),
8484
8838
  [${cost} \xB7 balance ${res.balance} \xB7 ${res.steps} step(s)]`)
8485
8839
  );
8486
8840
  } catch (err) {
8487
- if (err instanceof PaywallError) {
8841
+ if (err instanceof PremiumLockedError) {
8842
+ console.log("");
8843
+ printPremiumRefusal(err);
8844
+ } else if (err instanceof PaywallError) {
8488
8845
  console.log(c.yellow(`
8489
8846
  ${shortfallHeadline(err, config.model)}`));
8490
8847
  const freeLine = freeAlternativeLine(err.freeModels);
@@ -8606,6 +8963,7 @@ async function runTurn(client, config, messages) {
8606
8963
  let started = false;
8607
8964
  try {
8608
8965
  const result = await client.chatStream(messages, config.model, {
8966
+ effort: savedEffort(config, config.model),
8609
8967
  onDelta: (chunk) => {
8610
8968
  if (!started) process.stdout.write(c.green("assistant "));
8611
8969
  started = true;
@@ -8618,7 +8976,9 @@ async function runTurn(client, config, messages) {
8618
8976
  return result;
8619
8977
  } catch (err) {
8620
8978
  if (started) process.stdout.write("\n");
8621
- if (err instanceof PaywallError) {
8979
+ if (err instanceof PremiumLockedError) {
8980
+ printPremiumRefusal(err);
8981
+ } else if (err instanceof PaywallError) {
8622
8982
  console.log(c.yellow(shortfallHeadline(err, config.model)));
8623
8983
  const freeLine = freeAlternativeLine(err.freeModels);
8624
8984
  if (freeLine) console.log(c.green(freeLine));
@@ -8642,9 +9002,9 @@ async function showStreak(client) {
8642
9002
  console.log(streak.claimed ? c.green(` ${line2}`) : c.dim(` ${line2}`));
8643
9003
  return streak;
8644
9004
  }
8645
- async function safeWallet(client) {
9005
+ async function safeWallet(client, opts = {}) {
8646
9006
  try {
8647
- return await client.wallet();
9007
+ return await client.wallet(void 0, opts);
8648
9008
  } catch {
8649
9009
  return void 0;
8650
9010
  }
@@ -8670,7 +9030,7 @@ async function startupReads(client, gatewayUrl) {
8670
9030
  )
8671
9031
  ];
8672
9032
  try {
8673
- const [streak, wallet] = await Promise.all([client.claimStreak(), safeWallet(client)]);
9033
+ const [streak, wallet] = await Promise.all([client.claimStreak(), safeWallet(client, { premium: true })]);
8674
9034
  const balance = streak?.claimed ? streak.balance : wallet?.balance;
8675
9035
  return {
8676
9036
  streak,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clixad",
3
- "version": "0.0.1-beta.25",
3
+ "version": "0.0.1-beta.27",
4
4
  "description": "Free AI coding agent in your terminal, funded by rewarded ads.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",