sealkeep 0.11.5 → 0.11.6

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
@@ -3,6 +3,13 @@
3
3
  Notable changes, by published version. Sealkeep is pre-1.0: minor versions
4
4
  may change behavior, and say so here when they do.
5
5
 
6
+ ## 0.11.6 — 2026-09-25 — reconnect and Recovery Kit limits are separate
7
+
8
+ - After a machine reconnects successfully, the Recovery Kit page says so even
9
+ if a later cloud check is paused by a service limit. It shows when that
10
+ check can be retried and no longer asks for another reconnect that cannot
11
+ lift the limit. The local recovery phrase and existing vault are unchanged.
12
+
6
13
  ## 0.11.5 — 2026-09-23 — compacting is safe, and a lost destination says so
7
14
 
8
15
  - **An agent that has just been compacted is told where the work stands.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sealkeep",
3
- "version": "0.11.5",
3
+ "version": "0.11.6",
4
4
  "type": "module",
5
5
  "description": "Sealkeep by SPALA AI — your AI coding-agent history, sealed, searchable, and shared across your machines.",
6
6
  "repository": {
package/web/app.js CHANGED
@@ -435,7 +435,7 @@ async function api(path, options = {}, preparationAttempt = 0) {
435
435
  error.status = response.status;
436
436
  // Structured refusals (lastCopy, forever) ride to the caller so a button
437
437
  // can change its own words instead of string-matching prose.
438
- if (body?.error && typeof body.error === "object") Object.assign(error, { code: body.error.code, status: response.status, forever: body.error.forever, lastCopy: body.error.lastCopy });
438
+ if (body?.error && typeof body.error === "object") Object.assign(error, { code: body.error.code, status: response.status, forever: body.error.forever, lastCopy: body.error.lastCopy, retryAt: body.error.retryAt });
439
439
  throw error;
440
440
  }
441
441
  if (path === "/v1/archives" && Array.isArray(body)) {
@@ -3683,7 +3683,14 @@ async function renderAccountCard(status, options) {
3683
3683
  }
3684
3684
  }
3685
3685
 
3686
- let recoveryAccessState = { vaultExists: false, phraseHeld: null, kit: null };
3686
+ let recoveryAccessState = { vaultExists: false, phraseHeld: null, kit: null, kitCheckPausedUntil: null, accountReconnected: false };
3687
+
3688
+ function recoveryKitPauseMessage() {
3689
+ const retryAt = recoveryAccessState.kitCheckPausedUntil;
3690
+ const when = retryAt && Number.isFinite(Date.parse(retryAt))
3691
+ ? ` until ${new Date(retryAt).toLocaleString()}` : " until the service limit resets";
3692
+ return `${recoveryAccessState.accountReconnected ? 'This computer reconnected successfully. ' : ''}Cloud Recovery Kit check is paused${when}. Reconnecting again will not lift the service limit.`;
3693
+ }
3687
3694
 
3688
3695
  function paintRecoveryAccess() {
3689
3696
  const panel = $("recovery-access-panel");
@@ -3697,7 +3704,9 @@ function paintRecoveryAccess() {
3697
3704
  ? "Keychain: this machine does not hold its recovery phrase."
3698
3705
  : "Keychain: recovery phrase status is not available yet.";
3699
3706
  const kit = recoveryAccessState.kit;
3700
- $("recovery-access-cloud").textContent = kit?.eligible === true
3707
+ $("recovery-access-cloud").textContent = recoveryAccessState.kitCheckPausedUntil
3708
+ ? recoveryKitPauseMessage()
3709
+ : kit?.eligible === true
3701
3710
  ? kit.available
3702
3711
  ? `Cloud Recovery Kit: ${kit.remaining} unused backup code${kit.remaining === 1 ? " remains" : "s remain"}.`
3703
3712
  : "Cloud Recovery Kit: no unused backup codes are available."
@@ -3764,6 +3773,7 @@ async function pollAccountReconnect() {
3764
3773
  return;
3765
3774
  }
3766
3775
  accountReconnect = null; button.hidden = true; button.href = '#recovery-kit'; button.textContent = 'Reconnect account';
3776
+ recoveryAccessState = { ...recoveryAccessState, accountReconnected: true };
3767
3777
  message.textContent = 'This machine is reconnected. Checking your existing Recovery Kit…';
3768
3778
  overviewDetailCheckedAt = 0;
3769
3779
  try {
@@ -3812,6 +3822,7 @@ $('recovery-kit-account').onclick = async (event) => {
3812
3822
  try {
3813
3823
  const renewed = await api('/v1/account/reconnect', {method:'POST',body:'{}'});
3814
3824
  if (renewed.reconnected) {
3825
+ recoveryAccessState = { ...recoveryAccessState, accountReconnected: true };
3815
3826
  try { popup?.close(); } catch {}
3816
3827
  message.textContent = 'This computer is reconnected using its existing device seat. Checking recovery access…';
3817
3828
  overviewDetailCheckedAt = 0;
@@ -3850,7 +3861,7 @@ async function renderRecoveryKit() {
3850
3861
  try {
3851
3862
  const kit = await api("/v1/recovery-kit");
3852
3863
  if (accountReconnect) return;
3853
- recoveryAccessState = { ...recoveryAccessState, kit };
3864
+ recoveryAccessState = { ...recoveryAccessState, kit, kitCheckPausedUntil: null };
3854
3865
  paintRecoveryAccess();
3855
3866
  account.hidden = true;
3856
3867
  if (kit.eligible == null) {
@@ -3873,9 +3884,14 @@ async function renderRecoveryKit() {
3873
3884
  button.textContent = kit.available ? "Replace backup codes" : "Create Recovery Kit";
3874
3885
  } catch (error) {
3875
3886
  if (accountReconnect) return;
3876
- recoveryAccessState = { ...recoveryAccessState, kit: null };
3887
+ recoveryAccessState = { ...recoveryAccessState, kit: null, kitCheckPausedUntil: error.code === 'rate_limited' ? (error.retryAt || 'pending') : null };
3877
3888
  paintRecoveryAccess();
3878
3889
  button.hidden = true;
3890
+ if (error.code === 'rate_limited') {
3891
+ account.hidden = true;
3892
+ state.textContent = recoveryKitPauseMessage();
3893
+ return;
3894
+ }
3879
3895
  const needsAccount = /JWT|credential|sign|account|unauthorized/i.test(error.message);
3880
3896
  account.hidden = !needsAccount;
3881
3897
  state.textContent = needsAccount
@@ -3923,7 +3939,9 @@ function recoveryAccessSummary() {
3923
3939
  if (kit?.eligible === true) {
3924
3940
  return { message: "This machine can open your memories, but no usable Cloud Recovery Kit exists. Create one below for lost-machine recovery.", tone: "warn" };
3925
3941
  }
3926
- return { message: "This machine can open your memories, but Sealkeep could not confirm off-machine recovery. Reconnect the account and check again.", tone: "warn" };
3942
+ return { message: recoveryAccessState.kitCheckPausedUntil
3943
+ ? `This machine can open your memories, but off-machine recovery cannot be confirmed right now. ${recoveryKitPauseMessage()}`
3944
+ : "This machine can open your memories, but Sealkeep could not confirm off-machine recovery. Reconnect the account and check again.", tone: "warn" };
3927
3945
  }
3928
3946
 
3929
3947
  async function recoverMachine() {