claude-code-session-manager 0.109.0 → 0.111.0

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 (60) hide show
  1. package/dist/assets/{AgentLibrary-Br4Ispr4.js → AgentLibrary-rBO1daqM.js} +1 -1
  2. package/dist/assets/{Button-C8Mc6MEP.js → Button-DsEYRZg0.js} +1 -1
  3. package/dist/assets/{DataModel-Bz0MypYO.js → DataModel-D8sbvvBC.js} +1 -1
  4. package/dist/assets/{History-DT26Iy38.js → History-BEHLtxia.js} +1 -1
  5. package/dist/assets/{Hooks-e4xUGEf-.js → Hooks-CKpvlXrg.js} +3 -3
  6. package/dist/assets/{Library-B3rzEhvN.js → Library-CeF4qh9d.js} +1 -1
  7. package/dist/assets/{ListDetail-BAvci_aP.js → ListDetail-DcfmxPFk.js} +1 -1
  8. package/dist/assets/{MarkdownEditor-DBWTGgRz.js → MarkdownEditor-CvK1pyto.js} +1 -1
  9. package/dist/assets/{McpServers-C-grBN21.js → McpServers-D-h2ihzJ.js} +2 -2
  10. package/dist/assets/{Memory-BmM0Fz-C.js → Memory-BnF89x8v.js} +1 -1
  11. package/dist/assets/{Panel-Dc6F8wb4.js → Panel-CQY5zano.js} +1 -1
  12. package/dist/assets/{Permissions-m_NnldFh.js → Permissions-DLukmmsz.js} +3 -3
  13. package/dist/assets/{Plugins-BB0TLUC9.js → Plugins-0Pf0OZ4l.js} +2 -2
  14. package/dist/assets/{ProvenanceBadge-k9PH0S2n.js → ProvenanceBadge-vC7Tj-JP.js} +1 -1
  15. package/dist/assets/RemoteMachines-CWnKbFHc.js +3 -0
  16. package/dist/assets/{SaveBar-DSMnbUVI.js → SaveBar-DjijiUb0.js} +1 -1
  17. package/dist/assets/{Scheduler-CbIcJEfM.js → Scheduler-Cm3EXZvE.js} +7 -7
  18. package/dist/assets/{ScopeSwitcher-tHtloWbN.js → ScopeSwitcher-B-P-kSfq.js} +1 -1
  19. package/dist/assets/Settings-DYDlp2m_.js +4 -0
  20. package/dist/assets/{SkillReferenceGraph-Dndl1tzr.js → SkillReferenceGraph-Epv2GJmj.js} +1 -1
  21. package/dist/assets/{Skills-CGnxroik.js → Skills-DHO0N0C2.js} +1 -1
  22. package/dist/assets/{SystemPrompt-B3A3rHNp.js → SystemPrompt-DxeNofL9.js} +1 -1
  23. package/dist/assets/{TagLibrary-k4CTiAYo.js → TagLibrary-QOAs7ISO.js} +1 -1
  24. package/dist/assets/{TiptapBody-C2LfNQXw.js → TiptapBody-DAMO3DhO.js} +1 -1
  25. package/dist/assets/{Toggle-CTXTU1q5.js → Toggle-Ba_f7q_m.js} +1 -1
  26. package/dist/assets/{ViewTabs-CfEfOSE1.js → ViewTabs-CGX6MAhc.js} +1 -1
  27. package/dist/assets/{historyTotals-Db0xy2Fg.js → historyTotals-vgfk8EOL.js} +1 -1
  28. package/dist/assets/index-CPieAFsl.js +3078 -0
  29. package/dist/assets/{index-C8Smiz7z.css → index-Dutcsly-.css} +1 -1
  30. package/dist/assets/{settingsSchema-xxO9p4yF.js → settingsSchema-D0FxNrTy.js} +1 -1
  31. package/dist/assets/{useAgentsDirWatch-DLS1_moj.js → useAgentsDirWatch-DCu6BNYN.js} +1 -1
  32. package/dist/assets/useRemotePeers-D4cdJDyh.js +1 -0
  33. package/dist/index.html +2 -2
  34. package/package.json +2 -1
  35. package/plugins/session-manager-dev/skills/develop/SKILL.md +3 -1
  36. package/plugins/session-manager-dev/skills/send-feedback/SKILL.md +48 -15
  37. package/scripts/scheduler-mcp-server.cjs +29 -2
  38. package/src/main/build-info.json +4 -4
  39. package/src/main/chatRunner.cjs +7 -0
  40. package/src/main/index.cjs +6 -0
  41. package/src/main/ipcSchemas.cjs +28 -0
  42. package/src/main/lib/autoUpdater.cjs +190 -0
  43. package/src/main/lib/builtinMacros.cjs +2 -2
  44. package/src/main/lib/crossProjectFeedback.cjs +143 -6
  45. package/src/main/lib/mcpToolCatalog.cjs +12 -3
  46. package/src/main/lib/reaperHelpers.cjs +13 -1
  47. package/src/main/lib/relayClientAuth.cjs +133 -0
  48. package/src/main/lib/remoteClientSessions.cjs +84 -0
  49. package/src/main/lib/shippedPersonaSeeds.cjs +11 -0
  50. package/src/main/lib/updaterSettings.cjs +64 -0
  51. package/src/main/remoteClient.cjs +42 -13
  52. package/src/main/remoteClientIpc.cjs +33 -1
  53. package/src/main/scheduler.cjs +16 -0
  54. package/src/main/updaterIpc.cjs +110 -0
  55. package/src/main/webRemote.cjs +276 -132
  56. package/src/preload/api.d.ts +55 -2
  57. package/src/preload/index.cjs +24 -1
  58. package/src/seed/agents/demo-video-builder.md +62 -19
  59. package/dist/assets/Settings-CW3w7knp.js +0 -6
  60. package/dist/assets/index-fJxuoRvi.js +0 -3077
@@ -1,6 +1,21 @@
1
1
  import type { PromptSession } from '../renderer/state/promptSessions';
2
2
 
3
3
  /** Mirrors the item shape returned by main/lib/prereqs.cjs checkPrereqs(). */
4
+ export type AutoUpdaterState =
5
+ | 'unsupported' | 'disabled' | 'idle' | 'checking'
6
+ | 'downloading' | 'ready' | 'deferred' | 'error';
7
+
8
+ export type AutoUpdaterStatus = {
9
+ state: AutoUpdaterState;
10
+ current: string;
11
+ latest: string | null;
12
+ progress: number | null;
13
+ error: string | null;
14
+ busyReason: string | null;
15
+ lastCheckedAt: number | null;
16
+ enabled: boolean;
17
+ };
18
+
4
19
  export interface PrereqItem {
5
20
  id: string;
6
21
  label: string;
@@ -1601,11 +1616,22 @@ export interface RemoteMachinePeer {
1601
1616
  pairedAt: string;
1602
1617
  clientPublicKey: string;
1603
1618
  verifiedDevicePubKey: string | null;
1619
+ /** process.platform of the remote machine, last known (null until first authenticated). */
1620
+ os: string | null;
1604
1621
  status: string;
1605
1622
  reason: string | null;
1606
1623
  sas: string | null;
1607
1624
  }
1608
1625
 
1626
+ export interface RemoteClientDevice {
1627
+ deviceId: string;
1628
+ email: string;
1629
+ issuedAt: string;
1630
+ expiresAt: string;
1631
+ isOnline: boolean;
1632
+ devicePubKey: string;
1633
+ }
1634
+
1609
1635
  export interface RemoteClientStatus {
1610
1636
  peerId: string;
1611
1637
  status: string;
@@ -1630,9 +1656,18 @@ export interface WebRemoteStatus {
1630
1656
  e2eState: 'idle' | 'pending_sas' | 'authenticated' | 'failed';
1631
1657
  /** 6-digit Short Authentication String pending user confirmation, or null. */
1632
1658
  pendingSas: string | null;
1659
+ /** Every connected client's E2E state (no key material). */
1660
+ clients: WebRemoteClientStatus[];
1633
1661
  devices: WebRemoteDevice[];
1634
1662
  }
1635
1663
 
1664
+ export interface WebRemoteClientStatus {
1665
+ key: string;
1666
+ clientId: string | null;
1667
+ state: 'idle' | 'pending_sas' | 'authenticated' | 'failed';
1668
+ pendingSas: string | null;
1669
+ }
1670
+
1636
1671
  export interface WebRemotePairResult {
1637
1672
  ok: boolean;
1638
1673
  deviceId?: string;
@@ -2144,10 +2179,23 @@ export interface SessionManagerAPI {
2144
2179
  connect: (peerId: string) => Promise<{ status: string; reason: string | null; sas: string | null }>;
2145
2180
  confirmSas: (peerId: string) => Promise<{ ok: boolean; reason?: string }>;
2146
2181
  remove: (peerId: string) => Promise<{ ok: boolean }>;
2182
+ authStatus: () => Promise<{ paired: boolean; clientId: string | null }>;
2183
+ pair: (code: string, clientName?: string) => Promise<{ ok: boolean; clientId?: string; error?: string }>;
2184
+ unpair: () => Promise<{ ok: boolean }>;
2185
+ devices: () => Promise<{ ok: boolean; devices?: RemoteClientDevice[]; error?: string }>;
2186
+ add: (deviceId: string, machineName: string) => Promise<RemoteMachinePeer>;
2147
2187
  send: (peerId: string, type: string, payload?: unknown) => Promise<unknown>;
2148
2188
  onStatus: (handler: (status: RemoteClientStatus) => void) => () => void;
2149
2189
  onEvent: (handler: (ev: RemoteClientEvent) => void) => () => void;
2150
2190
  };
2191
+ updater: {
2192
+ status: () => Promise<AutoUpdaterStatus>;
2193
+ check: () => Promise<AutoUpdaterStatus>;
2194
+ install: (force?: boolean) => Promise<{ ok: boolean; error?: string }>;
2195
+ cancelDeferred: () => Promise<AutoUpdaterStatus>;
2196
+ setEnabled: (enabled: boolean) => Promise<AutoUpdaterStatus>;
2197
+ onStatus: (handler: (status: AutoUpdaterStatus) => void) => () => void;
2198
+ };
2151
2199
  webRemote: {
2152
2200
  getStatus: () => Promise<WebRemoteStatus>;
2153
2201
  enable: () => Promise<WebRemoteMutationResult>;
@@ -2166,8 +2214,9 @@ export interface SessionManagerAPI {
2166
2214
  /** Subscribe to the completion event broadcast after revokeAll. */
2167
2215
  onRevokedAll: (handler: (ev: { revokedCount: number }) => void) => () => void;
2168
2216
  /** Confirm that the SAS shown on the desktop matches the browser — marks the
2169
- * E2E session as authenticated and unblocks MUTATE-tier commands. */
2170
- confirmSas: () => Promise<WebRemoteMutationResult>;
2217
+ * E2E session as authenticated and unblocks MUTATE-tier commands. Pass the
2218
+ * client's `key` when several clients are pending (else `ambiguous_pending_sas`). */
2219
+ confirmSas: (clientKey?: string) => Promise<WebRemoteMutationResult>;
2171
2220
  };
2172
2221
  chat: {
2173
2222
  /** Spawn a headless `claude -p` run for a tab. Results arrive via the on* listeners. */
@@ -2187,6 +2236,10 @@ export interface SessionManagerAPI {
2187
2236
  * tool) pushes a prompt into an open tab's queue from outside the
2188
2237
  * renderer (PRD 753). */
2189
2238
  onExternalSend: (handler: (e: ChatExternalSendEvent) => void) => () => void;
2239
+ /** Fires when a paired phone asks main to start a new Session (payload pre-validated in main). */
2240
+ onExternalNewSession: (
2241
+ handler: (e: { cwd: string; agentName: string; tag: AgentPersonaTag; title: string; prompt: string }) => void,
2242
+ ) => () => void;
2190
2243
  /** Classify a queued PromptTicket's text as 'inline' (run through
2191
2244
  * chatRunner) or 'develop' (dispatch to /develop for PRD decomposition).
2192
2245
  * A single bounded `claude -p` call, never a scheduler job. */
@@ -405,6 +405,11 @@ contextBridge.exposeInMainWorld('api', {
405
405
  /** Confirm the short authentication string matches the remote machine. */
406
406
  confirmSas: (peerId) => ipcRenderer.invoke('remoteClient:confirm-sas', { peerId }),
407
407
  remove: (peerId) => ipcRenderer.invoke('remoteClient:remove', { peerId }),
408
+ authStatus: () => ipcRenderer.invoke('remoteClient:auth-status'),
409
+ pair: (code, clientName) => ipcRenderer.invoke('remoteClient:pair', { code, clientName }),
410
+ unpair: () => ipcRenderer.invoke('remoteClient:unpair'),
411
+ devices: () => ipcRenderer.invoke('remoteClient:devices'),
412
+ add: (deviceId, machineName) => ipcRenderer.invoke('remoteClient:add', { deviceId, machineName }),
408
413
  send: (peerId, type, payload) => ipcRenderer.invoke('remoteClient:send', { peerId, type, payload }),
409
414
  onStatus: (handler) => {
410
415
  const listener = (_e, payload) => handler(payload);
@@ -417,6 +422,18 @@ contextBridge.exposeInMainWorld('api', {
417
422
  return () => ipcRenderer.removeListener('remoteClient:event', listener);
418
423
  },
419
424
  },
425
+ updater: {
426
+ status: () => ipcRenderer.invoke('updater:status'),
427
+ check: () => ipcRenderer.invoke('updater:check'),
428
+ install: (force) => ipcRenderer.invoke('updater:install', { force: force === true }),
429
+ cancelDeferred: () => ipcRenderer.invoke('updater:cancel-deferred'),
430
+ setEnabled: (enabled) => ipcRenderer.invoke('updater:set-enabled', { enabled }),
431
+ onStatus: (handler) => {
432
+ const listener = (_e, payload) => handler(payload);
433
+ ipcRenderer.on('updater:status', listener);
434
+ return () => ipcRenderer.removeListener('updater:status', listener);
435
+ },
436
+ },
420
437
  webRemote: {
421
438
  /** Current connection state (no token values). */
422
439
  getStatus: () => ipcRenderer.invoke('webRemote:get-status'),
@@ -448,7 +465,7 @@ contextBridge.exposeInMainWorld('api', {
448
465
  },
449
466
  /** Revoke ALL paired devices and tear down every session immediately. */
450
467
  revokeAll: () => ipcRenderer.invoke('webRemote:revoke-all'),
451
- confirmSas: () => ipcRenderer.invoke('webRemote:confirm-sas'),
468
+ confirmSas: (clientKey) => ipcRenderer.invoke('webRemote:confirm-sas', clientKey ? { clientKey } : undefined),
452
469
  /** Push event when revokeAll completes (main broadcasts webRemote:revoked-all). */
453
470
  onRevokedAll: (handler) => {
454
471
  const listener = (_e, payload) => handler(payload);
@@ -509,6 +526,12 @@ contextBridge.exposeInMainWorld('api', {
509
526
  ipcRenderer.on('chat:external-send', listener);
510
527
  return () => ipcRenderer.removeListener('chat:external-send', listener);
511
528
  },
529
+ /** Fires when a paired phone asks main to start a new Session. */
530
+ onExternalNewSession: (handler) => {
531
+ const listener = (_e, payload) => handler(payload);
532
+ ipcRenderer.on('chat:external-new-session', listener);
533
+ return () => ipcRenderer.removeListener('chat:external-new-session', listener);
534
+ },
512
535
  /** Classify a queued PromptTicket's text as 'inline' or 'develop'. */
513
536
  classifyTicket: (payload) => ipcRenderer.invoke('chat:classify-ticket', payload),
514
537
  /** Author a mechanical draft PRD from a queued PromptTicket classified 'develop'. */
@@ -3,9 +3,9 @@ name: demo-video-builder
3
3
  description: 30-second self-contained HTML/JS demo video for Project Home, saved via project_demo_video_write.
4
4
  tools: Read, Grep, Glob, Bash, Write, Edit
5
5
  title: 'Project Home — Demo Video'
6
- model: sonnet
7
- effort: medium
8
- seedVersion: 3
6
+ model: opus
7
+ effort: max
8
+ seedVersion: 4
9
9
  ---
10
10
 
11
11
  You are the demo-video-builder. You make a 30-second animated demo that shows someone what this
@@ -28,17 +28,59 @@ testimonials, or UI screenshots. Where the project already describes itself well
28
28
  feature list, a mission statement), reuse its own wording instead of paraphrasing.
29
29
  </grounding_rules>
30
30
 
31
+ <script_first>
32
+ Write the narration script before any visual work: ~70-80 spoken words for 30 s (~150 wpm), beats =
33
+ hook (a question or bold claim, 0-4 s), problem, solution, three features, call to action with the
34
+ real get-started command. Every line traces to the claims list. Then storyboard one scene per line,
35
+ sized to that line's spoken length; visuals illustrate what the line says at that moment (the noun
36
+ on screen when it is spoken). Good script first, visuals second — a strong script with simple
37
+ visuals beats flashy visuals over a weak script.
38
+ </script_first>
39
+
40
+ <character>
41
+ Design one ORIGINAL mascot from the project's theme/purpose (e.g. a terminal-shaped robot for a CLI
42
+ tool, a ledger-owl for a finance app) — never a trademarked or existing character. Build it only
43
+ from inline SVG/CSS shapes, give it a name and 2-3 expressions, and a deterministic idle loop
44
+ (blink, bob, breathe) computed from t. It appears in most scenes and reacts to each beat (points at
45
+ the feature, celebrates at the CTA). Cute = rounded shapes, big eyes, soft palette drawn from the
46
+ project's own brand colors when it has any.
47
+ </character>
48
+
49
+ <styles>
50
+ Four styles; follow `Video style: <name>` from additional instructions, otherwise choose by the
51
+ project's audience (developer/enterprise → Professional, consumer/playful → Fun,
52
+ conceptual/educational → Animated whiteboard, text-heavy/CLI → Kinetic typography) and name the
53
+ choice in the report.
54
+ - Professional — clean 2D motion graphics, restrained palette, grid layout, smooth ease-in-out,
55
+ character small and polished.
56
+ - Fun — character-led cartoon, saturated palette, squash-and-stretch, bouncy overshoot easing,
57
+ playful sound-matched pops.
58
+ - Animated whiteboard — white background, black/one-accent strokes drawn on via SVG
59
+ stroke-dashoffset, a hand/marker cursor following the stroke, character sketched in line art.
60
+ - Kinetic typography — the script itself is the visual: words animate in sync with narration timing
61
+ (scale, slide, rotate), the character punctuates key words.
62
+ Whatever the style, readability first: captions ≤12 words, high contrast.
63
+ </styles>
64
+
65
+ <dynamic_elements>
66
+ Include at least three per video: overshoot/elastic easing, particle bursts or confetti, morphing
67
+ shapes, camera pan/zoom across the 1280x720 stage, parallax layers, animated counters/diagrams
68
+ built from real facts. Every element is a pure function of t; any randomness comes from a seeded
69
+ PRNG (e.g. mulberry32 with a fixed seed) so `seek(t)` stays exact and frames are deterministic.
70
+ </dynamic_elements>
71
+
31
72
  <storyboard>
32
- Before writing any code, write a 5-7 scene storyboard with timings that sum to exactly 30
33
- seconds:
73
+ Before writing any code, turn the script into a 5-8 scene storyboard, one scene per script line,
74
+ with timings that sum to exactly 30 seconds:
34
75
 
35
- 1. Title card — the project's name and a one-line purpose.
76
+ 1. Title card — the project's name and a one-line purpose (the hook).
36
77
  2. The problem it solves.
37
78
  3-6. One scene per key feature (3 to 4 features), each a short caption plus a simple animated
38
79
  diagram or kinetic type built from shapes, SVG, or canvas — never a fake screenshot.
39
80
  7. Closing card — the project's name and how to get started (the real command).
40
81
 
41
- Keep every caption at 12 words or fewer, and hold each scene on screen long enough to read twice.
82
+ The character appears in most scenes per `<character>`; the style follows `<styles>`. Keep every
83
+ caption at 12 words or fewer, and hold each scene on screen long enough to read twice.
42
84
  </storyboard>
43
85
 
44
86
  <output_contract>
@@ -117,25 +159,26 @@ open('demo.html', 'w').write(html)
117
159
  - Never write the literal pattern `function (` or `function(` in the inline `<script>` —
118
160
  `project_demo_video_write`'s safety scanner blocks anything matching `/\bFunction\s*\(/i`.
119
161
  Use arrow functions (`() => { ... }`) throughout.
120
- - Save the built file by path (see `<process>` step 6). Never retype the base64 audio into a tool
121
- argument — a ~300 KB payload retyped by the model is exactly how a run once shipped silent.
122
- </audio_policy>
123
-
124
- <process>
162
+ - Save the built file by path (see `<process>
125
163
  1. Read, with a bounded pass: the manifest, the README or top-level docs index, the top-level
126
164
  directory tree, and `git log --oneline -30` for a sense of recent direction. Stop once you
127
165
  have enough to describe the project honestly.
128
166
  2. Write the claims list: for every goal or feature you plan to show, note the source you read
129
167
  it from. Drop anything without a source.
130
- 3. Write the storyboard per `<storyboard>`, with scene timings summing to exactly 30 seconds.
131
- 4. Build the HTML per `<output_contract>` and the narration track per `<audio_policy>`, ending
132
- with the final document written to a file (`demo.html`) that contains the `<audio>` tag.
133
- 5. Self-check before saving: scene durations sum to 30, every caption traces to a claim in your
134
- list, and the document contains none of the forbidden network APIs.
135
- 6. Call `project_demo_video_write` exactly once with `{ htmlPath }` — the absolute path of the
168
+ 3. Write the narration script per `<script_first>`.
169
+ 4. Choose the style per `<styles>` and design the mascot per `<character>`.
170
+ 5. Write the storyboard per `<storyboard>`, with scene timings summing to exactly 30 seconds.
171
+ 6. Build the HTML per `<output_contract>` (with `<dynamic_elements>`) and the narration track per
172
+ `<audio_policy>`, ending with the final document written to a file (`demo.html`) that contains
173
+ the `<audio>` tag.
174
+ 7. Self-check before saving: scene durations sum to 30, every caption traces to a claim in your
175
+ list, the character appears, at least 3 dynamic elements are present, the style matches the
176
+ choice, and the document contains none of the forbidden network APIs.
177
+ 8. Call `project_demo_video_write` exactly once with `{ htmlPath }` — the absolute path of the
136
178
  built file. Never pass the document in `html` (retyping base64 is how narration got lost). It
137
179
  validates the document and writes it as the project's Demo Video.
138
- 7. Report back the storyboard as one short list — scene, timing, and the claim behind it.
180
+ 9. Report back the style, the character, and the scenes as one short list — scene, timing, and the
181
+ claim behind it.
139
182
  </process>
140
183
 
141
184
  <when_things_fail>
@@ -1,6 +0,0 @@
1
- import{aD as l,an as e,S as P,A as H,n as K,a2 as Re,aU as ue,bb as Z,bi as Ie,W as Ae,D as ne,_ as Te,a as L,a0 as Pe,b6 as Oe,b3 as De,aY as $e,c as ae,aB as Me,b0 as Le,ba as re,b2 as Fe,aW as q,ap as _e,au as Be,J as He,H as ie,aL as Ue}from"./index-fJxuoRvi.js";import{P as ze}from"./Panel-Dc6F8wb4.js";import{S as Ge}from"./ScopeSwitcher-tHtloWbN.js";import{S as pe}from"./SaveBar-DSMnbUVI.js";import{u as Je,s as oe,E as Ke}from"./settingsSchema-xxO9p4yF.js";import{V as We}from"./ViewTabs-CfEfOSE1.js";import{T as Y}from"./Toggle-CTXTU1q5.js";import"./monaco-editor-BsIlgG7c.js";import"./claude-settings-schema-COigsajx.js";const Ve=[{id:"essentials",title:"Essentials",summary:"The knobs you reach for first.",keys:["model","language","outputStyle","permissions"]},{id:"git",title:"Git & Commits",summary:"How Claude writes commits and pull requests on your behalf.",keys:["attribution","includeGitInstructions","includeCoAuthoredBy"]},{id:"memory",title:"Memory & Plans",summary:"Context Claude keeps between sessions and where it files plans.",keys:["autoMemoryEnabled","plansDirectory","claudeMdExcludes","respectGitignore"]},{id:"model",title:"Model & Reasoning",summary:"Model allow/override lists, reasoning depth, and fast-mode toggles — machine-wide, with no per-Epic override yet. An Epic's Agent persona model (Agent Library) still wins over Essentials' plain model default.",advanced:!0,keys:["availableModels","modelOverrides","effortLevel","fastMode","fastModePerSessionOptIn","alwaysThinkingEnabled","fallbackModel","enforceAvailableModels","teammateDefaultModel","advisorModel"]},{id:"interface",title:"Interface",summary:"Visual polish — spinners, progress bars, motion.",keys:["spinnerTipsEnabled","spinnerTipsOverride","spinnerVerbs","terminalProgressBarEnabled","showTurnDuration","prefersReducedMotion","companyAnnouncements","statusLine","teammateMode"]},{id:"updates",title:"Updates & Cleanup",summary:"Release channel and how long transcripts stick around.",keys:["autoUpdatesChannel","cleanupPeriodDays"]},{id:"env",title:"Environment & Auth",summary:"Environment variables and auth-helper scripts.",keys:["env","apiKeyHelper","awsCredentialExport","awsAuthRefresh"]},{id:"hooks",title:"Hooks",summary:"Scripts Claude runs before/after tool use.",keys:["hooks","disableAllHooks","allowedHttpHookUrls","httpHookAllowedEnvVars"]},{id:"mcp",title:"MCP Servers",summary:"External tool providers Claude can talk to.",keys:["enableAllProjectMcpServers","enabledMcpjsonServers","disabledMcpjsonServers"]},{id:"plugins",title:"Plugins",summary:"Installed plugins, marketplaces, and per-plugin configuration.",keys:["enabledPlugins","extraKnownMarketplaces","skippedPlugins","skippedMarketplaces","pluginConfigs"]},{id:"filesystem",title:"File Picker & Sandbox",summary:"How Claude browses files and the sandbox it runs in.",keys:["fileSuggestion","sandbox","worktree"]},{id:"login",title:"Login",summary:"Which login method to force on startup.",keys:["forceLoginMethod","forceLoginOrgUUID"]},{id:"feedback",title:"Feedback",summary:"Session-quality surveys.",keys:["feedbackSurveyRate"]},{id:"advanced",title:"Advanced / Enterprise",summary:"Managed-settings-only keys and enterprise allow/denylists.",advanced:!0,keys:["allowedMcpServers","deniedMcpServers","allowManagedHooksOnly","allowManagedPermissionRulesOnly","allowManagedMcpServersOnly","strictKnownMarketplaces","blockedMarketplaces","pluginTrustMessage","otelHeadersHelper","skipWebFetchPreflight"]}],qe={mcp:"MCP",url:"URL",urls:"URLs",http:"HTTP",ui:"UI",id:"ID",uuid:"UUID",api:"API",aws:"AWS",otel:"OTel",json:"JSON"};function fe(t){return t.replace(/^\$/,"").replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").toLowerCase().split(/\s+/).filter(Boolean).map((o,r)=>{const a=qe[o];return a||(r===0?o.charAt(0).toUpperCase()+o.slice(1):o)}).join(" ")}function he(t){const s=new Set,n=[];for(const r of Ve){const a=r.keys.filter(c=>t.has(c));a.forEach(c=>s.add(c)),a.length!==0&&n.push({id:r.id,title:r.title,summary:r.summary,advanced:!!r.advanced,keys:a})}const o=Array.from(t).filter(r=>!s.has(r)&&r!=="$schema").sort();return o.length>0&&n.push({id:"other",title:"Other",summary:"Keys not yet grouped.",advanced:!1,keys:o}),n}function Xe(t,s){return he(t).map(n=>({id:n.id,title:n.title,advanced:n.advanced,total:n.keys.length,setCount:n.keys.filter(o=>s.has(o)).length}))}const Ye={};function ge(t,s){if(s)return{text:s,authored:!1};const n=Ye[t];return n?{text:n,authored:!0}:null}const Ze={essentials:"star",git:"link",memory:"memory",model:"sparkle",interface:"eye",updates:"reload",env:"keys",hooks:"hooks",mcp:"mcp",plugins:"plugins",filesystem:"folder",login:"lock",feedback:"send",advanced:"shield",other:"dot"},Qe=["user","project","local","default"],et={user:"user",project:"project",local:"local",default:"default"};function tt({node:t,targetScope:s,onOverride:n,schema:o}){const[r,a]=l.useState(""),[c,p]=l.useState(!1),[b,j]=l.useState(!0),i=t.kind==="object"?t:null,u=i?Object.keys(i.children):[],g=o.rootKeys(),y=new Set([...u,...g]),f=l.useMemo(()=>he(y),[Array.from(y).sort().join(",")]),w=l.useMemo(()=>Xe(y,new Set(u)),[Array.from(y).sort().join(","),u.join(",")]),[R,S]=l.useState(null),m=l.useRef(null),N=h=>{const k=f.find(E=>E.id===h);k!=null&&k.advanced&&!c&&p(!0),S(h)};l.useEffect(()=>{if(!R)return;S(null);const h=m.current,k=document.getElementById(`settings-group-${R}`);if(!h||!k)return;const E=k.getBoundingClientRect().top-h.getBoundingClientRect().top;h.scrollBy({top:E,behavior:"smooth"})},[R]);const I=l.useMemo(()=>f.filter(h=>c||!h.advanced).map(h=>({...h,keys:h.keys.filter(k=>{const E=(i==null?void 0:i.children[k])!==void 0;if(!b&&!E)return!1;if(!r.trim())return!0;const v=o.at([k]);return[k,fe(k),(v==null?void 0:v.description)??""].join(" ").toLowerCase().includes(r.toLowerCase())})})).filter(h=>h.keys.length>0),[f,r,c,b,i,o]);return e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{className:"shrink-0 border-b border-line bg-bg-elev px-3 py-1.5 flex items-center gap-3 text-xs",children:[e.jsxs("span",{className:"text-fg-faint",children:["writing overrides to"," ",e.jsx("span",{className:"text-fg-dim font-medium",children:P[s].label})]}),e.jsxs("label",{className:"flex items-center gap-1 text-fg-faint cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:b,onChange:h=>j(h.target.checked),className:"accent-accent"}),"show not-yet-configured"]}),e.jsxs("label",{className:"flex items-center gap-1 text-fg-faint cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:c,onChange:h=>p(h.target.checked),className:"accent-accent"}),"show advanced"]}),e.jsx("div",{className:"flex-1"}),e.jsx("input",{value:r,onChange:h=>a(h.target.value),placeholder:"search settings…",className:"bg-bg border border-line rounded px-2 py-1 text-xs text-fg w-64"})]}),e.jsxs("div",{className:"flex-1 flex overflow-hidden",children:[e.jsx(st,{groups:w,onJump:N}),e.jsxs("div",{ref:m,className:"flex-1 overflow-auto py-3",children:[I.map(h=>e.jsx(nt,{id:h.id,title:h.title,summary:h.summary,advanced:h.advanced,children:h.keys.map(k=>{const E=i==null?void 0:i.children[k],v=o.at([k]);return e.jsx(at,{keyName:k,node:E,info:v,targetScope:s,onOverride:(d,C)=>n([k],d,C)},k)})},h.id)),I.length===0&&e.jsxs("div",{className:"px-6 py-12 text-center text-fg-faint text-sm",children:['no settings match "',r,'"']})]})]})]})}function st({groups:t,onJump:s}){return e.jsx("nav",{className:"shrink-0 w-48 border-r border-line bg-bg-elev/30 overflow-y-auto py-3",children:t.map(n=>e.jsxs("button",{onClick:()=>s(n.id),className:"w-full flex items-center gap-2 px-3 py-2 text-left hover:bg-bg-hi transition-colors",title:`jump to ${n.title}`,children:[e.jsx(H,{name:Ze[n.id]??"dot",size:15,className:"text-fg-faint shrink-0"}),e.jsx("span",{className:"flex-1 min-w-0 truncate text-xs text-fg-dim",children:n.title}),e.jsxs("span",{className:"text-[10px] text-fg-faint font-mono shrink-0",children:[n.setCount,"/",n.total]})]},n.id))})}function nt({id:t,title:s,summary:n,advanced:o,children:r}){const[a,c]=l.useState(!0);return e.jsxs("section",{id:`settings-group-${t}`,className:"mx-4 mb-4 rounded-lg border border-line shadow-sm bg-bg-elev/50 overflow-hidden",children:[e.jsxs("header",{className:"px-4 py-3 bg-bg-elev cursor-pointer hover:bg-bg-hi flex items-center gap-3 border-b border-line",onClick:()=>c(!a),children:[e.jsx("span",{className:"text-fg-faint w-3",children:a?"▾":"▸"}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-fg text-base font-semibold",children:s}),o&&e.jsx("span",{className:"text-[9px] px-1.5 py-0 rounded border border-yellow-900/40 bg-yellow-950/30 text-yellow-500/80 uppercase tracking-wide",children:"advanced"})]}),e.jsx("p",{className:"text-fg-dim text-sm mt-0.5",children:n})]})]}),a&&e.jsx("div",{className:"divide-y divide-line/60",children:r})]})}function at({keyName:t,node:s,info:n,targetScope:o,onOverride:r}){const[a,c]=l.useState(!1),[p,b]=l.useState(!1),j=fe(t),i=s!==void 0,g=(s==null?void 0:s.kind)==="leaf"?s:null,y=(s==null?void 0:s.kind)==="object"||(s==null?void 0:s.kind)==="array",f=n==null?void 0:n.default,w=g&&f!==void 0&&ye(g.value,f),R=mt({isSet:i,leafNode:g,defaultVal:f,isDefault:!!w,node:s}),S=rt(g,f),m=S.length>=2,N=!!g&&!w,I=ge(t,n==null?void 0:n.description);return e.jsx("div",{className:"px-4 py-3 hover:bg-bg-elev/20",children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("button",{onClick:()=>c(!a),className:"text-fg-faint hover:text-fg w-3 mt-0.5",title:a?"collapse":"expand",children:a?"▾":"▸"}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-baseline gap-2 flex-wrap",children:[e.jsx("h3",{className:"text-fg text-sm",children:j}),e.jsx(ut,{kind:R.kind,label:R.label}),m&&e.jsxs("button",{onClick:h=>{h.stopPropagation(),a||c(!0),b(k=>!k)},className:"text-[9px] px-1.5 py-0 rounded border border-blue-900/40 bg-blue-950/30 text-blue-300/90 uppercase tracking-wide hover:bg-blue-900/40",title:"this key is defined in multiple scopes — click to expand",children:["drift · ",S.map(h=>et[h]).join(" · ")]}),N&&g&&e.jsx("button",{onClick:h=>{h.stopPropagation(),r(void 0,g.winner)},className:"text-[9px] px-1.5 py-0 rounded border border-line bg-bg-elev text-fg-faint uppercase tracking-wide hover:text-fg hover:bg-bg-hi",title:`reset · delete from ${P[g.winner].label}`,children:"↺ reset"}),(n==null?void 0:n.deprecated)&&e.jsx("span",{className:"text-[9px] px-1 py-0 rounded border border-yellow-900/40 bg-yellow-950/30 text-yellow-500/80 uppercase tracking-wide",children:"deprecated"}),(n==null?void 0:n.required)&&!i&&e.jsx("span",{className:"text-[9px] px-1 py-0 rounded border border-red-900/40 bg-red-950/30 text-red-400/80 uppercase tracking-wide",children:"required"})]}),I&&e.jsx("p",{className:`text-fg-dim text-xs mt-0.5 leading-relaxed ${I.authored?"inline-block border-b border-dotted border-fg-faint/70 cursor-help":""}`,title:I.authored?"In-app-authored description — not from the official Claude Code schema":void 0,children:pt(I.text)}),a&&p&&g&&e.jsx(it,{leaf:g,defaultVal:f,onReset:h=>r(void 0,h)}),a&&e.jsx(ot,{keyName:t,node:s,info:n,targetScope:o,onOverride:h=>r(h)})]}),e.jsx("div",{className:"shrink-0",children:!a&&e.jsx(xt,{node:s,info:n,onSet:h=>{r(h)},disabled:y&&i})})]})})}function rt(t,s){if(!t)return[];const n=new Set;n.add(t.winner);for(const o of t.shadowed)n.add(o.scope);return s!==void 0&&n.add("default"),Qe.filter(o=>n.has(o))}function it({leaf:t,defaultVal:s,onReset:n}){const o={};o[t.winner]={value:t.value,winner:!0};for(const r of t.shadowed)o[r.scope]={value:r.value,winner:!1};return e.jsxs("div",{className:"mt-2 border border-line rounded bg-bg-elev/40 text-xs",children:[e.jsx("div",{className:"px-2 py-1 text-fg-faint border-b border-line/60",children:"Values across all scopes (winner highlighted · reset removes that scope's value):"}),e.jsxs("div",{className:"divide-y divide-line/60",children:[["user","project","local"].map(r=>{const a=o[r],c=a!==void 0;return e.jsxs("div",{className:"grid grid-cols-[5rem_1fr_auto] gap-2 px-2 py-1 items-center",children:[e.jsx("span",{className:"text-fg-faint",children:P[r].label}),e.jsx("span",{className:`font-mono truncate ${a!=null&&a.winner?"text-accent":"text-fg-dim"}`,children:c?B(a.value):e.jsx("span",{className:"text-fg-faint",children:"—"})}),c?e.jsx("button",{onClick:()=>n(r),className:"text-[9px] px-1.5 py-0 rounded border border-line text-fg-faint uppercase tracking-wide hover:text-fg hover:bg-bg-hi",title:`delete this key from ${P[r].label}`,children:"↺"}):e.jsx("span",{})]},r)}),e.jsxs("div",{className:"grid grid-cols-[5rem_1fr_auto] gap-2 px-2 py-1 items-center",children:[e.jsx("span",{className:"text-fg-faint",children:"Default"}),e.jsx("span",{className:"font-mono text-fg-dim truncate",children:s!==void 0?B(s):e.jsx("span",{className:"text-fg-faint",children:"—"})}),e.jsx("span",{})]})]})]})}function ot({keyName:t,node:s,info:n,targetScope:o,onOverride:r}){const c=(s==null?void 0:s.kind)==="leaf"?s:null,p=ge(t,n==null?void 0:n.description);return e.jsxs("div",{className:"mt-3 space-y-2 text-xs",children:[p&&e.jsx("p",{className:`text-fg-dim leading-relaxed whitespace-pre-wrap ${p.authored?"inline-block border-b border-dotted border-fg-faint/70 cursor-help":""}`,title:p.authored?"In-app-authored description — not from the official Claude Code schema":void 0,children:p.text}),e.jsxs("dl",{className:"grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-fg-faint",children:[(n==null?void 0:n.type)&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:"Type"}),e.jsx("dd",{className:"text-fg-dim font-mono",children:Array.isArray(n.type)?n.type.join(" | "):n.type})]}),(n==null?void 0:n.default)!==void 0&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:"Default"}),e.jsx("dd",{className:"text-fg-dim font-mono",children:B(n.default)})]}),(n==null?void 0:n.enum)&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:"Options"}),e.jsx("dd",{className:"text-fg-dim font-mono",children:n.enum.map(b=>String(b)).join(" | ")})]}),c&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:"Current"}),e.jsxs("dd",{className:"font-mono",children:[e.jsx("span",{className:"text-fg-dim",children:B(c.value)}),e.jsxs("span",{className:"ml-2 text-fg-faint",children:["from ",P[c.winner].label]})]})]}),c&&c.shadowed.length>0&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:"Shadowed"}),e.jsx("dd",{className:"space-y-0.5",children:c.shadowed.map((b,j)=>e.jsxs("div",{className:"font-mono text-fg-faint line-through",children:[B(b.value)," —"," ",e.jsx("span",{className:"no-underline",children:P[b.scope].label})]},j))})]}),(s==null?void 0:s.kind)==="array"&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{children:"Accumulated"}),e.jsxs("dd",{className:"text-fg-dim",children:[s.items.length," items across all scopes"]})]})]}),e.jsx("div",{className:"pt-2 flex items-center gap-2 flex-wrap border-t border-line/60",children:e.jsx(lt,{keyName:t,node:s,info:n,onChange:r,targetScope:o})})]})}function lt({node:t,info:s,onChange:n,targetScope:o}){const a=(t==null?void 0:t.kind)==="leaf"?t.value:s==null?void 0:s.default,c=be(s==null?void 0:s.type);if(c==="boolean"){const p=a===!0;return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-fg-faint",children:"Set to:"}),e.jsx("div",{className:"inline-flex rounded border border-line overflow-hidden",children:[{v:!0,label:"on"},{v:!1,label:"off"}].map(({v:b,label:j})=>e.jsx("button",{onClick:()=>n(b),className:`px-3 py-0.5 text-xs transition-colors ${p===b?"bg-accent text-bg":"bg-bg-elev text-fg-dim hover:bg-bg-hi hover:text-fg"}`,children:j},j))}),e.jsxs("span",{className:"text-fg-faint",children:["→ ",P[o].label]})]})}return s!=null&&s.enum?e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx("span",{className:"text-fg-faint",children:"Choose:"}),s.enum.map(p=>{const b=ye(a,p);return e.jsx("button",{onClick:()=>n(p),className:`px-2 py-0.5 text-xs rounded border ${b?"bg-accent text-bg border-accent":"bg-bg-elev text-fg-dim border-line hover:text-fg hover:bg-bg-hi"}`,children:String(p)},String(p))}),e.jsxs("span",{className:"text-fg-faint",children:["→ ",P[o].label]})]}):c==="integer"||c==="number"?e.jsx(ct,{value:typeof a=="number"?a:void 0,integer:c==="integer",onCommit:n,target:P[o].label}):c==="string"?e.jsx(dt,{value:typeof a=="string"?a:"",onCommit:n,target:P[o].label}):e.jsxs("div",{className:"text-fg-faint",children:["Complex value (",c??"object/array",") — use the"," ",e.jsx("span",{className:"text-fg-dim",children:"Raw"})," tab to edit."]})}function ct({value:t,integer:s,onCommit:n,target:o}){const[r,a]=l.useState(t!==void 0?String(t):""),c=()=>{const p=s?parseInt(r,10):parseFloat(r);Number.isNaN(p)||n(p)};return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-fg-faint",children:"Set to:"}),e.jsx("input",{type:"number",value:r,onChange:p=>a(p.target.value),onBlur:c,onKeyDown:p=>{p.key==="Enter"&&p.target.blur()},className:"w-24 bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg"}),e.jsxs("button",{onClick:c,className:"px-2 py-0.5 text-xs border border-accent/50 rounded text-accent hover:bg-accent hover:text-bg",children:["save → ",o]})]})}function dt({value:t,onCommit:s,target:n}){const[o,r]=l.useState(t);return e.jsxs("div",{className:"flex items-center gap-2 flex-1 min-w-0",children:[e.jsx("span",{className:"text-fg-faint shrink-0",children:"Set to:"}),e.jsx("input",{type:"text",value:o,onChange:a=>r(a.target.value),onKeyDown:a=>{a.key==="Enter"&&s(o)},className:"flex-1 min-w-0 bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg"}),e.jsxs("button",{onClick:()=>s(o),className:"shrink-0 px-2 py-0.5 text-xs border border-accent/50 rounded text-accent hover:bg-accent hover:text-bg",children:["save → ",n]})]})}function xt({node:t,info:s,onSet:n,disabled:o}){const r=(t==null?void 0:t.kind)==="leaf",a=be(s==null?void 0:s.type);if(o)return null;if(a==="boolean"){const c=r&&t.value===!0;return e.jsx("button",{onClick:()=>n(!c),className:`px-2.5 py-0.5 text-xs rounded border transition-colors ${c?"bg-accent/15 text-accent border-accent/40 hover:bg-accent hover:text-bg":"bg-bg-elev text-fg-dim border-line hover:text-fg hover:bg-bg-hi"}`,title:"toggle",children:c?"on":"off"})}return null}function mt({isSet:t,leafNode:s,defaultVal:n,isDefault:o,node:r}){return t?(r==null?void 0:r.kind)==="array"?{kind:"accumulated",label:`${r.items.length} items`}:(r==null?void 0:r.kind)==="object"?{kind:"set",label:"configured"}:s?o?{kind:"default",label:`= default · ${P[s.winner].label}`}:s.shadowed.length>0?{kind:"override",label:`overriding ${s.shadowed.length} scope(s)`}:{kind:"set",label:`set in ${P[s.winner].label}`}:{kind:"set",label:"configured"}:n!==void 0?{kind:"default",label:`using default: ${B(n)}`}:{kind:"unset",label:"not configured"}}function ut({kind:t,label:s}){const n={set:"bg-accent/15 text-accent border-accent/30",default:"bg-bg-hi text-fg-dim border-line",unset:"bg-bg-elev text-fg-faint border-line",override:"bg-purple-950/30 text-purple-400/80 border-purple-900/40",accumulated:"bg-blue-950/30 text-blue-400/80 border-blue-900/40"}[t];return e.jsx("span",{className:`text-[9px] px-1.5 py-0 rounded border uppercase tracking-wide ${n}`,children:s})}function be(t){if(t)return Array.isArray(t)?t.find(n=>n!=="null")??t[0]:t}function ye(t,s){return t===s?!0:typeof t!=typeof s?!1:t&&s&&typeof t=="object"?JSON.stringify(t)===JSON.stringify(s):!1}function pt(t){const s=t.trim().split(`
2
- `)[0],n=s.match(/^([^.!?]+[.!?])/);return n?n[1]:s.length>120?s.slice(0,120)+"…":s}function B(t){if(typeof t=="string")return t===""?'""':JSON.stringify(t);if(t===null)return"null";if(t===void 0)return"—";if(typeof t=="object"){const s=JSON.stringify(t);return s.length>60?s.slice(0,60)+"…":s}return String(t)}const ft=1440*60*1e3;function le(t){if(!t)return"never";const s=new Date(t).getTime();return Number.isNaN(s)?"never":new Date(s).toLocaleString()}function ce(t){return t?new Date(t).toLocaleString():"never"}function ht(){return e.jsxs("div",{className:"p-4 max-w-3xl space-y-8 text-sm",children:[e.jsx(gt,{}),e.jsx("hr",{className:"border-line"}),e.jsx(bt,{})]})}function gt(){const[t,s]=l.useState(null),[n,o]=l.useState(null),[r,a]=l.useState(null),[c,p]=l.useState(null),[b,j]=l.useState(!1),[i,u]=l.useState(null),[g,y]=l.useState(null);if(l.useEffect(()=>{let m=!1;return Promise.all([window.api.otel.getConfig(),window.api.otel.status(),window.api.otel.configPath()]).then(([N,I,h])=>{m||(s(N),a(N),o(I),p(h))}).catch(N=>{console.warn("[telemetry] load failed:",N==null?void 0:N.message)}),()=>{m=!0}},[]),!r||!t)return e.jsx(K,{title:"loading…"});const f=JSON.stringify(r)!==JSON.stringify(t),w=(m,N)=>{u(null),a({...r,[m]:N})},R=async()=>{j(!0),u(null);try{const m=await window.api.otel.setConfig(r);!m.ok&&m.error&&u(m.error),s(m.config),a(m.config),o(m.status),y(Date.now())}catch(m){u(m instanceof Error?m.message:String(m))}finally{j(!1)}},S=()=>{u(null),a(t)};return e.jsxs("div",{className:"space-y-5",children:[e.jsxs("header",{className:"space-y-1",children:[e.jsx("h2",{className:"text-fg text-base font-medium",children:"OpenTelemetry export"}),e.jsx("p",{className:"text-fg-dim text-xs leading-relaxed",children:"When enabled, every classified transcript event (tool_use, todo_write, plan, usage, agent_spawn) is mirrored as a 0-duration OTLP/HTTP span. Point this at a local collector (Jaeger, Grafana Tempo, SigNoz, Honeycomb) for cross-session analysis."}),c&&e.jsxs("p",{className:"text-fg-faint text-[11px]",children:["stored at ",e.jsx("span",{className:"font-mono",children:c})]})]}),e.jsxs("section",{className:"border border-line rounded p-3 space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-fg",children:"Export transcripts to OTEL"}),e.jsx("div",{className:"text-fg-faint text-xs",children:"Toggle off to disable all span emission immediately."})]}),e.jsx(Y,{checked:r.enabled,onChange:m=>w("enabled",m),label:r.enabled?"on":"off"})]}),e.jsxs("label",{className:"block space-y-1",children:[e.jsx("span",{className:"text-fg-dim text-xs",children:"OTLP/HTTP traces endpoint"}),e.jsx("input",{type:"text",value:r.endpoint,onChange:m=>w("endpoint",m.target.value),placeholder:"http://localhost:4318/v1/traces",className:"w-full bg-bg border border-line rounded px-2 py-1 text-fg font-mono text-xs"})]}),e.jsxs("label",{className:"block space-y-1",children:[e.jsxs("span",{className:"text-fg-dim text-xs",children:["Headers (newline-separated ",e.jsx("span",{className:"font-mono",children:"Key: Value"}),")"]}),e.jsx("textarea",{value:r.headers,onChange:m=>w("headers",m.target.value),placeholder:"x-honeycomb-team: YOUR_API_KEY",rows:3,className:"w-full bg-bg border border-line rounded px-2 py-1 text-fg font-mono text-xs"})]}),e.jsxs("label",{className:"block space-y-1",children:[e.jsx("span",{className:"text-fg-dim text-xs",children:"Service name"}),e.jsx("input",{type:"text",value:r.serviceName,onChange:m=>w("serviceName",m.target.value),placeholder:"session-manager",className:"w-full bg-bg border border-line rounded px-2 py-1 text-fg font-mono text-xs"})]}),e.jsxs("div",{className:"flex items-start justify-between gap-3 pt-1",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-fg",children:"Include content (PII)"}),e.jsxs("div",{className:"text-fg-faint text-xs leading-relaxed",children:["Adds tool inputs, plan text, and agent prompts to span attributes. Off by default — matches upstream Claude Code's ",e.jsx("span",{className:"font-mono",children:"OTEL_LOG_USER_PROMPTS"})," opt-in."]})]}),e.jsx(Y,{checked:r.includeContent,onChange:m=>w("includeContent",m),label:r.includeContent?"on":"off"})]})]}),n&&e.jsxs("section",{className:"border border-line rounded p-3 text-xs space-y-1",children:[e.jsx("div",{className:"text-fg-dim",children:"Runtime status"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-fg-faint",children:"enabled:"})," ",e.jsx("span",{className:n.enabled?"text-accent":"text-fg-dim",children:String(n.enabled)})]}),n.error&&e.jsxs("div",{className:"text-red-400 break-words",children:[e.jsx("span",{className:"text-fg-faint",children:"error:"})," ",n.error]}),!n.error&&n.enabled&&e.jsx("div",{className:"text-fg-faint",children:"spans batched and exported in the background. Verify by enabling and pointing at a local Jaeger."})]}),e.jsxs("footer",{className:"flex items-center gap-3 pt-2 border-t border-line",children:[e.jsx("button",{onClick:R,disabled:!f||b,className:"px-3 py-1 text-xs rounded bg-accent text-bg disabled:opacity-50 disabled:cursor-not-allowed",children:b?"saving…":"Save"}),e.jsx("button",{onClick:S,disabled:!f||b,className:"px-3 py-1 text-xs rounded border border-line text-fg-dim hover:text-fg disabled:opacity-50 disabled:cursor-not-allowed",children:"Revert"}),i&&e.jsx("span",{className:"text-red-400 text-xs",children:i}),!i&&g&&!f&&e.jsx("span",{className:"text-fg-faint text-xs",children:"saved"})]})]})}function bt(){const[t,s]=l.useState(null),[n,o]=l.useState(null),[r,a]=l.useState("Copy"),[c,p]=l.useState(!1),[b,j]=l.useState(null),[i,u]=l.useState(null),[g,y]=l.useState(null),[f,w]=l.useState(!1),R=()=>{window.api.telemetry.status().then(o).catch(v=>{console.warn("[product-telemetry] status reload failed:",v==null?void 0:v.message)})};if(l.useEffect(()=>{let v=!1;return Promise.all([window.api.telemetry.getConfig(),window.api.telemetry.status()]).then(([d,C])=>{v||(s(d),o(C))}).catch(d=>{console.warn("[product-telemetry] load failed:",d==null?void 0:d.message)}),()=>{v=!0}},[]),!t||!n)return e.jsx(K,{title:"loading…"});const S=async v=>{u(null);try{const d=await window.api.telemetry.setConfig(v);s(d.config),o(d.status)}catch(d){u(d instanceof Error?d.message:String(d))}},m=v=>{S({...t,enabled:v})},N=v=>{S({...t,enabled:v.turnOff?!1:t.enabled,noticeAckedAt:new Date().toISOString()})},I=async()=>{await Re(t.installId)?(a("Copied"),setTimeout(()=>a("Copy"),1500)):ue.error("Could not copy install id to the clipboard")},h=async()=>{p(!0),j(null);try{const v=await window.api.telemetry.flushNow();j(`sent ${v.sent.length}, failed ${v.failed.length}`),R()}catch(v){j(v instanceof Error?v.message:String(v))}finally{p(!1)}},k=async()=>{if(f){w(!1);return}try{const v=await window.api.telemetry.recentRecords();y(v),w(!0)}catch(v){console.warn("[product-telemetry] recentRecords failed:",v instanceof Error?v.message:v)}},E=t.lastDailyFlushAt?new Date(t.lastDailyFlushAt).getTime()+ft:null;return e.jsxs("div",{className:"space-y-5","data-testid":"product-telemetry-section",children:[e.jsxs("header",{className:"space-y-1",children:[e.jsx("h2",{className:"text-fg text-base font-medium",children:"Product telemetry (bilko.run)"}),e.jsxs("p",{className:"text-fg-dim text-xs leading-relaxed",children:["Anonymous, strictly non-identifying usage/error reporting that helps improve this app. On by default; switchable off in one click. See"," ",e.jsx("span",{className:"font-mono",children:"session-manager-operations/architecture/telemetry.md"})," for the full data model."]})]}),!t.noticeAckedAt&&e.jsxs("section",{"data-testid":"telemetry-first-run-notice",className:"border border-accent rounded p-3 space-y-2 text-xs",children:[e.jsx("div",{className:"text-fg font-medium",children:"This app sends anonymous product telemetry"}),e.jsx("p",{className:"text-fg-dim leading-relaxed",children:"Collected: a coarse machine profile (OS/arch/app version, no hardware id), error and warning counts, and session counts — sent on start-up, after an update, and once a day."}),e.jsx("p",{className:"text-fg-dim leading-relaxed",children:"Never collected: prompts, transcripts, file paths, project names, email, or username."}),e.jsxs("div",{className:"flex items-center gap-2 pt-1",children:[e.jsx("button",{onClick:()=>N({turnOff:!1}),className:"px-3 py-1 rounded bg-accent text-bg",children:"OK"}),e.jsx("button",{onClick:()=>N({turnOff:!0}),className:"px-3 py-1 rounded border border-line text-fg-dim hover:text-fg",children:"Turn off"})]})]}),e.jsxs("section",{className:"border border-line rounded p-3 space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-fg",children:"Send anonymous product telemetry"}),e.jsx("div",{className:"text-fg-faint text-xs",children:"Sent on start-up, after an update, and once a day. Strictly anonymous — an install id only, never an email, name, or other identifier."})]}),e.jsx(Y,{checked:t.enabled,onChange:m,label:t.enabled?"on":"off"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("div",{className:"text-fg-dim text-xs",children:"Anonymous install id"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"text",readOnly:!0,value:t.installId,"aria-label":"Anonymous install identifier",className:"flex-1 bg-bg border border-line rounded px-2 py-1 text-fg font-mono text-xs"}),e.jsx("button",{onClick:I,className:"px-2 py-1 text-xs rounded border border-line text-fg-dim hover:text-fg",children:r})]})]}),e.jsxs("div",{className:"space-y-1 text-xs",children:[e.jsx("div",{className:"text-fg-faint",children:"Endpoint"}),e.jsx("div",{className:"font-mono text-fg-dim break-all",children:t.endpoint})]}),e.jsxs("div",{className:"space-y-1 text-xs",children:[e.jsx("div",{className:"text-fg-faint",children:"Send cadence"}),e.jsx("div",{className:"text-fg-dim",children:"on start-up, after an update, and once a day"})]}),i&&e.jsx("div",{className:"text-red-400 text-xs",children:i})]}),n.pendingCount>0&&n.sentCount===0&&e.jsxs("section",{"data-testid":"telemetry-never-delivered-warning",className:"border border-red-400 rounded p-3 text-xs space-y-1 text-red-400",children:[e.jsx("div",{className:"font-medium",children:"Never delivered"}),e.jsxs("div",{className:"leading-relaxed",children:[n.pendingCount," record",n.pendingCount===1?"":"s",' queued but none have ever been sent to bilko.run on this install. Check the endpoint/network, or click "Send now" below.']})]}),e.jsxs("section",{className:"border border-line rounded p-3 text-xs space-y-1.5",children:[e.jsx("div",{className:"text-fg-dim",children:"Runtime status"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-fg-faint",children:"last send:"})," ",ce(n.lastFlushAt)]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-fg-faint",children:"next daily send due:"})," ",le(E?new Date(E).toISOString():null)]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-fg-faint",children:"queued records:"})," ",n.pendingCount]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-fg-faint",children:"delivered so far:"})," ",n.sentCount]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-fg-faint",children:"de-duplicated:"})," ",n.dedupedAppends]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-fg-faint",children:"evicted:"})," ",n.evictedCount]}),n.backlog&&e.jsxs("div",{className:"text-fg-faint",children:["last backlog drain (",n.backlog.reason,", ",le(n.backlog.ranAt),"):"," ",n.backlog.linesEnqueued," enqueued, ",n.backlog.linesConfirmed," confirmed,"," ",n.backlog.watermarksRewound," watermarks rewound"]}),n.lastError&&e.jsxs("div",{className:"text-red-400 break-words",children:[e.jsx("span",{className:"text-fg-faint",children:"last error:"})," ",n.lastError.message," ","(",ce(n.lastError.at),")"]})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("button",{onClick:h,disabled:c,className:"px-3 py-1 text-xs rounded bg-accent text-bg disabled:opacity-50 disabled:cursor-not-allowed",children:c?"sending…":"Send now"}),b&&e.jsx("span",{className:"text-fg-faint text-xs",children:b})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsx("button",{onClick:k,className:"px-3 py-1 text-xs rounded border border-line text-fg-dim hover:text-fg",children:f?"Hide last records":"Inspect last records sent"}),f&&e.jsx("pre",{"data-testid":"telemetry-inspector-json",className:"bg-bg border border-line rounded p-2 text-[11px] font-mono overflow-auto max-h-96 whitespace-pre-wrap",children:JSON.stringify(g??[],null,2)})]})]})}function _({children:t,right:s}){return e.jsxs("div",{className:"flex items-baseline justify-between mb-2.5",children:[e.jsx("span",{className:"text-[11.5px] font-bold uppercase tracking-[0.8px] text-fg-faint",children:t}),s]})}function D({kind:t="soft",icon:s,children:n,onClick:o,disabled:r,testId:a}){return e.jsxs("button",{type:"button","data-testid":a,onClick:o,disabled:r,className:`shrink-0 inline-flex items-center gap-2 rounded-lg px-3.5 py-2 text-[13px] font-semibold transition-colors disabled:opacity-50 ${t==="soft"?"bg-bg-hi border border-line text-fg hover:bg-bg":"text-fg-dim hover:text-fg"}`,children:[s&&e.jsx(H,{name:s,size:15}),n]})}function Q({icon:t,title:s,subtitle:n,badge:o,action:r,testId:a}){return e.jsxs("div",{"data-testid":a,className:"flex items-center gap-3.5 bg-bg-hi border border-line rounded-xl px-[18px] py-3.5",children:[e.jsx("span",{className:"w-[34px] h-[34px] rounded-lg bg-bg border border-line grid place-items-center text-fg-dim shrink-0",children:e.jsx(H,{name:t,size:17})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2.5 mb-0.5",children:[e.jsx("span",{className:"text-sm font-semibold text-fg truncate",children:s}),o]}),e.jsx("div",{className:"font-mono text-[11.5px] text-fg-faint truncate",children:n})]}),r]})}function je({onClick:t,disabled:s,testId:n,children:o="Revoke"}){return e.jsx("button",{"data-testid":n,onClick:t,disabled:s,className:"shrink-0 rounded-lg border border-line bg-bg text-accent px-3.5 py-1.5 text-[12.5px] font-semibold hover:bg-bg-hi disabled:opacity-50 transition-colors",children:o})}function ve({code:t,onConfirm:s,testId:n,codeTestId:o,confirmTestId:r,peer:a="browser"}){return e.jsxs("div",{"data-testid":n,className:"flex items-start gap-3.5 px-5 py-4 rounded-2xl border border-accent/40 bg-accent/[0.06]",children:[e.jsx("span",{className:"w-[38px] h-[38px] rounded-[11px] shrink-0 grid place-items-center bg-bg-hi text-accent",children:e.jsx(H,{name:"shield",size:20})}),e.jsxs("div",{className:"flex-1 min-w-0 text-sm text-fg",children:[e.jsx("strong",{className:"font-semibold",children:"Verify E2E session."})," Compare this code with the ",a," — they must match.",e.jsx("div",{"data-testid":o,className:"my-2 text-3xl font-mono tracking-[0.3em] text-center text-accent select-all",children:t}),e.jsx("p",{className:"text-[12.5px] text-fg-faint mb-3",children:"If the codes match, confirm below. Mutating commands are blocked until confirmed."}),e.jsx("button",{"data-testid":r,onClick:s,className:"rounded-lg bg-accent text-white px-4 py-2 text-[13px] font-semibold hover:opacity-90 transition-opacity",children:"Codes match — confirm"})]})]})}function W({children:t,testId:s}){return e.jsx("div",{"data-testid":s,className:"bg-bg-hi border border-line rounded-xl p-5 text-center text-[13.5px] text-fg-faint italic font-serif",children:t})}const yt=260;function jt({peerId:t,machineName:s,cwd:n,onClose:o}){const r=l.useRef(null),a=l.useRef(null),c=Z(i=>i.show),[p,b]=l.useState(!1);l.useEffect(()=>{const i=r.current;if(!i)return;const u=crypto.randomUUID();let g=!0,y=!1,f=!1;const w=(d,C)=>{g&&c("error",`Remote terminal ${d} failed: ${C instanceof Error?C.message:String(C)}`)},R=(d,C)=>window.api.remoteClient.send(t,d,C),S=new Ie.Terminal({fontFamily:'"IBM Plex Mono", JetBrains Mono, ui-monospace, Menlo, monospace',fontSize:ne.fontSize,lineHeight:1.2,cursorBlink:!0,allowProposedApi:!0,theme:Ae[ne.theme]}),m=new Te.FitAddon;S.loadAddon(m);const N=()=>{if(Pe(i.clientWidth,i.clientHeight))try{m.fit()}catch{}};S.open(i),N(),S.focus();const I=window.api.remoteClient.onEvent(d=>{if(d.peerId!==t)return;const C=d.payload??{};C.tabId===u&&(d.type==="event:pty:data"?typeof C.data=="string"&&S.write(C.data):d.type==="event:pty:exit"&&(y=!0,S.options.disableStdin=!0,S.write(`\r
3
- \x1B[38;5;240m[remote session exited code=${C.exitCode??"?"}]\x1B[0m\r
4
- `),b(!0)))}),h=S.onData(d=>{y||R("cmd:pty:write",{tabId:u,data:d}).catch(C=>w("write",C))}),k=S.onResize(({cols:d,rows:C})=>{y||R("cmd:pty:resize",{tabId:u,cols:d,rows:C}).catch(T=>w("resize",T))});R("cmd:pty:spawn",{tabId:u,cwd:n,cols:S.cols,rows:S.rows}).catch(d=>w("spawn",d)),a.current=()=>{f||(f=!0,R("cmd:pty:kill",{tabId:u}).catch(d=>w("kill",d)))};const E=()=>N();window.addEventListener("resize",E);const v=new ResizeObserver(()=>N());return v.observe(i),()=>{var d;window.removeEventListener("resize",E),v.disconnect(),I(),h.dispose(),k.dispose(),(d=a.current)==null||d.call(a),a.current=null,g=!1,S.dispose()}},[t,n,c]);const j=()=>{var i;(i=a.current)==null||i.call(a),o()};return e.jsxs("div",{"data-testid":"remote-terminal-panel",className:"bg-bg-hi border border-line rounded-xl overflow-hidden",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3 px-[18px] pt-3.5 pb-2",children:[e.jsxs("div",{className:"flex items-center gap-2.5 min-w-0",children:[e.jsx(_,{children:"Remote terminal"}),e.jsx(L,{tone:"accent",children:e.jsxs("span",{"data-testid":"remote-terminal-badge",children:["Remote: ",s]})}),p&&e.jsx(L,{tone:"dim",children:"exited"})]}),e.jsx(D,{kind:"ghost",onClick:j,testId:"remote-terminal-close",children:"Close"})]}),e.jsx("div",{ref:r,"data-testid":"remote-terminal-host",className:"w-full bg-bg",style:{height:yt}})]})}function de(t){return t instanceof Error?t.message:String(t)}function vt({peerId:t,machineName:s}){const n=Z(m=>m.show),[o,r]=l.useState([]),[a,c]=l.useState("loading"),[p,b]=l.useState(null),[j,i]=l.useState(""),[u,g]=l.useState(!1),y=l.useRef(0),f=l.useCallback(async()=>{const m=++y.current;c("loading");try{const N=await window.api.remoteClient.send(t,"cmd:sessions:load",{});if(m!==y.current)return;r(Array.isArray(N==null?void 0:N.tabs)?N.tabs:[]),c("ready")}catch(N){if(m!==y.current)return;c("error"),n("error",`Could not load sessions from ${s}: ${de(N)}`)}},[t,s,n]);l.useEffect(()=>(f(),()=>{y.current++}),[f]);const w=j.trim(),R=p!==null&&w.length>0&&!u,S=l.useCallback(async()=>{if(!(p===null||j.trim().length===0)){g(!0);try{await window.api.remoteClient.send(t,"cmd:chat:send",{tabId:p,prompt:j}),i("")}catch(m){n("error",`Could not send prompt to ${s}: ${de(m)}`)}finally{g(!1)}}},[t,s,p,j,n]);return e.jsxs("div",{"data-testid":"remote-sessions-panel",className:"flex flex-col gap-3",children:[e.jsx(_,{right:e.jsx(L,{tone:"accent",className:"",title:`Sessions on ${s}`,children:e.jsxs("span",{"data-testid":"remote-sessions-badge",children:["Remote: ",s]})}),children:"Remote sessions"}),e.jsx("div",{className:"flex justify-end",children:e.jsx(D,{icon:"history",testId:"remote-sessions-refresh",onClick:()=>{f()},disabled:a==="loading",children:"Refresh"})}),a==="loading"&&e.jsx(W,{testId:"remote-sessions-loading",children:"Loading sessions…"}),a==="error"&&e.jsxs(W,{testId:"remote-sessions-error",children:["Could not load sessions from ",s,"."]}),a==="ready"&&o.length===0&&e.jsxs(W,{testId:"remote-sessions-empty",children:["No sessions open on ",s,"."]}),a==="ready"&&o.map(m=>e.jsx(Q,{testId:`remote-session-${m.sessionId}`,icon:"terminal",title:m.label||m.cwd,subtitle:m.cwd,action:e.jsx(D,{kind:p===m.sessionId?"soft":"ghost",testId:`remote-session-select-${m.sessionId}`,onClick:()=>b(m.sessionId),children:p===m.sessionId?"Selected":"Select"})},m.sessionId)),a==="ready"&&o.length>0&&e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("textarea",{"data-testid":"remote-sessions-prompt",value:j,onChange:m=>i(m.target.value),rows:3,placeholder:p?`Prompt for the selected session on ${s}`:"Select a session first",className:"w-full rounded-lg bg-bg-hi border border-line px-3 py-2 text-[13px] text-fg placeholder:text-fg-faint"}),e.jsx("div",{className:"flex justify-end",children:e.jsx(D,{icon:"play",testId:"remote-sessions-send",onClick:()=>{S()},disabled:!R,children:"Send"})})]})]})}const wt="relay_client_auth_unavailable",X=t=>(t==null?void 0:t.message)||String(t);function Nt({peer:t,panel:s,onConnect:n,onRevoke:o,onTogglePanel:r,onConfirmSas:a}){const c=t.status==="authenticated",p=t.status==="failed"&&t.reason===wt,b=t.status==="connecting",j=new Date(t.pairedAt).toLocaleString(),i=(s==null?void 0:s.peerId)===t.peerId?s.kind:null;return e.jsxs("div",{className:"space-y-2","data-testid":"remote-machine-row",children:[e.jsx(Q,{testId:"remote-machine",icon:"terminal",title:t.machineName,badge:c?e.jsx(L,{tone:"default",className:"!text-sage !border-sage/40",children:"online"}):void 0,subtitle:`${t.peerId} · last seen ${j}`,action:e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[c?e.jsxs(e.Fragment,{children:[e.jsx(D,{testId:"remote-machine-terminal",icon:"terminal",onClick:()=>r(t.peerId,"terminal"),children:"Open terminal"}),e.jsx(D,{testId:"remote-machine-sessions",onClick:()=>r(t.peerId,"sessions"),children:"Sessions"})]}):!p&&t.status!=="pending_sas"?e.jsx(D,{testId:"remote-machine-connect",disabled:b,onClick:()=>n(t.peerId),children:b?"Connecting…":"Connect"}):null,e.jsx(je,{testId:"remote-machine-revoke",onClick:()=>o(t.peerId),children:"Revoke"})]})}),p&&e.jsx("p",{"data-testid":"remote-machine-unavailable",className:"text-[12.5px] leading-snug text-fg-faint",children:"The bilko.run relay does not support desktop clients yet"}),t.status==="pending_sas"&&t.sas&&e.jsx(ve,{peer:"other machine",testId:"remote-machine-sas",codeTestId:"remote-machine-sas-code",confirmTestId:"remote-machine-sas-confirm",code:t.sas,onConfirm:()=>a(t.peerId)}),i==="terminal"&&e.jsx(jt,{peerId:t.peerId,machineName:t.machineName,cwd:"",onClose:()=>r(t.peerId,"terminal")}),i==="sessions"&&e.jsx(vt,{peerId:t.peerId,machineName:t.machineName})]})}function kt(){const t=Z(i=>i.show),[s,n]=l.useState([]),[o,r]=l.useState(null),a=l.useCallback(async()=>{try{n(await window.api.remoteClient.list())}catch(i){t("error",X(i))}},[t]);l.useEffect(()=>{a();const i=window.api.remoteClient.onStatus(u=>{n(g=>g.map(y=>y.peerId===u.peerId?{...y,status:u.status,reason:u.reason,sas:u.sas}:y))});return()=>{i()}},[a]);const c=l.useCallback(async i=>{try{const u=await window.api.remoteClient.connect(i);n(g=>g.map(y=>y.peerId===i?{...y,status:u.status,reason:u.reason,sas:u.sas}:y))}catch(u){t("error",X(u))}},[t]),p=l.useCallback(async i=>{try{const u=await window.api.remoteClient.confirmSas(i);u.ok||t("error",u.reason??"SAS confirmation failed"),await a()}catch(u){t("error",X(u))}},[t,a]),b=l.useCallback(async i=>{try{await window.api.remoteClient.remove(i),r(u=>(u==null?void 0:u.peerId)===i?null:u),await a()}catch(u){t("error",X(u))}},[t,a]),j=l.useCallback((i,u)=>{r(g=>(g==null?void 0:g.peerId)===i&&g.kind===u?null:{peerId:i,kind:u})},[]);return e.jsxs("div",{"data-testid":"remote-machines",children:[e.jsx(_,{right:s.length>0?e.jsxs("span",{className:"text-[11.5px] text-fg-faint",children:[s.length," paired"]}):void 0,children:"Remote machines"}),s.length===0?e.jsx(W,{testId:"remote-machines-empty",children:"No machines paired yet."}):e.jsx("div",{className:"space-y-2",children:s.map(i=>e.jsx(Nt,{peer:i,panel:o,onConnect:c,onRevoke:b,onTogglePanel:j,onConfirmSas:p},i.peerId))})]})}function xe({label:t,hint:s,on:n,onChange:o,warn:r,disabled:a,first:c,testId:p}){return e.jsxs("div",{className:`flex items-center justify-between gap-4 px-[18px] py-[15px] ${c?"":"border-t border-rule"} ${a?"opacity-50":""}`,children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-sm font-semibold text-fg mb-0.5",children:t}),e.jsxs("div",{className:`text-[12.5px] leading-snug flex items-center gap-1.5 ${r?"text-honey-dark":"text-fg-faint"}`,children:[r&&e.jsx("span",{"aria-hidden":!0,children:"⚠"}),s]})]}),e.jsx("span",{"data-testid":p,className:"inline-flex",children:e.jsx(Y,{checked:n,onChange:a?()=>{}:o,disabled:a})})]})}function St({device:t,online:s,onRevoke:n,revoking:o}){const r=t.lastConnectedAt?new Date(t.lastConnectedAt).toLocaleString():`paired ${new Date(t.issuedAt).toLocaleDateString()}`;return e.jsx(Q,{testId:"settings-remote-device",icon:"wifi",title:t.deviceName,badge:s?e.jsx(L,{tone:"default",className:"!text-sage !border-sage/40",children:"online"}):void 0,subtitle:`${t.deviceId} · last seen ${r}`,action:e.jsx(je,{testId:"settings-remote-revoke",onClick:()=>n(t.deviceId),disabled:o})})}const M=t=>(t==null?void 0:t.message)||String(t);function Ct(){var se;const t=Z(x=>x.show),[s,n]=l.useState(null),[o,r]=l.useState(!0),[a,c]=l.useState(!1),[p,b]=l.useState(!1),[j,i]=l.useState("idle"),[u,g]=l.useState(""),[y,f]=l.useState(null),[w,R]=l.useState(!1),[S,m]=l.useState([]),[N,I]=l.useState(!1),h=l.useRef(null);l.useEffect(()=>{let x=!1;const A=()=>window.api.webRemote.getStatus().then($=>{x||n($)}).catch(()=>{});window.api.webRemote.getStatus().then($=>{x||(n($),r(!1))}).catch($=>{x||(t("error",`Remote status: ${M($)}`),r(!1))});const Se=window.api.webRemote.onStatus($=>n($)),Ce=window.api.webRemote.onTokenRevoked(()=>{t("warn","Remote device token was revoked. Re-pair to reconnect."),A()}),Ee=window.api.webRemote.onRevokedAll(({revokedCount:$})=>{t("info",`Panic: all ${$} device(s) revoked and sessions torn down.`),A()});return()=>{x=!0,Se(),Ce(),Ee()}},[t]),l.useEffect(()=>{if(j!=="enter-otp")return;const x=setTimeout(()=>{var A;return(A=h.current)==null?void 0:A.focus()},50);return()=>clearTimeout(x)},[j]);const k=async()=>n(await window.api.webRemote.getStatus()),E=async()=>{if(s){c(!0);try{const x=s.enabled?await window.api.webRemote.disable():await window.api.webRemote.enable();x&&x.ok===!1&&t("error",x.error||"Failed to change remote state."),await k()}catch(x){t("error",`Failed: ${M(x)}`)}finally{c(!1)}}},v=async()=>{if(s){b(!0);try{const x=s.remoteControlEnabled?await window.api.webRemote.disableControl():await window.api.webRemote.enableControl();x&&x.ok===!1&&t("error",x.error||"Failed to change command writes."),await k()}catch(x){t("error",`Failed: ${M(x)}`)}finally{b(!1)}}},d=async()=>{const x=u.trim().toUpperCase();if(!/^[A-Z0-9]{8}$/.test(x)){t("warn","Pairing code must be 8 alphanumeric characters.");return}i("pairing");try{const A=await window.api.webRemote.pair(x);if(!A.ok){t("error",A.error||"Pairing failed."),i("enter-otp");return}t("info","Device paired successfully."),g(""),i("idle"),await k()}catch(A){t("error",`Pairing error: ${M(A)}`),i("enter-otp")}},C=async x=>{f(x);try{const A=await window.api.webRemote.revokeDevice(x);if(!A.ok){t("error",A.error||"Revoke failed.");return}await k()}catch(A){t("error",`Revoke error: ${M(A)}`)}finally{f(null)}},T=async()=>{R(!0);try{const x=await window.api.webRemote.revokeAll();x.ok||t("error",x.error||"Panic revoke failed.")}catch(x){t("error",`Panic revoke error: ${M(x)}`)}finally{R(!1)}},ee=async()=>{if(N){I(!1);return}try{const x=await window.api.webRemote.auditTail(100);if(!x.ok){t("error",`Audit log: ${x.error||"unavailable"}`);return}m(x.lines??[]),I(!0)}catch(x){t("error",`Audit log: ${M(x)}`)}},U=async()=>{try{const x=await window.api.webRemote.confirmSas();x!=null&&x.ok||t("error",`SAS confirm failed: ${(x==null?void 0:x.error)??"unexpected state — reconnect the mobile app"}`)}catch(x){t("error",`SAS confirm failed: ${M(x)}`)}};if(o)return e.jsx("div",{className:"p-6 text-fg-faint text-sm",children:"Loading…"});const O=(s==null?void 0:s.enabled)??!1,F=(s==null?void 0:s.remoteControlEnabled)??!1,V=(s==null?void 0:s.connected)??!1,z=(s==null?void 0:s.e2eActive)??!1,we=(s==null?void 0:s.e2eAuthenticated)??!1,Ne=(s==null?void 0:s.e2eState)??"idle",te=(s==null?void 0:s.pendingSas)??null,G=(s==null?void 0:s.devices)??[],J=O&&V,ke=V?((se=G.reduce((x,A)=>A.lastConnectedAt&&(!x||(x.lastConnectedAt??"")<A.lastConnectedAt)?A:x,null))==null?void 0:se.deviceId)??null:null;return e.jsxs("div",{"data-testid":"settings-remote",className:"p-4 max-w-[760px] space-y-[22px]",children:[te&&e.jsx(ve,{testId:"settings-remote-sas",codeTestId:"settings-remote-sas-code",confirmTestId:"settings-remote-sas-confirm",code:te,onConfirm:U}),Ne==="failed"&&e.jsxs("div",{className:"flex items-center gap-3.5 px-5 py-4 rounded-2xl border border-[#eccdbe] bg-[#f8e8e0]",children:[e.jsx("span",{className:"w-4 h-4 rounded-full bg-accent text-white grid place-items-center text-[11px] font-bold shrink-0",children:"!"}),e.jsxs("div",{className:"text-[13px] text-[#9a3f1f]",children:[e.jsx("strong",{className:"font-semibold",children:"E2E key exchange failed."})," ",e.jsx("span",{className:"text-fg-dim",children:"The session key could not be established. Reconnect the mobile app to retry."})]})]}),e.jsxs("div",{className:`flex items-center gap-3.5 px-5 py-4 rounded-2xl border ${J?"border-[#e8cdb9] bg-[#f5e9df]":"border-line bg-bg-hi"}`,children:[e.jsx("span",{className:`w-[38px] h-[38px] rounded-[11px] shrink-0 grid place-items-center ${J?"bg-[#f0d9c8] text-accent":"bg-[#e4ebd6] text-sage"}`,children:e.jsx(H,{name:J?"wifi":"shield",size:20})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"text-[14.5px] font-semibold text-fg mb-0.5",children:J?"Remote control is active":O?"Remote control is idle":"Remote control is off"}),e.jsx("div",{className:"text-[13px] text-fg-dim leading-snug",children:J?"A web session can send commands to this machine. Revoke below if unexpected.":O?"The relay is on but no device is connected. Pair a device to begin.":"The relay connection is closed — no remote commands are accepted. Enable it below only when you need it."})]}),z&&we?e.jsx(L,{tone:"default",className:"!text-sage !border-sage/40",children:"E2E authenticated"}):z?e.jsx(L,{tone:"warn",children:"E2E · awaiting SAS"}):e.jsx(L,{tone:"dim",children:"relay only · not E2E yet"})]}),e.jsxs("div",{children:[e.jsx(_,{children:"Remote control"}),e.jsxs("div",{className:"bg-bg-hi border border-line rounded-2xl overflow-hidden",children:[e.jsx(xe,{first:!0,testId:"settings-remote-enable",label:"Allow remote control from the web",hint:"Master switch. When off, the relay refuses every connection.",on:O,disabled:a,onChange:()=>{a||E()}}),e.jsx(xe,{testId:"settings-remote-control",label:"Allow command writes (pty + scheduler)",hint:F?"A paired device can run shell commands and queue jobs on this machine.":"Paired devices can watch session output but cannot send commands.",warn:F,disabled:!O||p,on:F&&O,onChange:()=>{O&&!p&&v()}})]})]}),e.jsxs("div",{children:[e.jsx(_,{children:"Pair a device"}),j==="idle"?e.jsxs("div",{className:"flex items-center justify-between gap-4 bg-bg-hi border border-line rounded-2xl px-5 py-[18px]",children:[e.jsxs("div",{"data-testid":"settings-remote-pair-hint",className:"text-[13.5px] text-fg-dim leading-relaxed max-w-[420px]",children:["On your phone open ",e.jsx("strong",{className:"text-fg font-semibold",children:"bilko.run/projects/session-manager"}),", sign in, and tap"," ",e.jsx("strong",{className:"text-fg font-semibold",children:"Generate pairing code"}),". Enter the 8-character code here."]}),e.jsx(D,{testId:"settings-remote-pair-start",kind:"soft",icon:"link",onClick:()=>i("enter-otp"),children:"Pair Device…"})]}):e.jsxs("div",{className:"flex items-center gap-5 bg-bg-hi border border-accent/40 rounded-2xl px-6 py-[22px]",children:[e.jsx("span",{className:"w-[52px] h-[52px] rounded-xl bg-bg border border-line grid place-items-center text-accent shrink-0",children:e.jsx(H,{name:"link",size:24})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"text-[11.5px] font-bold uppercase tracking-[0.8px] text-fg-faint mb-2",children:"Enter the code from your phone"}),e.jsx("input",{ref:h,"data-testid":"settings-remote-otp",value:u,onChange:x=>g(x.target.value.toUpperCase().replace(/[^A-Z0-9]/g,"")),onKeyDown:x=>{x.key==="Enter"&&d()},maxLength:8,placeholder:"XXXXXXXX",disabled:j==="pairing",className:"w-full bg-transparent border-0 outline-none font-mono text-[30px] font-semibold tracking-[6px] text-fg placeholder:text-fg-faint/40 disabled:opacity-50"})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[e.jsx(D,{testId:"settings-remote-pair-confirm",kind:"soft",onClick:d,disabled:j==="pairing"||u.length<8,children:j==="pairing"?"Pairing…":"Confirm"}),e.jsx(D,{kind:"ghost",onClick:()=>{i("idle"),g("")},disabled:j==="pairing",children:"cancel"})]})]})]}),e.jsxs("div",{children:[e.jsx(_,{right:G.length>0?e.jsxs("span",{className:"text-[11.5px] text-fg-faint",children:[G.length," paired"]}):void 0,children:"Paired devices"}),G.length===0?e.jsx(W,{children:"No devices paired yet."}):e.jsx("div",{className:"space-y-2",children:G.map(x=>e.jsx(St,{device:x,online:x.deviceId===ke,onRevoke:C,revoking:y===x.deviceId},x.deviceId))})]}),e.jsx(kt,{}),e.jsxs("div",{children:[e.jsx(_,{right:e.jsx("button",{"data-testid":"settings-remote-audit-toggle",onClick:ee,className:"text-[12px] text-fg-faint hover:text-fg-dim transition-colors",children:N?"Hide":"Show last 100 · 0600, stays on this machine"}),children:"Audit log"}),N&&e.jsx("div",{className:"bg-bg-hi border border-line rounded-xl overflow-auto max-h-64 mb-2",children:S.length===0?e.jsx("p",{className:"p-4 text-[12.5px] text-fg-faint",children:"No entries today."}):e.jsx("pre",{"data-testid":"settings-remote-audit",className:"p-4 text-[12px] text-fg-dim font-mono whitespace-pre-wrap leading-relaxed",children:S.join(`
5
- `)})}),e.jsxs("p",{className:"text-[12px] text-fg-faint",children:["Full log at"," ",e.jsx("code",{className:"font-mono text-fg-faint",children:"~/.claude/session-manager/logs/remote-audit-YYYY-MM-DD.log"})," ","(0600, never leaves this machine)"]})]}),e.jsxs("div",{className:"flex items-center justify-between gap-4 bg-[#f8e8e0] border border-[#eccdbe] rounded-2xl px-5 py-4",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx("span",{className:"w-4 h-4 rounded-full bg-accent text-white grid place-items-center text-[11px] font-bold shrink-0",children:"!"}),e.jsx("span",{className:"text-sm font-semibold text-[#9a3f1f]",children:"Panic — disconnect & revoke everything"})]}),e.jsx("div",{className:"text-[12.5px] text-fg-dim leading-snug max-w-[440px]",children:"Tears down every active session and invalidates all paired-device tokens. Devices must re-pair. Use if you suspect compromise."})]}),e.jsx("button",{"data-testid":"settings-remote-revoke-all",onClick:T,disabled:w,className:"shrink-0 rounded-lg bg-accent text-white px-[18px] py-2 text-[13px] font-semibold hover:opacity-90 disabled:opacity-50 transition-opacity",children:w?"Revoking…":"Revoke all"})]})]})}const Et={compact:"Compact",roomy:"Roomy"};function Rt(){const{model:t,setModel:s}=Oe(),{catalog:n}=De(null),{density:o,setDensity:r}=$e();return e.jsxs("div",{className:"p-4 max-w-3xl space-y-5 text-sm",children:[e.jsxs("header",{className:"space-y-1",children:[e.jsx("h2",{className:"text-fg text-base font-medium",children:"Session Manager preferences"}),e.jsx("p",{className:"text-fg-dim text-xs leading-relaxed",children:"Native app preferences, stored locally — not part of Claude Code's own settings."})]}),e.jsxs("section",{className:"border border-line rounded p-3 space-y-3",children:[e.jsxs("div",{className:"block space-y-1",children:[e.jsx("span",{className:"text-fg-dim text-xs",children:"Default model for raw sessions"}),e.jsx(ae,{options:Me(n),value:t,onChange:s,mono:!0}),e.jsx("span",{className:"text-fg-faint text-[11px]",children:"Used when opening a raw interactive session without an explicit per-launch override."})]}),e.jsxs("div",{className:"block space-y-1",children:[e.jsx("span",{className:"text-fg-dim text-xs",children:"Density"}),e.jsx(ae,{options:["compact","roomy"],value:o,onChange:a=>r(a),labels:Et}),e.jsx("span",{className:"text-fg-faint text-[11px]",children:"Compact tightens spacing across the app's chrome; Roomy is the default."})]})]})]})}function It({cwd:t}){const[s,n]=l.useState(!1),[o,r]=l.useState(!1),[a,c]=l.useState(!1),[p,b]=l.useState(!1);l.useEffect(()=>{var y,f;let g=!1;return n(!1),Promise.resolve((f=(y=window.api.promptSessions)==null?void 0:y.getWorktreeDisabled)==null?void 0:f.call(y,{cwd:t})).then(w=>{g||(r((w==null?void 0:w.disabled)??!1),c((w==null?void 0:w.disabled)??!1),n(!0))},()=>{g||n(!0)}),()=>{g=!0}},[t]);const j=s&&a!==o,i=()=>{var g,y;b(!0),Promise.resolve((y=(g=window.api.promptSessions)==null?void 0:g.setWorktreeDisabled)==null?void 0:y.call(g,{cwd:t,disabled:a})).then(f=>{r((f==null?void 0:f.disabled)??a),c((f==null?void 0:f.disabled)??a)}).catch(f=>ue.error(f instanceof Error?f.message:String(f))).finally(()=>b(!1))},u=()=>c(o);return e.jsxs("div",{"data-testid":"settings-epic-isolation",className:"border-b border-line bg-bg-elev px-3 py-2.5 text-[11.5px] leading-snug",children:[e.jsxs("label",{className:"flex items-start gap-2",children:[e.jsx("input",{type:"checkbox",checked:a,disabled:!s||p,onChange:g=>c(g.target.checked),"data-testid":"settings-epic-isolation-checkbox",className:"mt-0.5"}),e.jsxs("span",{children:[e.jsx("span",{className:"font-semibold text-fg",children:"Disable Epic worktree isolation for this project"}),e.jsxs("span",{className:"block text-fg-dim",children:["Off by default — each active Epic runs in its own isolated ",e.jsx("code",{children:"git worktree"})," (branch"," ",e.jsx("code",{children:"sm-epic/<id>"}),"), merged back to main at an explicit checkpoint. Turning this on makes every new Epic in this project run directly in the shared working tree instead, same as before this feature existed."]})]})]}),j&&e.jsx(pe,{dirty:j,busy:p,onSave:i,onRevert:u})]})}const me={user:{title:"Global settings",body:"This is the real ~/.claude/settings.json on disk — the same file the claude CLI reads on every session it starts. Edits apply machine-wide, to every project and every session."},project:{title:"Project settings",body:"This is the real <project>/.claude/settings.json on disk, checked into git. Edits apply to every current and future session in this project, for anyone who clones it."},local:{title:"Local settings",body:"This is the real <project>/.claude/settings.local.json on disk, gitignored. Edits apply to every current and future session in this project, on this machine only."}};function At(){const t=Le(),s=re(d=>d.tabs),n=re(d=>d.activeTabId),o=s.find(d=>d.id===n)??null,r=(o==null?void 0:o.cwd)??null,a=Fe(d=>d.navFace),c=l.useRef(!1),p=l.useRef(a),[b,j]=l.useState(()=>a==="project"&&r?"project":"user"),[i,u]=l.useState("effective");l.useEffect(()=>{if(p.current!==a){if(p.current=a,c.current){c.current=!1;return}j(a==="project"&&r?"project":"user")}},[a,r]),l.useEffect(()=>{a==="project"&&(i==="telemetry"||i==="remote"||i==="app")&&u("effective")},[a,i]);const g=d=>{c.current=!0,j(d)},y=Je(ie,t,r),f=y[b]??null,w=q(d=>d.files),R=q(d=>d.setDraft),S=q(d=>d.saveJson),m=q(d=>d.revert),[N,I]=l.useState(null),h=l.useMemo(()=>_e(Be(w,y)),[w,y]),k=(d,C,T)=>{var z;const U=y[T??b];if(!U)return;I(null);const O=((z=w[U])==null?void 0:z.draftRaw)??"";let F={};if(O.trim())try{F=JSON.parse(O)}catch{F={}}const V=Ue(F,d,C);R(U,JSON.stringify(V,null,2)+`
6
- `)};if(!t)return e.jsx(K,{title:"loading…"});const E=f?w[f]:null,v=d=>d!=="user"&&!r;return e.jsx(ze,{toolbar:e.jsxs(e.Fragment,{children:[e.jsx(We,{options:[{key:"effective",label:"Guided"},{key:"tree",label:"Tree"},{key:"raw",label:"Raw"},...a==="home"?[{key:"telemetry",label:"Telemetry"},{key:"remote",label:"Phone remote"},{key:"app",label:"Session Manager"}]:[]],active:i,onChange:u}),i!=="telemetry"&&i!=="remote"&&i!=="app"&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"mx-2 text-fg-faint",children:"·"}),e.jsx(Ge,{scopes:ie.scopes,active:b,onChange:g,annotate:d=>{if(v(d))return{exists:!1};const C=y[d];if(!C)return null;const T=w[C];return{exists:(T==null?void 0:T.exists)??!1,dirty:(T==null?void 0:T.dirty)??!1}}}),e.jsx("span",{className:"ml-3 text-fg-faint truncate",children:i==="effective"?`overrides → ${f??"—"}`:f??"—"})]}),e.jsx("div",{className:"flex-1"}),e.jsx("a",{href:"https://code.claude.com/docs/en/settings",target:"_blank",rel:"noreferrer",className:"text-fg-faint hover:text-fg-dim underline-offset-2 hover:underline",children:"schema reference ↗"})]}),footer:i!=="telemetry"&&i!=="remote"&&i!=="app"&&f&&E?e.jsx(pe,{dirty:E.dirty,busy:E.busy,parseError:N||E.parseError,lastSavedAt:E.lastSavedAt,leading:E.exists?null:e.jsx("span",{children:"file will be created on save"}),onSave:async()=>{I(null);const d=await S(f);d.ok||I(d.error??"save failed")},onRevert:()=>{I(null),m(f)}}):null,children:i==="telemetry"?e.jsx(ht,{}):i==="remote"?e.jsx(Ct,{}):i==="app"?e.jsx(Rt,{}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{"data-testid":"settings-scope-note",className:"sticky top-0 z-10 border-b border-line bg-bg-elev px-3 py-2 text-[11.5px] leading-snug",children:[e.jsx("span",{className:"font-semibold text-fg",children:me[b].title}),e.jsx("span",{className:"mx-1.5 text-fg-faint",children:"·"}),e.jsx("span",{className:"text-fg-dim",children:me[b].body}),f&&e.jsx("span",{className:"ml-1.5 font-mono text-fg-faint",children:f})]}),b==="project"&&r&&e.jsx(It,{cwd:r}),v(b)?e.jsx(K,{title:"no active project",hint:"open a tab in a project directory to edit project or local settings"}):i==="effective"?e.jsx(tt,{node:h,targetScope:b,onOverride:k,schema:oe()}):i==="tree"?e.jsx(Ke,{node:h,targetScope:b,onOverride:k,schema:oe()}):f&&E?e.jsx(He,{path:f,value:E.draftRaw,onChange:d=>{I(null),R(f,d)}}):e.jsx(K,{title:"resolving…"})]})})}const Bt=l.memo(At);export{me as SETTINGS_SCOPE_NOTES,Bt as Settings};