@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
@@ -3642,6 +3642,11 @@ function fileToContentPart(file) {
3642
3642
  }
3643
3643
  });
3644
3644
  }
3645
+ function attachmentKey(a, i) {
3646
+ if (a.type === "text") return `text:${i}:${a.text.length}`;
3647
+ const name = a.type === "file" ? a.name : "";
3648
+ return `${a.type}:${a.media_type}:${name}:${a.data.length}:${a.data.slice(0, 16)}`;
3649
+ }
3645
3650
  function InputBar({ attachments, onAttachmentsChange, onSubmit, onQueue, onStop, streaming, disabled, placeholder, voiceEnabled, agentId, onVoiceTranscript }) {
3646
3651
  // Text state is intentionally LOCAL. Lifting it to ChatView would re-render
3647
3652
  // the entire message list (every MessageBubble + ReactMarkdown pass) on
@@ -3846,7 +3851,10 @@ function InputBar({ attachments, onAttachmentsChange, onSubmit, onQueue, onStop,
3846
3851
  children: [
3847
3852
  attachments.length > 0 && /*#__PURE__*/ (0,react_jsx_runtime.jsx)("div", {
3848
3853
  className: "flex flex-wrap gap-2 mb-2",
3849
- children: attachments.map((a, i)=>/*#__PURE__*/ (0,react_jsx_runtime.jsxs)("div", {
3854
+ children: attachments.map((a, i)=>// Content-derived key — using the index reused DOM nodes when
3855
+ // earlier attachments were removed, flashing the wrong preview
3856
+ // (and the wrong filename) into the slot of the survivor.
3857
+ /*#__PURE__*/ (0,react_jsx_runtime.jsxs)("div", {
3850
3858
  className: "relative group shrink-0",
3851
3859
  children: [
3852
3860
  a.type === "image" ? // eslint-disable-next-line @next/next/no-img-element
@@ -3875,7 +3883,7 @@ function InputBar({ attachments, onAttachmentsChange, onSubmit, onQueue, onStop,
3875
3883
  })
3876
3884
  })
3877
3885
  ]
3878
- }, i))
3886
+ }, attachmentKey(a, i)))
3879
3887
  }),
3880
3888
  /*#__PURE__*/ (0,react_jsx_runtime.jsxs)("div", {
3881
3889
  className: "relative flex gap-2 items-end",
@@ -62373,195 +62381,26 @@ function BootScreen({ agents, agentsLoaded, activeAgentId, onPickAgent, suppress
62373
62381
  });
62374
62382
  }
62375
62383
 
62384
+ // EXTERNAL MODULE: ./components/setup/PinKeypad.tsx
62385
+ var PinKeypad = __webpack_require__(71654);
62376
62386
  ;// ./components/setup/ScreenLock.tsx
62377
62387
  /* __next_internal_client_entry_do_not_use__ ScreenLock auto */
62378
62388
 
62379
-
62380
- // Screen-lock overlay (presence check). Shown when the server reports
62381
- // `screen_locked: true` after an idle timeout. Distinct from
62382
- // UnlockScreen this does NOT touch the in-memory master key, just
62383
- // verifies the human at the keyboard knows the PIN. Background work
62384
- // (agents, scheduler, bridges) keeps running underneath.
62385
- const PIN_LENGTH = 6;
62389
+ // Screen-lock overlay (presence check). Mounted by AppShell when the
62390
+ // idle timer fires or the server returns 423 `screen-locked`. Does
62391
+ // NOT touch the in-memory master key background work (agents,
62392
+ // scheduler, bridges) keeps running underneath. The /verify-pin
62393
+ // endpoint just confirms the human at the keyboard and clears the
62394
+ // idle flag.
62386
62395
  function ScreenLock({ onUnlock }) {
62387
- const [digits, setDigits] = (0,react.useState)("");
62388
- const [error, setError] = (0,react.useState)(null);
62389
- const [submitting, setSubmitting] = (0,react.useState)(false);
62390
- const [retryAfterSec, setRetryAfterSec] = (0,react.useState)(0);
62391
- const submittingRef = (0,react.useRef)(false);
62392
- const submit = (0,react.useCallback)(async (pin)=>{
62393
- if (submittingRef.current) return;
62394
- submittingRef.current = true;
62395
- setSubmitting(true);
62396
- setError(null);
62397
- try {
62398
- const res = await fetch("/api/v1/security/verify-pin", {
62399
- method: "POST",
62400
- headers: {
62401
- "content-type": "application/json"
62402
- },
62403
- body: JSON.stringify({
62404
- pin
62405
- })
62406
- });
62407
- if (res.ok) {
62408
- onUnlock();
62409
- return;
62410
- }
62411
- const body = await res.json().catch(()=>({}));
62412
- if (res.status === 429 && typeof body.retry_after_ms === "number") {
62413
- setRetryAfterSec(Math.ceil(body.retry_after_ms / 1000));
62414
- setError("Too many attempts. Try again later.");
62415
- } else if (res.status === 401) {
62416
- setError("Wrong PIN. Try again.");
62417
- } else if (res.status === 400) {
62418
- setError("Invalid PIN format.");
62419
- } else {
62420
- setError(body.error ?? `Error (${res.status})`);
62421
- }
62422
- setDigits("");
62423
- } catch (err) {
62424
- setError(err instanceof Error ? err.message : String(err));
62425
- setDigits("");
62426
- } finally{
62427
- submittingRef.current = false;
62428
- setSubmitting(false);
62429
- }
62430
- }, [
62431
- onUnlock
62432
- ]);
62433
- const append = (0,react.useCallback)((d)=>{
62434
- if (submitting || retryAfterSec > 0) return;
62435
- setError(null);
62436
- setDigits((cur)=>cur.length >= PIN_LENGTH ? cur : cur + d);
62437
- }, [
62438
- submitting,
62439
- retryAfterSec
62440
- ]);
62441
- process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react.useEffect)(()=>{
62442
- if (digits.length === PIN_LENGTH && !submittingRef.current) {
62443
- void submit(digits);
62444
- }
62445
- }, [
62446
- digits,
62447
- submit
62448
- ]);
62449
- const backspace = (0,react.useCallback)(()=>{
62450
- if (submitting) return;
62451
- setError(null);
62452
- setDigits((cur)=>cur.slice(0, -1));
62453
- }, [
62454
- submitting
62455
- ]);
62456
- process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react.useEffect)(()=>{
62457
- function onKey(e) {
62458
- if (/^[0-9]$/.test(e.key)) {
62459
- e.preventDefault();
62460
- append(e.key);
62461
- } else if (e.key === "Backspace") {
62462
- e.preventDefault();
62463
- backspace();
62464
- }
62465
- }
62466
- window.addEventListener("keydown", onKey);
62467
- return ()=>window.removeEventListener("keydown", onKey);
62468
- }, [
62469
- append,
62470
- backspace
62471
- ]);
62472
- process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react.useEffect)(()=>{
62473
- if (retryAfterSec <= 0) return;
62474
- const t = setInterval(()=>{
62475
- setRetryAfterSec((s)=>s > 0 ? s - 1 : 0);
62476
- }, 1000);
62477
- return ()=>clearInterval(t);
62478
- }, [
62479
- retryAfterSec
62480
- ]);
62481
- return /*#__PURE__*/ (0,react_jsx_runtime.jsxs)("div", {
62482
- className: "fixed inset-0 z-[1000] flex flex-col items-center justify-center gap-6 bg-surface text-fg",
62483
- style: {
62484
- paddingTop: "env(safe-area-inset-top)",
62485
- paddingBottom: "env(safe-area-inset-bottom)"
62486
- },
62487
- children: [
62488
- /*#__PURE__*/ (0,react_jsx_runtime.jsx)(Logo/* Logo */.g, {
62489
- className: "h-16 w-auto"
62490
- }),
62491
- /*#__PURE__*/ (0,react_jsx_runtime.jsxs)("div", {
62492
- className: "w-full max-w-xs p-6",
62493
- children: [
62494
- /*#__PURE__*/ (0,react_jsx_runtime.jsx)("h1", {
62495
- className: "mb-1 text-center text-lg font-semibold text-fg",
62496
- children: "Locked"
62497
- }),
62498
- /*#__PURE__*/ (0,react_jsx_runtime.jsx)("p", {
62499
- className: "mb-6 text-center text-xs text-fg-faint",
62500
- children: "Enter your 6-digit PIN to resume."
62501
- }),
62502
- /*#__PURE__*/ (0,react_jsx_runtime.jsx)("div", {
62503
- className: "mb-6 flex justify-center gap-3",
62504
- "aria-label": "PIN entry progress",
62505
- children: Array.from({
62506
- length: PIN_LENGTH
62507
- }).map((_, i)=>/*#__PURE__*/ (0,react_jsx_runtime.jsx)("span", {
62508
- className: `h-3 w-3 rounded-full transition-colors ${i < digits.length ? error ? "bg-red-500" : "bg-fg" : "bg-surface-3"}`
62509
- }, i))
62510
- }),
62511
- /*#__PURE__*/ (0,react_jsx_runtime.jsxs)("div", {
62512
- className: "grid grid-cols-3 gap-2",
62513
- children: [
62514
- [
62515
- "1",
62516
- "2",
62517
- "3",
62518
- "4",
62519
- "5",
62520
- "6",
62521
- "7",
62522
- "8",
62523
- "9"
62524
- ].map((d)=>/*#__PURE__*/ (0,react_jsx_runtime.jsx)(PinKey, {
62525
- digit: d,
62526
- onPress: ()=>append(d),
62527
- disabled: submitting || retryAfterSec > 0
62528
- }, d)),
62529
- /*#__PURE__*/ (0,react_jsx_runtime.jsx)("div", {}),
62530
- /*#__PURE__*/ (0,react_jsx_runtime.jsx)(PinKey, {
62531
- digit: "0",
62532
- onPress: ()=>append("0"),
62533
- disabled: submitting || retryAfterSec > 0
62534
- }),
62535
- /*#__PURE__*/ (0,react_jsx_runtime.jsx)(PinKey, {
62536
- digit: "←",
62537
- onPress: backspace,
62538
- disabled: submitting || digits.length === 0,
62539
- ariaLabel: "Backspace"
62540
- })
62541
- ]
62542
- }),
62543
- /*#__PURE__*/ (0,react_jsx_runtime.jsx)("p", {
62544
- className: `mt-4 min-h-[1.5rem] text-center text-xs ${error ? "text-red-400" : "text-fg-faint"}`,
62545
- role: "status",
62546
- "aria-live": "polite",
62547
- children: retryAfterSec > 0 ? `Try again in ${retryAfterSec}s` : error ?? (submitting ? "Verifying…" : "\u00A0")
62548
- })
62549
- ]
62550
- })
62551
- ]
62552
- });
62553
- }
62554
- function PinKey({ digit, onPress, disabled, ariaLabel }) {
62555
- return /*#__PURE__*/ (0,react_jsx_runtime.jsx)("button", {
62556
- type: "button",
62557
- onClick: onPress,
62558
- disabled: disabled,
62559
- "aria-label": ariaLabel ?? digit,
62560
- 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",
62561
- children: digit
62396
+ return /*#__PURE__*/ (0,react_jsx_runtime.jsx)(PinKeypad/* PinKeypad */.y, {
62397
+ mode: "unlock",
62398
+ onSuccess: onUnlock
62562
62399
  });
62563
62400
  }
62564
62401
 
62402
+ // EXTERNAL MODULE: ./components/setup/UnlockScreen.tsx
62403
+ var UnlockScreen = __webpack_require__(26751);
62565
62404
  // EXTERNAL MODULE: ./node_modules/lucide-react/dist/esm/icons/message-square.mjs
62566
62405
  var message_square = __webpack_require__(61022);
62567
62406
  ;// ./node_modules/lucide-react/dist/esm/icons/chart-column.mjs
@@ -65351,6 +65190,7 @@ function InteractiveCostChart({ series, currencyInfo, selectedDay, onSelectDay }
65351
65190
 
65352
65191
 
65353
65192
 
65193
+
65354
65194
 
65355
65195
 
65356
65196
  const AppShell_ADVANCED_TABS = new Set([
@@ -65521,24 +65361,30 @@ function AppShell() {
65521
65361
  showAgentPicker
65522
65362
  ]);
65523
65363
  const activeAgent = state.activeAgentId ? agents.find((a)=>a.id === state.activeAgentId) ?? null : null;
65524
- // Screen-lock overlay. Distinct from the boot-time master-key unlock
65525
- // (that's gated server-side in `app/page.tsx`). This one is the
65526
- // presence check that fires after `idle_timeout_ms` of inactivity:
65527
- // background work keeps running but the UI is hidden until the user
65528
- // re-enters their PIN. Triggered either by a 423 `screen-locked`
65529
- // response from the api client or by the periodic state probe below.
65364
+ // Screen-lock overlay (presence check) AND master-key-locked overlay
65365
+ // (decrypt). Distinct from the boot-time gate in `app/page.tsx`:
65366
+ // those mounts are triggered mid-session either by an idle timer
65367
+ // (screen-lock) or by the master key being re-locked by an external
65368
+ // process (decrypt). Both are signalled by the API client when it
65369
+ // sees the matching 423 response.
65530
65370
  const [screenLocked, setScreenLocked] = (0,react.useState)(false);
65371
+ const [masterKeyLocked, setMasterKeyLocked] = (0,react.useState)(false);
65531
65372
  // Bumped after each unlock so BootScreen remounts with fresh state
65532
65373
  // (its `done` / `pickedId` / `prefetchStartedRef` would otherwise
65533
- // suppress the picker on the second appearance).
65374
+ // suppress the picker on the second appearance). Both unlock paths
65375
+ // bump this — the agent selector is always the post-unlock landing.
65534
65376
  const [bootSeq, setBootSeq] = (0,react.useState)(0);
65535
65377
  process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react.useEffect)(()=>{
65536
65378
  let cancelled = false;
65537
65379
  let timer = null;
65538
- function onLocked() {
65380
+ function onScreenLocked() {
65539
65381
  if (!cancelled) setScreenLocked(true);
65540
65382
  }
65541
- window.addEventListener("jarela:screen-locked", onLocked);
65383
+ function onMasterKeyLocked() {
65384
+ if (!cancelled) setMasterKeyLocked(true);
65385
+ }
65386
+ window.addEventListener("jarela:screen-locked", onScreenLocked);
65387
+ window.addEventListener("jarela:master-key-locked", onMasterKeyLocked);
65542
65388
  // Soft poll every 30s so the overlay still appears if no user
65543
65389
  // action triggered a request after the idle timer elapsed.
65544
65390
  async function probe() {
@@ -65546,11 +65392,13 @@ function AppShell() {
65546
65392
  const res = await fetch("/api/v1/security/state");
65547
65393
  if (!res.ok) return;
65548
65394
  const body = await res.json();
65549
- if (!cancelled && body.screen_locked === true) {
65550
- setScreenLocked(true);
65551
- }
65552
- } catch {
65553
- // Network blip; try again next tick.
65395
+ if (cancelled) return;
65396
+ if (body.state === "locked") setMasterKeyLocked(true);
65397
+ if (body.screen_locked === true) setScreenLocked(true);
65398
+ } catch (err) {
65399
+ // Network blip; try again next tick. Logged at debug-level so
65400
+ // a sustained outage is at least findable in devtools.
65401
+ if (false) {}
65554
65402
  }
65555
65403
  }
65556
65404
  void probe();
@@ -65558,9 +65406,22 @@ function AppShell() {
65558
65406
  return ()=>{
65559
65407
  cancelled = true;
65560
65408
  if (timer) clearInterval(timer);
65561
- window.removeEventListener("jarela:screen-locked", onLocked);
65409
+ window.removeEventListener("jarela:screen-locked", onScreenLocked);
65410
+ window.removeEventListener("jarela:master-key-locked", onMasterKeyLocked);
65562
65411
  };
65563
65412
  }, []);
65413
+ // Shared post-unlock landing: clear the current chat and force the
65414
+ // BootScreen to remount so the user lands on the agent picker. Used
65415
+ // by BOTH the screen-unlock and the master-key decrypt paths so the
65416
+ // two transitions feel identical from the user's side.
65417
+ const landOnAgentPicker = (0,react.useCallback)(()=>{
65418
+ dispatch({
65419
+ type: "NEW_CHAT"
65420
+ });
65421
+ setBootSeq((n)=>n + 1);
65422
+ }, [
65423
+ dispatch
65424
+ ]);
65564
65425
  return(// `dvh` natively tracks the visible viewport on iOS 16.4+ / modern
65565
65426
  // Chromium, including the on-screen keyboard. The `--actual-vh`
65566
65427
  // override (set by the iOS-standalone-PWA shim in layout.tsx) covers
@@ -65594,13 +65455,21 @@ function AppShell() {
65594
65455
  onUnlock: ()=>{
65595
65456
  // Drop the user back on the picker so they consciously
65596
65457
  // re-enter their workspace rather than landing mid-chat.
65597
- dispatch({
65598
- type: "NEW_CHAT"
65599
- });
65600
- setBootSeq((n)=>n + 1);
65458
+ landOnAgentPicker();
65601
65459
  setScreenLocked(false);
65602
65460
  }
65603
65461
  }),
65462
+ masterKeyLocked && !screenLocked && // Master key got re-locked mid-session (e.g. external lock
65463
+ // command). Mount the decrypt splash — same shared keypad as
65464
+ // boot — and on success drop back to the agent picker. The
65465
+ // existing components below stay mounted underneath; once
65466
+ // unlocked they resume against the now-unlocked DB.
65467
+ /*#__PURE__*/ (0,react_jsx_runtime.jsx)(UnlockScreen.UnlockScreen, {
65468
+ onUnlock: ()=>{
65469
+ landOnAgentPicker();
65470
+ setMasterKeyLocked(false);
65471
+ }
65472
+ }),
65604
65473
  /*#__PURE__*/ (0,react_jsx_runtime.jsx)(NotificationStatus, {}),
65605
65474
  /*#__PURE__*/ (0,react_jsx_runtime.jsx)(Toaster, {}),
65606
65475
  /*#__PURE__*/ (0,react_jsx_runtime.jsx)(ServerStatus, {}),
@@ -68509,7 +68378,7 @@ function Home() {
68509
68378
  var __webpack_require__ = require("../webpack-runtime.js");
68510
68379
  __webpack_require__.C(exports);
68511
68380
  var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId))
68512
- var __webpack_exports__ = __webpack_require__.X(0, [4741,1050,2497,319,1918,7883,2082,4045,8167], () => (__webpack_exec__(21424)));
68381
+ var __webpack_exports__ = __webpack_require__.X(0, [4741,1050,2497,319,1918,9557,2082,4045,8167], () => (__webpack_exec__(21424)));
68513
68382
  module.exports = __webpack_exports__;
68514
68383
 
68515
68384
  })();