@circuitwall/jarela 1.3.0 → 1.4.1

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 (102) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.html +2 -2
  15. package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  22. package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js +10 -1
  23. package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js.map +1 -1
  24. package/.next/standalone/.next/server/app/api/v1/dashboard/currency/route.js +10 -5
  25. package/.next/standalone/.next/server/app/api/v1/dashboard/currency/route.js.map +1 -1
  26. package/.next/standalone/.next/server/app/api/v1/page-capture/route.js +37 -3
  27. package/.next/standalone/.next/server/app/api/v1/page-capture/route.js.map +1 -1
  28. package/.next/standalone/.next/server/app/api/v1/providers/[provider]/probe/route.js +9 -1
  29. package/.next/standalone/.next/server/app/api/v1/providers/[provider]/probe/route.js.map +1 -1
  30. package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js +33 -8
  31. package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js.map +1 -1
  32. package/.next/standalone/.next/server/app/page.js +73 -204
  33. package/.next/standalone/.next/server/app/page.js.map +1 -1
  34. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/setup/page.js +1 -1
  37. package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  39. package/.next/standalone/.next/server/chunks/1718.js +159 -0
  40. package/.next/standalone/.next/server/chunks/1718.js.map +1 -0
  41. package/.next/standalone/.next/server/chunks/2082.js +6 -3
  42. package/.next/standalone/.next/server/chunks/2082.js.map +1 -1
  43. package/.next/standalone/.next/server/chunks/210.js +28 -0
  44. package/.next/standalone/.next/server/chunks/210.js.map +1 -1
  45. package/.next/standalone/.next/server/chunks/423.js +6 -3
  46. package/.next/standalone/.next/server/chunks/423.js.map +1 -1
  47. package/.next/standalone/.next/server/chunks/4631.js +37 -5
  48. package/.next/standalone/.next/server/chunks/4631.js.map +1 -1
  49. package/.next/standalone/.next/server/chunks/8167.js +255 -204
  50. package/.next/standalone/.next/server/chunks/8167.js.map +1 -1
  51. package/.next/standalone/.next/server/chunks/8866.js +38 -5
  52. package/.next/standalone/.next/server/chunks/8866.js.map +1 -1
  53. package/.next/standalone/.next/server/chunks/9032.js +8 -0
  54. package/.next/standalone/.next/server/chunks/9032.js.map +1 -1
  55. package/.next/standalone/.next/server/chunks/{7883.js → 9557.js} +15 -3
  56. package/.next/standalone/.next/server/chunks/9557.js.map +1 -0
  57. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  58. package/.next/standalone/.next/server/middleware.js +6 -3
  59. package/.next/standalone/.next/server/pages/404.html +2 -2
  60. package/.next/standalone/.next/server/pages/500.html +1 -1
  61. package/.next/standalone/.next/server/proxy.js.map +1 -1
  62. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  63. package/.next/standalone/.next/static/chunks/{2351-68d8987bbe17ba2d.js → 2351-1ab119fb3b48f4c9.js} +258 -205
  64. package/.next/standalone/.next/static/chunks/2351-1ab119fb3b48f4c9.js.map +1 -0
  65. package/.next/standalone/.next/static/chunks/{9209-0d46118e502f8bf5.js → 4097-64691f9110cf167c.js} +14 -2
  66. package/.next/standalone/.next/static/chunks/4097-64691f9110cf167c.js.map +1 -0
  67. package/.next/standalone/.next/static/chunks/app/{page-2ab710949b62a638.js → page-145150e0468544e7.js} +74 -205
  68. package/.next/standalone/.next/static/chunks/app/page-145150e0468544e7.js.map +1 -0
  69. package/.next/standalone/.next/static/chunks/app/setup/{page-9a465b5fa755b3c3.js → page-a1463a9ace439ff7.js} +2 -2
  70. package/.next/standalone/.next/static/chunks/app/setup/{page-9a465b5fa755b3c3.js.map → page-a1463a9ace439ff7.js.map} +1 -1
  71. package/.next/standalone/.next/static/chunks/{webpack-ff5627013a5e3842.js → webpack-f4ac5c5f92cfd1c1.js} +13 -1
  72. package/.next/standalone/.next/static/chunks/webpack-f4ac5c5f92cfd1c1.js.map +1 -0
  73. package/.next/standalone/package.json +2 -1
  74. package/CHANGELOG.md +84 -0
  75. package/README.md +51 -26
  76. package/api/client.ts +10 -9
  77. package/app/api/v1/dashboard/currency/route.ts +7 -2
  78. package/app/api/v1/providers/[provider]/probe/route.ts +12 -1
  79. package/app/api/v1/threads/[thread_id]/run/route.ts +22 -8
  80. package/components/chat/InputBar.tsx +10 -1
  81. package/components/layout/AppShell.tsx +53 -17
  82. package/components/setup/PinKeypad.tsx +238 -0
  83. package/components/setup/ScreenLock.tsx +8 -173
  84. package/components/setup/UnlockScreen.tsx +25 -192
  85. package/lib/api/page-capture.test.ts +58 -0
  86. package/lib/api/page-capture.ts +31 -1
  87. package/lib/documents/remote/github.ts +16 -2
  88. package/lib/documents/remote/mail.ts +11 -2
  89. package/lib/lifecycle/shutdown.ts +9 -0
  90. package/lib/providers/github-copilot-auth.ts +2 -0
  91. package/lib/providers/github-copilot.ts +1 -0
  92. package/lib/tools/async-results.ts +11 -0
  93. package/package.json +2 -1
  94. package/scripts/install-to-system.ps1 +2 -2
  95. package/scripts/installed-launcher.ps1 +81 -17
  96. package/.next/standalone/.next/server/chunks/7883.js.map +0 -1
  97. package/.next/standalone/.next/static/chunks/2351-68d8987bbe17ba2d.js.map +0 -1
  98. package/.next/standalone/.next/static/chunks/9209-0d46118e502f8bf5.js.map +0 -1
  99. package/.next/standalone/.next/static/chunks/app/page-2ab710949b62a638.js.map +0 -1
  100. package/.next/standalone/.next/static/chunks/webpack-ff5627013a5e3842.js.map +0 -1
  101. /package/.next/standalone/.next/static/{ZKy7LJ3KXj2TIyKOg_fBH → WQdcnm9NyqpeNc0Z8_woo}/_buildManifest.js +0 -0
  102. /package/.next/standalone/.next/static/{ZKy7LJ3KXj2TIyKOg_fBH → WQdcnm9NyqpeNc0Z8_woo}/_ssgManifest.js +0 -0
@@ -1819,6 +1819,11 @@ function fileToContentPart(file) {
1819
1819
  }
1820
1820
  });
1821
1821
  }
1822
+ function attachmentKey(a, i) {
1823
+ if (a.type === "text") return `text:${i}:${a.text.length}`;
1824
+ const name = a.type === "file" ? a.name : "";
1825
+ return `${a.type}:${a.media_type}:${name}:${a.data.length}:${a.data.slice(0, 16)}`;
1826
+ }
1822
1827
  function InputBar({ attachments, onAttachmentsChange, onSubmit, onQueue, onStop, streaming, disabled, placeholder, voiceEnabled, agentId, onVoiceTranscript }) {
1823
1828
  // Text state is intentionally LOCAL. Lifting it to ChatView would re-render
1824
1829
  // the entire message list (every MessageBubble + ReactMarkdown pass) on
@@ -2023,7 +2028,10 @@ function InputBar({ attachments, onAttachmentsChange, onSubmit, onQueue, onStop,
2023
2028
  children: [
2024
2029
  attachments.length > 0 && /*#__PURE__*/ (0,jsx_runtime.jsx)("div", {
2025
2030
  className: "flex flex-wrap gap-2 mb-2",
2026
- children: attachments.map((a, i)=>/*#__PURE__*/ (0,jsx_runtime.jsxs)("div", {
2031
+ children: attachments.map((a, i)=>// Content-derived key — using the index reused DOM nodes when
2032
+ // earlier attachments were removed, flashing the wrong preview
2033
+ // (and the wrong filename) into the slot of the survivor.
2034
+ /*#__PURE__*/ (0,jsx_runtime.jsxs)("div", {
2027
2035
  className: "relative group shrink-0",
2028
2036
  children: [
2029
2037
  a.type === "image" ? // eslint-disable-next-line @next/next/no-img-element
@@ -2052,7 +2060,7 @@ function InputBar({ attachments, onAttachmentsChange, onSubmit, onQueue, onStop,
2052
2060
  })
2053
2061
  })
2054
2062
  ]
2055
- }, i))
2063
+ }, attachmentKey(a, i)))
2056
2064
  }),
2057
2065
  /*#__PURE__*/ (0,jsx_runtime.jsxs)("div", {
2058
2066
  className: "relative flex gap-2 items-end",
@@ -23088,195 +23096,26 @@ function BootScreen({ agents, agentsLoaded, activeAgentId, onPickAgent, suppress
23088
23096
  });
23089
23097
  }
23090
23098
 
23099
+ // EXTERNAL MODULE: ./components/setup/PinKeypad.tsx
23100
+ var PinKeypad = __webpack_require__(772);
23091
23101
  ;// ./components/setup/ScreenLock.tsx
23092
23102
  /* __next_internal_client_entry_do_not_use__ ScreenLock auto */
23093
23103
 
23094
-
23095
- // Screen-lock overlay (presence check). Shown when the server reports
23096
- // `screen_locked: true` after an idle timeout. Distinct from
23097
- // UnlockScreen this does NOT touch the in-memory master key, just
23098
- // verifies the human at the keyboard knows the PIN. Background work
23099
- // (agents, scheduler, bridges) keeps running underneath.
23100
- const PIN_LENGTH = 6;
23104
+ // Screen-lock overlay (presence check). Mounted by AppShell when the
23105
+ // idle timer fires or the server returns 423 `screen-locked`. Does
23106
+ // NOT touch the in-memory master key background work (agents,
23107
+ // scheduler, bridges) keeps running underneath. The /verify-pin
23108
+ // endpoint just confirms the human at the keyboard and clears the
23109
+ // idle flag.
23101
23110
  function ScreenLock({ onUnlock }) {
23102
- const [digits, setDigits] = (0,react.useState)("");
23103
- const [error, setError] = (0,react.useState)(null);
23104
- const [submitting, setSubmitting] = (0,react.useState)(false);
23105
- const [retryAfterSec, setRetryAfterSec] = (0,react.useState)(0);
23106
- const submittingRef = (0,react.useRef)(false);
23107
- const submit = (0,react.useCallback)(async (pin)=>{
23108
- if (submittingRef.current) return;
23109
- submittingRef.current = true;
23110
- setSubmitting(true);
23111
- setError(null);
23112
- try {
23113
- const res = await fetch("/api/v1/security/verify-pin", {
23114
- method: "POST",
23115
- headers: {
23116
- "content-type": "application/json"
23117
- },
23118
- body: JSON.stringify({
23119
- pin
23120
- })
23121
- });
23122
- if (res.ok) {
23123
- onUnlock();
23124
- return;
23125
- }
23126
- const body = await res.json().catch(()=>({}));
23127
- if (res.status === 429 && typeof body.retry_after_ms === "number") {
23128
- setRetryAfterSec(Math.ceil(body.retry_after_ms / 1000));
23129
- setError("Too many attempts. Try again later.");
23130
- } else if (res.status === 401) {
23131
- setError("Wrong PIN. Try again.");
23132
- } else if (res.status === 400) {
23133
- setError("Invalid PIN format.");
23134
- } else {
23135
- setError(body.error ?? `Error (${res.status})`);
23136
- }
23137
- setDigits("");
23138
- } catch (err) {
23139
- setError(err instanceof Error ? err.message : String(err));
23140
- setDigits("");
23141
- } finally{
23142
- submittingRef.current = false;
23143
- setSubmitting(false);
23144
- }
23145
- }, [
23146
- onUnlock
23147
- ]);
23148
- const append = (0,react.useCallback)((d)=>{
23149
- if (submitting || retryAfterSec > 0) return;
23150
- setError(null);
23151
- setDigits((cur)=>cur.length >= PIN_LENGTH ? cur : cur + d);
23152
- }, [
23153
- submitting,
23154
- retryAfterSec
23155
- ]);
23156
- (0,react.useEffect)(()=>{
23157
- if (digits.length === PIN_LENGTH && !submittingRef.current) {
23158
- void submit(digits);
23159
- }
23160
- }, [
23161
- digits,
23162
- submit
23163
- ]);
23164
- const backspace = (0,react.useCallback)(()=>{
23165
- if (submitting) return;
23166
- setError(null);
23167
- setDigits((cur)=>cur.slice(0, -1));
23168
- }, [
23169
- submitting
23170
- ]);
23171
- (0,react.useEffect)(()=>{
23172
- function onKey(e) {
23173
- if (/^[0-9]$/.test(e.key)) {
23174
- e.preventDefault();
23175
- append(e.key);
23176
- } else if (e.key === "Backspace") {
23177
- e.preventDefault();
23178
- backspace();
23179
- }
23180
- }
23181
- window.addEventListener("keydown", onKey);
23182
- return ()=>window.removeEventListener("keydown", onKey);
23183
- }, [
23184
- append,
23185
- backspace
23186
- ]);
23187
- (0,react.useEffect)(()=>{
23188
- if (retryAfterSec <= 0) return;
23189
- const t = setInterval(()=>{
23190
- setRetryAfterSec((s)=>s > 0 ? s - 1 : 0);
23191
- }, 1000);
23192
- return ()=>clearInterval(t);
23193
- }, [
23194
- retryAfterSec
23195
- ]);
23196
- return /*#__PURE__*/ (0,jsx_runtime.jsxs)("div", {
23197
- className: "fixed inset-0 z-[1000] flex flex-col items-center justify-center gap-6 bg-surface text-fg",
23198
- style: {
23199
- paddingTop: "env(safe-area-inset-top)",
23200
- paddingBottom: "env(safe-area-inset-bottom)"
23201
- },
23202
- children: [
23203
- /*#__PURE__*/ (0,jsx_runtime.jsx)(Logo/* Logo */.g, {
23204
- className: "h-16 w-auto"
23205
- }),
23206
- /*#__PURE__*/ (0,jsx_runtime.jsxs)("div", {
23207
- className: "w-full max-w-xs p-6",
23208
- children: [
23209
- /*#__PURE__*/ (0,jsx_runtime.jsx)("h1", {
23210
- className: "mb-1 text-center text-lg font-semibold text-fg",
23211
- children: "Locked"
23212
- }),
23213
- /*#__PURE__*/ (0,jsx_runtime.jsx)("p", {
23214
- className: "mb-6 text-center text-xs text-fg-faint",
23215
- children: "Enter your 6-digit PIN to resume."
23216
- }),
23217
- /*#__PURE__*/ (0,jsx_runtime.jsx)("div", {
23218
- className: "mb-6 flex justify-center gap-3",
23219
- "aria-label": "PIN entry progress",
23220
- children: Array.from({
23221
- length: PIN_LENGTH
23222
- }).map((_, i)=>/*#__PURE__*/ (0,jsx_runtime.jsx)("span", {
23223
- className: `h-3 w-3 rounded-full transition-colors ${i < digits.length ? error ? "bg-red-500" : "bg-fg" : "bg-surface-3"}`
23224
- }, i))
23225
- }),
23226
- /*#__PURE__*/ (0,jsx_runtime.jsxs)("div", {
23227
- className: "grid grid-cols-3 gap-2",
23228
- children: [
23229
- [
23230
- "1",
23231
- "2",
23232
- "3",
23233
- "4",
23234
- "5",
23235
- "6",
23236
- "7",
23237
- "8",
23238
- "9"
23239
- ].map((d)=>/*#__PURE__*/ (0,jsx_runtime.jsx)(PinKey, {
23240
- digit: d,
23241
- onPress: ()=>append(d),
23242
- disabled: submitting || retryAfterSec > 0
23243
- }, d)),
23244
- /*#__PURE__*/ (0,jsx_runtime.jsx)("div", {}),
23245
- /*#__PURE__*/ (0,jsx_runtime.jsx)(PinKey, {
23246
- digit: "0",
23247
- onPress: ()=>append("0"),
23248
- disabled: submitting || retryAfterSec > 0
23249
- }),
23250
- /*#__PURE__*/ (0,jsx_runtime.jsx)(PinKey, {
23251
- digit: "←",
23252
- onPress: backspace,
23253
- disabled: submitting || digits.length === 0,
23254
- ariaLabel: "Backspace"
23255
- })
23256
- ]
23257
- }),
23258
- /*#__PURE__*/ (0,jsx_runtime.jsx)("p", {
23259
- className: `mt-4 min-h-[1.5rem] text-center text-xs ${error ? "text-red-400" : "text-fg-faint"}`,
23260
- role: "status",
23261
- "aria-live": "polite",
23262
- children: retryAfterSec > 0 ? `Try again in ${retryAfterSec}s` : error ?? (submitting ? "Verifying…" : "\u00A0")
23263
- })
23264
- ]
23265
- })
23266
- ]
23267
- });
23268
- }
23269
- function PinKey({ digit, onPress, disabled, ariaLabel }) {
23270
- return /*#__PURE__*/ (0,jsx_runtime.jsx)("button", {
23271
- type: "button",
23272
- onClick: onPress,
23273
- disabled: disabled,
23274
- "aria-label": ariaLabel ?? digit,
23275
- className: "h-14 rounded-xl bg-surface-3 text-xl font-medium text-fg transition-colors hover:bg-surface-3/70 active:bg-surface-3/50 disabled:cursor-not-allowed disabled:opacity-50",
23276
- children: digit
23111
+ return /*#__PURE__*/ (0,jsx_runtime.jsx)(PinKeypad/* PinKeypad */.y, {
23112
+ mode: "unlock",
23113
+ onSuccess: onUnlock
23277
23114
  });
23278
23115
  }
23279
23116
 
23117
+ // EXTERNAL MODULE: ./components/setup/UnlockScreen.tsx
23118
+ var UnlockScreen = __webpack_require__(2283);
23280
23119
  // EXTERNAL MODULE: ./node_modules/lucide-react/dist/esm/icons/message-square.mjs
23281
23120
  var message_square = __webpack_require__(5494);
23282
23121
  // EXTERNAL MODULE: ./node_modules/lucide-react/dist/esm/icons/chart-column.mjs
@@ -25715,6 +25554,7 @@ function InteractiveCostChart({ series, currencyInfo, selectedDay, onSelectDay }
25715
25554
 
25716
25555
 
25717
25556
 
25557
+
25718
25558
 
25719
25559
 
25720
25560
  const AppShell_ADVANCED_TABS = new Set([
@@ -25885,24 +25725,30 @@ function AppShell() {
25885
25725
  showAgentPicker
25886
25726
  ]);
25887
25727
  const activeAgent = state.activeAgentId ? agents.find((a)=>a.id === state.activeAgentId) ?? null : null;
25888
- // Screen-lock overlay. Distinct from the boot-time master-key unlock
25889
- // (that's gated server-side in `app/page.tsx`). This one is the
25890
- // presence check that fires after `idle_timeout_ms` of inactivity:
25891
- // background work keeps running but the UI is hidden until the user
25892
- // re-enters their PIN. Triggered either by a 423 `screen-locked`
25893
- // response from the api client or by the periodic state probe below.
25728
+ // Screen-lock overlay (presence check) AND master-key-locked overlay
25729
+ // (decrypt). Distinct from the boot-time gate in `app/page.tsx`:
25730
+ // those mounts are triggered mid-session either by an idle timer
25731
+ // (screen-lock) or by the master key being re-locked by an external
25732
+ // process (decrypt). Both are signalled by the API client when it
25733
+ // sees the matching 423 response.
25894
25734
  const [screenLocked, setScreenLocked] = (0,react.useState)(false);
25735
+ const [masterKeyLocked, setMasterKeyLocked] = (0,react.useState)(false);
25895
25736
  // Bumped after each unlock so BootScreen remounts with fresh state
25896
25737
  // (its `done` / `pickedId` / `prefetchStartedRef` would otherwise
25897
- // suppress the picker on the second appearance).
25738
+ // suppress the picker on the second appearance). Both unlock paths
25739
+ // bump this — the agent selector is always the post-unlock landing.
25898
25740
  const [bootSeq, setBootSeq] = (0,react.useState)(0);
25899
25741
  (0,react.useEffect)(()=>{
25900
25742
  let cancelled = false;
25901
25743
  let timer = null;
25902
- function onLocked() {
25744
+ function onScreenLocked() {
25903
25745
  if (!cancelled) setScreenLocked(true);
25904
25746
  }
25905
- window.addEventListener("jarela:screen-locked", onLocked);
25747
+ function onMasterKeyLocked() {
25748
+ if (!cancelled) setMasterKeyLocked(true);
25749
+ }
25750
+ window.addEventListener("jarela:screen-locked", onScreenLocked);
25751
+ window.addEventListener("jarela:master-key-locked", onMasterKeyLocked);
25906
25752
  // Soft poll every 30s so the overlay still appears if no user
25907
25753
  // action triggered a request after the idle timer elapsed.
25908
25754
  async function probe() {
@@ -25910,11 +25756,13 @@ function AppShell() {
25910
25756
  const res = await fetch("/api/v1/security/state");
25911
25757
  if (!res.ok) return;
25912
25758
  const body = await res.json();
25913
- if (!cancelled && body.screen_locked === true) {
25914
- setScreenLocked(true);
25915
- }
25916
- } catch {
25917
- // Network blip; try again next tick.
25759
+ if (cancelled) return;
25760
+ if (body.state === "locked") setMasterKeyLocked(true);
25761
+ if (body.screen_locked === true) setScreenLocked(true);
25762
+ } catch (err) {
25763
+ // Network blip; try again next tick. Logged at debug-level so
25764
+ // a sustained outage is at least findable in devtools.
25765
+ if (false) {}
25918
25766
  }
25919
25767
  }
25920
25768
  void probe();
@@ -25922,9 +25770,22 @@ function AppShell() {
25922
25770
  return ()=>{
25923
25771
  cancelled = true;
25924
25772
  if (timer) clearInterval(timer);
25925
- window.removeEventListener("jarela:screen-locked", onLocked);
25773
+ window.removeEventListener("jarela:screen-locked", onScreenLocked);
25774
+ window.removeEventListener("jarela:master-key-locked", onMasterKeyLocked);
25926
25775
  };
25927
25776
  }, []);
25777
+ // Shared post-unlock landing: clear the current chat and force the
25778
+ // BootScreen to remount so the user lands on the agent picker. Used
25779
+ // by BOTH the screen-unlock and the master-key decrypt paths so the
25780
+ // two transitions feel identical from the user's side.
25781
+ const landOnAgentPicker = (0,react.useCallback)(()=>{
25782
+ dispatch({
25783
+ type: "NEW_CHAT"
25784
+ });
25785
+ setBootSeq((n)=>n + 1);
25786
+ }, [
25787
+ dispatch
25788
+ ]);
25928
25789
  return(// `dvh` natively tracks the visible viewport on iOS 16.4+ / modern
25929
25790
  // Chromium, including the on-screen keyboard. The `--actual-vh`
25930
25791
  // override (set by the iOS-standalone-PWA shim in layout.tsx) covers
@@ -25958,13 +25819,21 @@ function AppShell() {
25958
25819
  onUnlock: ()=>{
25959
25820
  // Drop the user back on the picker so they consciously
25960
25821
  // re-enter their workspace rather than landing mid-chat.
25961
- dispatch({
25962
- type: "NEW_CHAT"
25963
- });
25964
- setBootSeq((n)=>n + 1);
25822
+ landOnAgentPicker();
25965
25823
  setScreenLocked(false);
25966
25824
  }
25967
25825
  }),
25826
+ masterKeyLocked && !screenLocked && // Master key got re-locked mid-session (e.g. external lock
25827
+ // command). Mount the decrypt splash — same shared keypad as
25828
+ // boot — and on success drop back to the agent picker. The
25829
+ // existing components below stay mounted underneath; once
25830
+ // unlocked they resume against the now-unlocked DB.
25831
+ /*#__PURE__*/ (0,jsx_runtime.jsx)(UnlockScreen.UnlockScreen, {
25832
+ onUnlock: ()=>{
25833
+ landOnAgentPicker();
25834
+ setMasterKeyLocked(false);
25835
+ }
25836
+ }),
25968
25837
  /*#__PURE__*/ (0,jsx_runtime.jsx)(NotificationStatus, {}),
25969
25838
  /*#__PURE__*/ (0,jsx_runtime.jsx)(Toaster, {}),
25970
25839
  /*#__PURE__*/ (0,jsx_runtime.jsx)(ServerStatus, {}),
@@ -26212,9 +26081,9 @@ Promise.resolve(/* import() eager */).then(__webpack_require__.bind(__webpack_re
26212
26081
  },
26213
26082
  /******/ __webpack_require__ => { // webpackRuntimeModules
26214
26083
  /******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId))
26215
- /******/ __webpack_require__.O(0, [1973,9209,7537,2351,8441,3457,7358], () => (__webpack_exec__(9852)));
26084
+ /******/ __webpack_require__.O(0, [1973,4097,7537,2351,8441,3457,7358], () => (__webpack_exec__(9852)));
26216
26085
  /******/ var __webpack_exports__ = __webpack_require__.O();
26217
26086
  /******/ _N_E = __webpack_exports__;
26218
26087
  /******/ }
26219
26088
  ]);
26220
- //# sourceMappingURL=page-2ab710949b62a638.js.map
26089
+ //# sourceMappingURL=page-145150e0468544e7.js.map