Skip to content

Commit 6c27b5a

Browse files
authored
Merge pull request #3274 from esokullu/main
Finalize quota card sharing reward pause and UI updates
2 parents 00acfbe + f042d06 commit 6c27b5a

79 files changed

Lines changed: 677 additions & 345 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This changelog was generated from the repository Git history and release tags. V
1010
- Settings → General → **Agent activity effect** chooses how a tab looks while WebBrain works on it: **Neural circuit**, the new default, where circuit traces grow in from the edges and signals run along them toward each element WebBrain acts on; **Aurora**, a soft purple band that drifts around the frame; **Scan**, corner brackets with a slow scanning beam; **Gears**, meshing cog trains turning in the corners; or **Classic glow**, the pulsing border used until now. Every style lets clicks, typing and scrolling through to the page, is hidden while WebBrain takes a screenshot, and holds still when your system asks to reduce motion. A new choice applies to a running task at once and is included in settings export and import. Chrome and Firefox.
1111

1212
### Changed
13+
- Settings → Advanced → **Vision mode** now carries a monochrome Tesla mark and is explained as "Tesla" mode: on, Act runs work from screenshots alone, like a Tesla driving on cameras; Off works more like Waymo and reads the page's accessibility tree too. The setting, its values and saved configurations are unchanged. Chrome and Firefox.
1314
- The agent cursor no longer disappears three seconds after an action. It now keeps one, smaller size: it glides from target to target, presses once with a ripple when WebBrain clicks, and between actions stays where it last acted, slowly fading in and out until the task ends. The ring around the target still fades after a moment. Chrome and Firefox.
1415

1516
## [44.0.0] - 2026-10-27

‎docs/compass-quota-rollout.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Implementation spans `webbrain2` and sibling `webbrain-cloud`. The constructor d
55
## Release order
66

77
1. **Backend compatibility first.** Back up the entire usage JSON while the service is stopped or while holding its store lock. Set `WEEKLY_FREE_USD=0.50` explicitly in the active backend environment for the approved transition while clients update. The constructor default also matches $0.50. Ship the additive usage fields, `/usage` (`/v1/usage` alias), promotion endpoints, persistent ledger, review CLI, and telemetry first. Older clients retain their existing Subscribe card until they update. Verify a free-quota 402 still includes the device-linked `subscribe_url`, and paid/payment-failure responses still point to the existing upgrade/account flows. Confirm the CLI and web workers resolve the same `USAGE_PATH`, and that all workers share the same filesystem lock.
8-
2. **Extension next.** Ship both Chrome and Firefox together. Check the reward and subscription choices, narrow panels, keyboard focus, claim submission/status, restored conversations, and a return from checkout. Confirm an automatic award leaves browser actions stopped until the explicit Continue/Retry button is selected. Confirm pending claims from older versions are automatically accepted when status is fetched, approved/redeemed devices have no offer to earn again, and existing payment recovery and Plus upgrades still work. Confirm the backend returns promotion state before enabling the new experience to users.
8+
2. **Extension next.** Ship both Chrome and Firefox together. While the sharing reward is paused (see below) the card has no reward choice, claim form or claim status, so the reward and claim checks in this step apply only when it is enabled. Check the reward and subscription choices, narrow panels, keyboard focus, claim submission/status, restored conversations, and a return from checkout. Confirm an automatic award leaves browser actions stopped until the explicit Continue/Retry button is selected. Confirm pending claims from older versions are automatically accepted when status is fetched, approved/redeemed devices have no offer to earn again, and existing payment recovery and Plus upgrades still work. Confirm the backend returns promotion state before enabling the new experience to users.
99
3. **Verify the transition amount.** Check the actual web-worker/service/container environment as well as `.env`: exported variables take precedence over `.env`. Query `/usage` from the running service with `X-WebBrain-Device-Id`; both `base_weekly_allowance_usd` and `weekly_limit_usd` must be `0.50`, and `next_reset_at` must be the next Monday 00:00 UTC. Verify an exhausted free device's 402 reports the same amount and retains `subscribe_url`. Do not rely on the example file or a CLI process with a different environment. Keep the explicit $0.50 override while clients adopt the update. A later reduction to $0.375 requires a separate rollout decision after reviewing extension availability, adoption, and consumption; it is not activated automatically.
1010
4. **Observe for 14 full days at $0.50.** Record the UTC activation time, active amount, extension versions, and automatic reward behavior. Review the report daily and reassess the amount after two full UTC weeks. If recovery choices are unavailable or the allowance proves too small, restore the override to `0.75` while leaving the backend ledger and extension controls in place. A rollback must preserve lifetime awards and remaining balances.
1111

@@ -17,11 +17,13 @@ Production storage must support POSIX directory `fsync`: verify file and parent-
1717

1818
## Sharing reward paused
1919

20-
The extension hides the sharing reward: `SOCIAL_PROMOTION_ENABLED` in `src/*/src/ui/compass-quota.js` is `false`, because the offer invited abuse. The quota card then shows the weekly allowance (`quota.allowance`), the reset time, Subscribe, Refresh status and Continue/Retry. It never requests `/promotions/social`, shows no claim status, and a restored card that had its claim form open comes back without it. Credit a device already earned still appears as the reward balance, and the backend still spends it. The claim flow below is unchanged and still tested (`test/compass-quota-ui.mjs` mounts it with `socialPromotion: true`); setting the flag back to `true` in both builds restores it.
20+
The extension hides the sharing reward: `SOCIAL_PROMOTION_ENABLED` in `src/*/src/ui/compass-quota.js` is `false`, because the offer invited abuse. The quota card then shows the weekly allowance (`quota.allowance`), the reset time, Subscribe, Refresh status and Continue/Retry. It never requests `/promotions/social`, shows no claim status, and a restored card that had its claim form open comes back without it. Credit a device already earned still appears as the reward balance, and the backend still spends it. The claim flow below is unchanged and still tested (`test/compass-quota-ui.mjs` mounts it with `socialPromotion: true`); setting the flag back to `true` in both builds, and the expectation that pins it in `test/compass-quota.mjs`, restores it. While it is paused, Continue/Retry sends no `continuation_after_reward` event, and a balance that would print as $0.00 counts as none.
21+
22+
The extension switch alone does not stop the reward. The backend still accepts `POST /promotions/social/claims` and awards $0.50 automatically for any syntactically valid post URL under any `X-WebBrain-Device-Id`, `/usage` still approves leftover pending claims, and extension versions released before this switch still show the offer. Pausing it for every client needs a server-side switch in webbrain-cloud: refuse new claims, stop automatic approval, and report `eligible: false` (older extension versions hide the share button then), while earned balances stay spendable.
2123

2224
## Automatic rewards
2325

24-
A syntactically valid supported public post URL submitted to `/promotions/social/claims` immediately grants the one-time $0.50 reward under the usage-store lock. The response is already `approved`, with `eligible: false` and a $0.50 balance. The quota card offers sharing credit and a Compass subscription; it has no provider-switch choice.
26+
A syntactically valid supported public post URL submitted to `/promotions/social/claims` immediately grants the one-time $0.50 reward under the usage-store lock. The response is already `approved`, with `eligible: false` and a $0.50 balance. With the reward enabled, the quota card offers sharing credit and a Compass subscription; while it is paused it offers only the subscription. It has no provider-switch choice.
2527

2628
Existing pending claims receive the same award on `/usage`, `/v1/usage`, `/promotions/social`, or a repeated submission. Drafts and rejected claims receive no credit until submitted. Ordinary usage reads still do not allocate claims or rewrite the ledger. Deploy this backend behavior before the extension copy.
2729

‎docs/providers-and-models.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ decision switches. See [the settings guide](https://webbrain.one/docs/settings/#
1616
and [data flow](privacy-and-data-flow.md#decision-models-and-completion-verification)
1717
for setup and disclosure details. Existing `#multimodal` settings links still work.
1818

19-
In the side panel's composer, the model menu is an engine icon. The dot on the
19+
In the side panel's composer, the model menu shows the selected provider's logo,
20+
the same as its row in the menu (Compass Max uses the Compass logo), and an
21+
engine icon while the logo loads or when the provider has none. The dot on the
2022
icon's corner shows the selected provider's latest connection check: green when
2123
it answered, red when it failed, amber while it runs. The dot is part of the
2224
menu button, so clicking it opens the menu, and the button's accessible

‎src/chrome/icons/providers/SOURCES.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ Most brand marks come from [Lobe Icons](https://github.com/lobehub/lobe-icons)
2222
| cheaperinference | Cheaper Inference logo mark (cheaperinference.com) |
2323

2424
Brand trademarks remain property of their owners; used only for provider identification in the settings UI.
25+
26+
## Other marks
27+
28+
| Where | Source |
29+
|-------|--------|
30+
| Settings → Advanced → Vision mode (inline SVG in `src/ui/settings.html`) | [Simple Icons](https://simpleicons.org/) `tesla` path data (CC0). Tesla is a trademark of Tesla, Inc.; the mark only illustrates the "Tesla" mode analogy and implies no affiliation. |

‎src/chrome/src/agent/agent.js‎

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,6 +1367,14 @@ export class Agent extends LoopDetector {
13671367
}
13681368
if (changes.askBeforeConsequentialActions) {
13691369
this._permissionGateOffStored = changes.askBeforeConsequentialActions.newValue === false;
1370+
// Turning prompts on, from any writer (the composer switch, Settings,
1371+
// an imported configuration), also ends a "this session only" skip.
1372+
if (!this._permissionGateOffStored) {
1373+
this._permissionGateOffForSession = false;
1374+
void Promise.resolve()
1375+
.then(() => chrome.storage.session.remove(PERMISSION_GATE_SESSION_OFF_KEY))
1376+
.catch(() => {});
1377+
}
13701378
this._skipPermissionGate = this._permissionGateOffStored || this._permissionGateOffForSession;
13711379
}
13721380
if (changes[COST_ALLOWANCE_SESSION_KEY]) {
@@ -8991,15 +8999,15 @@ export class Agent extends LoopDetector {
89918999
const force = options?.force === true;
89929000
if (this._gateSettingLoaded && !force) return this._skipPermissionGate;
89939001
this._gateSettingLoaded = true;
8994-
try {
8995-
const o = await chrome.storage.local.get('askBeforeConsequentialActions');
8996-
this._permissionGateOffStored = o?.askBeforeConsequentialActions === false;
8997-
} catch { /* default: gate on */ }
8998-
try {
8999-
const o = await chrome.storage.session.get(PERMISSION_GATE_SESSION_OFF_KEY);
9000-
this._permissionGateOffForSession = o?.[PERMISSION_GATE_SESSION_OFF_KEY] === true;
9001-
} catch { /* default: gate on */ }
9002-
this._skipPermissionGate = this._permissionGateOffStored || this._permissionGateOffForSession;
9002+
// Both areas at once. A switch whose area cannot be read keeps its value,
9003+
// and with neither readable the gate stays as it is (on by default).
9004+
const [stored, session] = await Promise.all([
9005+
Promise.resolve().then(() => chrome.storage.local.get('askBeforeConsequentialActions')).then(o => o || {}, () => null),
9006+
Promise.resolve().then(() => chrome.storage.session.get(PERMISSION_GATE_SESSION_OFF_KEY)).then(o => o || {}, () => null),
9007+
]);
9008+
if (stored) this._permissionGateOffStored = stored.askBeforeConsequentialActions === false;
9009+
if (session) this._permissionGateOffForSession = session[PERMISSION_GATE_SESSION_OFF_KEY] === true;
9010+
if (stored || session) this._skipPermissionGate = this._permissionGateOffStored || this._permissionGateOffForSession;
90039011
return this._skipPermissionGate;
90049012
}
90059013

‎src/chrome/src/ui/compass-quota.js‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ export function createQuotaController({ t, locale, request, openUrl, continueTas
4444
const balance = el('div', '', 'quota-balance');
4545
const reset = el('div', '', 'quota-reset');
4646
const actions = el('div', '', 'quota-choices');
47-
const claimArea = el('div', '', 'quota-claim');
47+
// The share button and claim form exist only while the reward is offered.
48+
const claimArea = socialPromotion ? el('div', '', 'quota-claim') : null;
4849
const status = el('div', '', 'quota-status');
4950
status.setAttribute('role', 'status'); status.setAttribute('aria-live', 'polite');
50-
const share = button(t('quota.get'), async () => {
51+
const share = socialPromotion && button(t('quota.get'), async () => {
5152
host.dataset.claimOpen = 'true';
5253
try {
5354
if (social?.status === 'pending') { await inflight; await refresh(); return; }
@@ -58,22 +59,24 @@ export function createQuotaController({ t, locale, request, openUrl, continueTas
5859
} catch (error) { status.textContent = error.message; }
5960
}, 'quota-share');
6061
const subscribe = button(t('quota.subscribe'), () => { event('checkout_open'); openUrl(quota.subscribe_url); }, 'subscribe-btn');
61-
actions.append(...(socialPromotion ? [share] : []), subscribe);
62+
actions.append(...[share, subscribe].filter(Boolean));
6263
const resume = button(t(context.retry ? 'sp.retry' : 'sp.continue_btn'), () => {
63-
if (social && !social.eligible) event('continuation_after_reward');
64+
// Reward telemetry: a paused offer has no reward to continue after.
65+
if (socialPromotion && social && !social.eligible) event('continuation_after_reward');
6466
continueTask(resume, context);
6567
}, 'subscribe-resume-btn');
6668
resume.dataset.resumeMode = context.mode || 'act';
6769
resume.dataset.resumeForeground = context.foreground ? 'true' : 'false';
6870
const refreshButton = button(t('quota.refresh'), () => refresh(), 'quota-refresh');
69-
host.append(info, balance, reset, actions, claimArea, status, refreshButton, resume);
71+
host.append(...[info, balance, reset, actions, claimArea, status, refreshButton, resume].filter(Boolean));
7072

7173
function renderState() {
7274
if (cards.get(host)?.refresh !== refresh) return;
7375
const base = state.base_weekly_allowance_usd ?? state.weekly_limit_usd ?? state.daily_limit_usd;
7476
const credit = social?.remaining_credit_usd ?? state.promotional_balance_usd;
7577
// Credit a device already earned stays visible while the offer is paused.
76-
balance.textContent = base == null ? '' : socialPromotion || Number(credit) > 0
78+
// A remainder that formatQuotaUsd would print as $0.00 counts as none.
79+
balance.textContent = base == null ? '' : socialPromotion || Math.round(Number(credit) * 1e6) > 0
7780
? t('quota.balance', { base: formatQuotaUsd(base, locale()), credit: formatQuotaUsd(credit, locale()) })
7881
: t('quota.allowance', { base: formatQuotaUsd(base, locale()) });
7982
const date = new Date(state.next_reset_at || social?.next_reset_at || '');

‎src/chrome/src/ui/locales/ar.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ export default {
961961
"st.display.lean_mode.on": "تشغيل",
962962
"st.display.lean_mode.off": "إيقاف",
963963
"st.display.vision_mode.label": "الوضع المرئي",
964-
"st.display.vision_mode.desc": "الرؤية فقط (aka \"Tesla\" mode)، مثل سيارة Tesla تقود بالكاميرات وحدها. عند الإيقاف يشبه Waymo أكثر، إذ يضيف حساسات أخرى: يقرأ الوكيل شجرة إمكانية الوصول أيضًا. في وضع التنفيذ يرى الوكيل الصفحة كلقطات شاشة وينقر ويكتب ويمرّر عند إحداثيات الشاشة بدلًا من قراءة شجرة إمكانية الوصول. يتطلب نموذجًا يقبل الصور؛ وتواصل النماذج الأخرى استخدام شجرة إمكانية الوصول. بعد كل إجراء يُرسَل الجزء المتغيّر من الشاشة فقط. تُعالَج شاشات Retina وتكبير المتصفح تلقائيًا.",
964+
"st.display.vision_mode.desc": "الرؤية فقط (aka \"Tesla\" mode)، مثل سيارة Tesla تقود بالكاميرات وحدها. في وضع التنفيذ يرى الوكيل الصفحة كلقطات شاشة وينقر ويكتب ويمرّر عند إحداثيات الشاشة بدلًا من قراءة شجرة إمكانية الوصول. عند الإيقاف يشبه Waymo أكثر، إذ يضيف حساسات أخرى: يقرأ الوكيل شجرة إمكانية الوصول أيضًا. يتطلب نموذجًا يقبل الصور؛ وتواصل النماذج الأخرى استخدام شجرة إمكانية الوصول. بعد كل إجراء يُرسَل الجزء المتغيّر من الشاشة فقط. تُعالَج شاشات Retina وتكبير المتصفح تلقائيًا.",
965965
"st.display.vision_mode.auto": "تلقائي",
966966
"st.display.vision_mode.off": "إيقاف",
967967
"st.display.vision_quality.label": "حجم لقطات الوضع المرئي",

0 commit comments

Comments
 (0)