clay-server 4.1.0 → 4.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/daemon-project-access.js +14 -0
- package/lib/daemon.js +7 -0
- package/lib/project-connection.js +5 -2
- package/lib/project-full-access.js +69 -56
- package/lib/project-issue-launch.js +3 -1
- package/lib/project-sessions.js +158 -32
- package/lib/project-user-message.js +5 -2
- package/lib/project.js +5 -0
- package/lib/public/app.js +7 -0
- package/lib/public/css/command-palette.css +51 -33
- package/lib/public/css/filebrowser.css +20 -0
- package/lib/public/css/issues.css +4 -4
- package/lib/public/css/messages.css +27 -0
- package/lib/public/css/overlays.css +60 -0
- package/lib/public/css/pane.css +21 -4
- package/lib/public/css/pending-message-queue.css +5 -0
- package/lib/public/index.html +18 -3
- package/lib/public/modules/app-connection.js +4 -0
- package/lib/public/modules/app-header.js +18 -25
- package/lib/public/modules/app-messages.js +24 -7
- package/lib/public/modules/app-panels.js +22 -11
- package/lib/public/modules/clay-file-links.js +7 -0
- package/lib/public/modules/clay-issue-links.js +1 -0
- package/lib/public/modules/clay-log-links.js +5 -0
- package/lib/public/modules/github-links.js +115 -0
- package/lib/public/modules/markdown.js +9 -2
- package/lib/public/modules/mcp-ui.js +154 -17
- package/lib/public/modules/pane-bridge.js +2 -0
- package/lib/public/modules/pane-file-bridge.js +69 -0
- package/lib/public/modules/pending-message-queue-model.js +6 -0
- package/lib/public/modules/pending-message-queue-recovery.js +73 -0
- package/lib/public/modules/pending-message-queue.js +67 -64
- package/lib/public/modules/permission-control.js +187 -0
- package/lib/public/modules/project-session-visibility-settings.js +138 -0
- package/lib/public/modules/project-settings.js +5 -0
- package/lib/public/modules/sidebar-projects.js +14 -3
- package/lib/public/modules/split-view.js +29 -29
- package/lib/sdk-bridge.js +95 -40
- package/lib/server.js +8 -1
- package/lib/session-permission-mode.js +42 -0
- package/lib/session-visibility.js +62 -1
- package/lib/sessions.js +16 -1
- package/lib/ws-schema.js +4 -0
- package/lib/yoke/adapters/claude-worker-policy.js +67 -0
- package/lib/yoke/adapters/claude-worker.js +47 -7
- package/lib/yoke/adapters/claude.js +114 -6
- package/package.json +1 -1
|
@@ -512,9 +512,45 @@ body.mate-dm-active .search-clay-transcript .search-clay-message-user .bubble,
|
|
|
512
512
|
.search-clay-activity-item small { color: var(--text-dimmer); font-size: 10px; line-height: 1.35; }
|
|
513
513
|
@keyframes searchClaySpin { to { transform: rotate(360deg); } }
|
|
514
514
|
|
|
515
|
-
.clayos-
|
|
516
|
-
|
|
517
|
-
|
|
515
|
+
.clayos-record-link {
|
|
516
|
+
display: inline-flex;
|
|
517
|
+
align-items: center;
|
|
518
|
+
gap: 6px;
|
|
519
|
+
min-height: 28px;
|
|
520
|
+
margin: 2px 1px;
|
|
521
|
+
padding: 3px 8px 3px 6px;
|
|
522
|
+
border: 1px solid color-mix(in srgb, var(--record-color, var(--accent)) 32%, var(--border));
|
|
523
|
+
border-radius: 8px;
|
|
524
|
+
background: color-mix(in srgb, var(--record-color, var(--accent)) 7%, var(--bg-alt));
|
|
525
|
+
color: var(--text);
|
|
526
|
+
font: inherit;
|
|
527
|
+
font-size: 11px;
|
|
528
|
+
line-height: 1;
|
|
529
|
+
vertical-align: middle;
|
|
530
|
+
cursor: pointer;
|
|
531
|
+
}
|
|
532
|
+
.clayos-record-link::before {
|
|
533
|
+
content: "\2197";
|
|
534
|
+
display: grid;
|
|
535
|
+
place-items: center;
|
|
536
|
+
width: 17px;
|
|
537
|
+
height: 17px;
|
|
538
|
+
border-radius: 5px;
|
|
539
|
+
background: color-mix(in srgb, var(--record-color, var(--accent)) 14%, transparent);
|
|
540
|
+
color: var(--record-color, var(--accent));
|
|
541
|
+
font-size: 12px;
|
|
542
|
+
}
|
|
543
|
+
.clayos-record-link > span { font-weight: 620; }
|
|
544
|
+
.clayos-record-link > small { min-width: 0; max-width: 28em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-inline-start: 1px solid color-mix(in srgb, currentColor 22%, transparent); padding-inline-start: 6px; color: var(--text-muted); font-size: 11px; }
|
|
545
|
+
.clayos-record-link:hover { border-color: color-mix(in srgb, var(--record-color, var(--accent)) 58%, var(--border)); background: color-mix(in srgb, var(--record-color, var(--accent)) 11%, var(--bg-alt)); }
|
|
546
|
+
.clayos-record-link:focus-visible { outline: 2px solid var(--record-color, var(--accent)); outline-offset: 2px; }
|
|
547
|
+
.clayos-record-link.is-loading { opacity: 0.62; cursor: progress; }
|
|
548
|
+
.clayos-record-link.is-loading::before { animation: searchClaySpin 1.2s linear infinite; }
|
|
549
|
+
.clayos-record-link.is-error { border-color: color-mix(in srgb, var(--warning) 48%, var(--border)); }
|
|
550
|
+
.clayos-issue-link { --record-color: var(--link, var(--accent)); }
|
|
551
|
+
.clayos-log-link { --record-color: var(--accent); }
|
|
552
|
+
|
|
553
|
+
.clayos-session-link {
|
|
518
554
|
display: inline-flex;
|
|
519
555
|
align-items: center;
|
|
520
556
|
gap: 6px;
|
|
@@ -531,9 +567,7 @@ body.mate-dm-active .search-clay-transcript .search-clay-message-user .bubble,
|
|
|
531
567
|
vertical-align: middle;
|
|
532
568
|
cursor: pointer;
|
|
533
569
|
}
|
|
534
|
-
.clayos-session-link::before
|
|
535
|
-
.clayos-issue-link::before,
|
|
536
|
-
.clayos-log-link::before {
|
|
570
|
+
.clayos-session-link::before {
|
|
537
571
|
content: "\2197";
|
|
538
572
|
display: grid;
|
|
539
573
|
place-items: center;
|
|
@@ -544,29 +578,13 @@ body.mate-dm-active .search-clay-transcript .search-clay-message-user .bubble,
|
|
|
544
578
|
color: var(--accent);
|
|
545
579
|
font-size: 12px;
|
|
546
580
|
}
|
|
547
|
-
.clayos-
|
|
548
|
-
.clayos-
|
|
549
|
-
.clayos-session-link
|
|
550
|
-
.clayos-
|
|
551
|
-
.clayos-
|
|
552
|
-
.clayos-session-link
|
|
553
|
-
.clayos-
|
|
554
|
-
.clayos-log-link > small { color: var(--text-muted); font-size: 10px; }
|
|
555
|
-
.clayos-session-link:hover,
|
|
556
|
-
.clayos-issue-link:hover,
|
|
557
|
-
.clayos-log-link:hover { border-color: color-mix(in srgb, var(--accent) 52%, var(--border)); background: color-mix(in srgb, var(--accent) 11%, var(--bg-alt)); }
|
|
558
|
-
.clayos-session-link:focus-visible,
|
|
559
|
-
.clayos-issue-link:focus-visible,
|
|
560
|
-
.clayos-log-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
561
|
-
.clayos-session-link.is-loading,
|
|
562
|
-
.clayos-issue-link.is-loading,
|
|
563
|
-
.clayos-log-link.is-loading { opacity: 0.62; cursor: progress; }
|
|
564
|
-
.clayos-session-link.is-loading::before,
|
|
565
|
-
.clayos-issue-link.is-loading::before,
|
|
566
|
-
.clayos-log-link.is-loading::before { animation: searchClaySpin 1.2s linear infinite; }
|
|
567
|
-
.clayos-session-link.is-error,
|
|
568
|
-
.clayos-issue-link.is-error,
|
|
569
|
-
.clayos-log-link.is-error { border-color: color-mix(in srgb, var(--warning) 48%, var(--border)); }
|
|
581
|
+
.clayos-session-link > span { font-weight: 620; }
|
|
582
|
+
.clayos-session-link > small { color: var(--text-muted); font-size: 10px; }
|
|
583
|
+
.clayos-session-link:hover { border-color: color-mix(in srgb, var(--accent) 52%, var(--border)); background: color-mix(in srgb, var(--accent) 11%, var(--bg-alt)); }
|
|
584
|
+
.clayos-session-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
585
|
+
.clayos-session-link.is-loading { opacity: 0.62; cursor: progress; }
|
|
586
|
+
.clayos-session-link.is-loading::before { animation: searchClaySpin 1.2s linear infinite; }
|
|
587
|
+
.clayos-session-link.is-error { border-color: color-mix(in srgb, var(--warning) 48%, var(--border)); }
|
|
570
588
|
|
|
571
589
|
.search-clay-pending {
|
|
572
590
|
align-self: flex-start;
|
|
@@ -925,11 +943,11 @@ body.mate-dm-active .search-clay-transcript .search-clay-message-user .bubble,
|
|
|
925
943
|
}
|
|
926
944
|
|
|
927
945
|
.clayos-issue-link { max-width: min(100%, 30em); box-sizing: border-box; }
|
|
946
|
+
.clayos-issue-link::before,
|
|
947
|
+
.clayos-log-link::before { content: "\2261"; }
|
|
928
948
|
.clayos-issue-link::before { content: "\25CE"; flex: 0 0 17px; }
|
|
929
|
-
.clayos-issue-link[data-status="in_progress"]::before { color:
|
|
930
|
-
.clayos-issue-link[data-status="resolved"]::before { content: "\2713"; color: var(--
|
|
949
|
+
.clayos-issue-link[data-status="in_progress"]::before { color: var(--warning, var(--accent)); }
|
|
950
|
+
.clayos-issue-link[data-status="resolved"]::before { content: "\2713"; color: var(--success, var(--accent)); }
|
|
931
951
|
.clayos-issue-link[data-status="closed"]::before { content: "\00D7"; color: var(--text-dimmer); }
|
|
932
952
|
.clayos-issue-link > span { flex-shrink: 0; }
|
|
933
|
-
.clayos-issue-link > small { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
|
934
953
|
.clayos-log-link { max-width: 100%; box-sizing: border-box; }
|
|
935
|
-
.clayos-log-link > small { min-width: 0; overflow-wrap: anywhere; line-height: 1.35; }
|
|
@@ -686,6 +686,26 @@
|
|
|
686
686
|
font-size: 12px;
|
|
687
687
|
color: var(--text-muted);
|
|
688
688
|
}
|
|
689
|
+
.mcp-server-perm-mode {
|
|
690
|
+
max-width: 118px;
|
|
691
|
+
padding: 5px 24px 5px 8px;
|
|
692
|
+
border: 1px solid var(--border);
|
|
693
|
+
border-radius: 6px;
|
|
694
|
+
background: var(--bg);
|
|
695
|
+
color: var(--text);
|
|
696
|
+
font-size: 12px;
|
|
697
|
+
}
|
|
698
|
+
.mcp-server-perm-mode:disabled {
|
|
699
|
+
cursor: wait;
|
|
700
|
+
opacity: 0.55;
|
|
701
|
+
}
|
|
702
|
+
.mcp-server-perm-reason {
|
|
703
|
+
max-width: 180px;
|
|
704
|
+
color: var(--text-muted);
|
|
705
|
+
font-size: 11px;
|
|
706
|
+
line-height: 1.3;
|
|
707
|
+
text-align: right;
|
|
708
|
+
}
|
|
689
709
|
|
|
690
710
|
/* Beta toggle row inside settings card */
|
|
691
711
|
.ps-beta-toggle-row,
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
.issue-row small { font-size: 10.5px; font-family: var(--font-mono); }
|
|
47
47
|
.issue-row-meta { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; margin-top: 8px; color: var(--text-dimmer); font: 10.5px/1.5 var(--font-mono); }
|
|
48
48
|
.issue-row-meta .issue-status { font-family: var(--font-sans); }
|
|
49
|
-
.issue-status { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; letter-spacing: .03em; }
|
|
49
|
+
.issue-status { display: inline-flex; align-items: center; gap: 6px; color: var(--text-secondary); font-size: 11px; letter-spacing: .03em; }
|
|
50
50
|
.issue-status::before { content: ''; width: 7px; height: 7px; border: 1px solid currentColor; border-radius: 50%; }
|
|
51
|
-
.issue-status[data-status="in_progress"] { color:
|
|
52
|
-
.issue-status[data-status="resolved"] { color:
|
|
53
|
-
.issue-status[data-status="closed"] { color: var(--text-muted); }
|
|
51
|
+
.issue-status[data-status="in_progress"]::before { color: var(--warning, var(--accent)); }
|
|
52
|
+
.issue-status[data-status="resolved"]::before { color: var(--success, var(--accent)); }
|
|
53
|
+
.issue-status[data-status="closed"]::before { color: var(--text-muted); }
|
|
54
54
|
.issue-detail { max-width: 760px; margin: 0 auto; padding: 32px 28px 52px; }
|
|
55
55
|
.issue-doc-chips { display: flex; align-items: center; flex-wrap: wrap; gap: 7px; margin-bottom: 10px; }
|
|
56
56
|
.issue-detail .issue-doc-title { margin: 0 0 10px; font-family: var(--font-display); font-size: clamp(24px, 2.8vw, 34px); letter-spacing: -.025em; line-height: 1.12; overflow-wrap: anywhere; }
|
|
@@ -508,6 +508,33 @@ pre:hover .code-copy-btn { opacity: 1; }
|
|
|
508
508
|
.clay-file-link-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
509
509
|
.clay-file-link-notice { margin-top: 5px; color: var(--text-secondary); font-size: 12px; }
|
|
510
510
|
|
|
511
|
+
.hosted-link-chip, .github-link-chip {
|
|
512
|
+
display: inline-flex;
|
|
513
|
+
align-items: center;
|
|
514
|
+
gap: 5px;
|
|
515
|
+
max-width: 100%;
|
|
516
|
+
padding: 3px 8px;
|
|
517
|
+
border: 1px solid color-mix(in srgb, var(--text) 16%, var(--border));
|
|
518
|
+
border-radius: 999px;
|
|
519
|
+
background: color-mix(in srgb, var(--text) 8%, var(--bg));
|
|
520
|
+
color: var(--text);
|
|
521
|
+
font-size: 0.92em;
|
|
522
|
+
line-height: 1.35;
|
|
523
|
+
vertical-align: baseline;
|
|
524
|
+
text-decoration: none !important;
|
|
525
|
+
transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
|
|
526
|
+
}
|
|
527
|
+
.hosted-link-chip { color: var(--text); }
|
|
528
|
+
.md-content .github-link-chip { color: var(--text); }
|
|
529
|
+
.md-content .gitlab-link-chip { color: var(--text); }
|
|
530
|
+
.hosted-link-chip:hover, .github-link-chip:hover { background: color-mix(in srgb, var(--text) 14%, var(--bg-alt)); border-color: color-mix(in srgb, var(--text) 30%, var(--border)); }
|
|
531
|
+
.hosted-link-chip:active, .github-link-chip:active { transform: translateY(1px); }
|
|
532
|
+
.hosted-link-chip:focus-visible, .github-link-chip:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
|
|
533
|
+
.github-link-icon { display: inline-flex; width: 15px; height: 15px; flex: 0 0 15px; }
|
|
534
|
+
.github-link-icon .lucide { width: 15px; height: 15px; }
|
|
535
|
+
.github-link-main { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
536
|
+
.github-link-kind { min-width: 0; max-width: 65%; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-muted); font-size: 0.88em; }
|
|
537
|
+
|
|
511
538
|
.md-content ul, .md-content ol {
|
|
512
539
|
padding-left: 24px;
|
|
513
540
|
margin: 10px 0;
|
|
@@ -519,6 +519,66 @@ button.top-bar-pill.pill-accent:hover { background: color-mix(in srgb, var(--acc
|
|
|
519
519
|
.popover-copy.copied { color: var(--success); border-color: var(--success); }
|
|
520
520
|
|
|
521
521
|
/* Skip permissions pill */
|
|
522
|
+
.session-permission-control {
|
|
523
|
+
display: inline-flex;
|
|
524
|
+
align-items: center;
|
|
525
|
+
flex: 0 1 auto;
|
|
526
|
+
flex-wrap: wrap;
|
|
527
|
+
gap: 7px;
|
|
528
|
+
max-width: 100%;
|
|
529
|
+
min-width: 0;
|
|
530
|
+
}
|
|
531
|
+
.session-permission-control.hidden { display: none; }
|
|
532
|
+
.session-permission-segmented {
|
|
533
|
+
display: inline-flex;
|
|
534
|
+
align-items: center;
|
|
535
|
+
flex: 0 0 auto;
|
|
536
|
+
gap: 2px;
|
|
537
|
+
padding: 2px;
|
|
538
|
+
border: 1px solid var(--border, rgba(255,255,255,.18));
|
|
539
|
+
border-radius: 999px;
|
|
540
|
+
background: var(--surface-raised, rgba(0,0,0,.18));
|
|
541
|
+
}
|
|
542
|
+
.session-permission-segmented button {
|
|
543
|
+
flex: 0 0 auto;
|
|
544
|
+
border: 0;
|
|
545
|
+
border-radius: 999px;
|
|
546
|
+
padding: 3px 8px;
|
|
547
|
+
color: var(--text-muted, inherit);
|
|
548
|
+
background: transparent;
|
|
549
|
+
font: inherit;
|
|
550
|
+
font-size: 11px;
|
|
551
|
+
white-space: nowrap;
|
|
552
|
+
cursor: pointer;
|
|
553
|
+
}
|
|
554
|
+
.session-permission-segmented button.active { background: var(--accent, #6f8cff); color: #fff; }
|
|
555
|
+
.session-permission-segmented button:disabled { cursor: not-allowed; opacity: .5; }
|
|
556
|
+
.session-permission-control.permission-pending .session-permission-segmented button:disabled { cursor: wait; }
|
|
557
|
+
.session-permission-status {
|
|
558
|
+
flex: 1 1 118px;
|
|
559
|
+
min-width: 0;
|
|
560
|
+
max-width: 190px;
|
|
561
|
+
overflow-wrap: anywhere;
|
|
562
|
+
white-space: normal;
|
|
563
|
+
color: var(--text-dimmer, var(--text-muted));
|
|
564
|
+
font-size: 10px;
|
|
565
|
+
line-height: 1.2;
|
|
566
|
+
}
|
|
567
|
+
.session-permission-status.hidden { display: none; }
|
|
568
|
+
.permission-runtime-fallback .session-permission-status { color: var(--warning, #d49a55); }
|
|
569
|
+
.permission-globally-forced .session-permission-status { color: var(--warning, #d49a55); }
|
|
570
|
+
.permission-label-short { display: none; }
|
|
571
|
+
@media (max-width: 760px) {
|
|
572
|
+
.permission-label-long { display: none; }
|
|
573
|
+
.permission-label-short { display: inline; }
|
|
574
|
+
.session-permission-control { column-gap: 5px; row-gap: 2px; }
|
|
575
|
+
.session-permission-segmented button { padding-inline: 7px; }
|
|
576
|
+
.session-permission-status {
|
|
577
|
+
flex-basis: 96px;
|
|
578
|
+
max-width: 170px;
|
|
579
|
+
font-size: 9px;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
522
582
|
#skip-perms-pill .lucide { width: 12px; height: 12px; flex-shrink: 0; }
|
|
523
583
|
|
|
524
584
|
/* --- Confirm modal --- */
|
package/lib/public/css/pane.css
CHANGED
|
@@ -370,13 +370,31 @@ body.pane-mode #input-area {
|
|
|
370
370
|
tie and keep the pill at its 22px title-bar size inside the 24px pane
|
|
371
371
|
header. Scope under .split-pane-header to outrank them. */
|
|
372
372
|
.split-pane-header .split-pane-full-access {
|
|
373
|
-
height:
|
|
373
|
+
height: auto;
|
|
374
374
|
align-self: center;
|
|
375
375
|
margin-left: 0;
|
|
376
|
-
padding: 0
|
|
377
|
-
gap:
|
|
376
|
+
padding: 0;
|
|
377
|
+
gap: 4px;
|
|
378
378
|
font-size: 8px;
|
|
379
379
|
letter-spacing: 0.02em;
|
|
380
|
+
flex-wrap: nowrap;
|
|
381
|
+
flex: 0 0 auto;
|
|
382
|
+
overflow: hidden;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.split-pane-header .session-permission-segmented button {
|
|
386
|
+
padding: 1px 5px;
|
|
387
|
+
font-size: 8px;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.split-pane-header .session-permission-status {
|
|
391
|
+
flex: 0 0 auto;
|
|
392
|
+
min-width: 40px;
|
|
393
|
+
max-width: 72px;
|
|
394
|
+
overflow: hidden;
|
|
395
|
+
text-overflow: ellipsis;
|
|
396
|
+
white-space: nowrap;
|
|
397
|
+
font-size: 8px;
|
|
380
398
|
}
|
|
381
399
|
|
|
382
400
|
.split-pane-header .session-full-access-track {
|
|
@@ -395,7 +413,6 @@ body.pane-mode #input-area {
|
|
|
395
413
|
|
|
396
414
|
@media (max-width: 900px) {
|
|
397
415
|
.split-pane-header .session-full-access-label { display: none; }
|
|
398
|
-
.split-pane-header .split-pane-full-access { padding-left: 3px; }
|
|
399
416
|
}
|
|
400
417
|
|
|
401
418
|
.split-pane-close {
|
|
@@ -167,6 +167,11 @@ button.pending-message-drag:active { cursor: grabbing; }
|
|
|
167
167
|
.pending-message-banner-error { color: var(--error); font-size: 10px; line-height: 1.35; }
|
|
168
168
|
.pending-message-error { margin-top: 4px; }
|
|
169
169
|
.pending-message-banner-error { padding: 0 8px 6px; }
|
|
170
|
+
.pending-message-restoring { padding: 0 8px 6px; color: var(--text-dimmer); font-size: 10px; line-height: 1.35; }
|
|
171
|
+
|
|
172
|
+
.pending-message-drag:disabled,
|
|
173
|
+
.pending-message-actions button:disabled,
|
|
174
|
+
.pending-message-resume:disabled { cursor: not-allowed; opacity: .48; }
|
|
170
175
|
|
|
171
176
|
.pending-message-drag:focus-visible,
|
|
172
177
|
.pending-message-actions button:focus-visible,
|
package/lib/public/index.html
CHANGED
|
@@ -408,9 +408,14 @@
|
|
|
408
408
|
<span class="header-title" id="header-title">Connecting...</span>
|
|
409
409
|
<button id="header-rename-btn" type="button" title="Rename session"><i data-lucide="pencil"></i></button>
|
|
410
410
|
<button id="header-info-btn" type="button" title="Session info"><i data-lucide="info"></i></button>
|
|
411
|
-
<
|
|
412
|
-
<
|
|
413
|
-
|
|
411
|
+
<div id="header-full-access-btn" class="session-permission-control hidden" role="group" aria-label="Session permission mode">
|
|
412
|
+
<div class="session-permission-segmented">
|
|
413
|
+
<button type="button" data-permission-mode="default">Ask</button>
|
|
414
|
+
<button type="button" data-permission-mode="auto">Auto</button>
|
|
415
|
+
<button type="button" data-permission-mode="bypassPermissions" aria-label="Skip permissions"><span class="permission-label-long">Skip permissions</span><span class="permission-label-short" aria-hidden="true">Skip</span></button>
|
|
416
|
+
</div>
|
|
417
|
+
<span class="session-permission-status hidden" aria-live="polite"></span>
|
|
418
|
+
</div>
|
|
414
419
|
<button type="button" id="header-tui-close-btn" class="header-tui-close-btn hidden" title="Close terminal (keeps history, resume anytime)" aria-label="Close terminal"><i data-lucide="power"></i></button>
|
|
415
420
|
<div id="mate-mobile-title" class="mate-mobile-title hidden">
|
|
416
421
|
<img id="mate-mobile-avatar" class="mate-mobile-avatar" alt="">
|
|
@@ -830,6 +835,16 @@
|
|
|
830
835
|
</div>
|
|
831
836
|
</div>
|
|
832
837
|
</div>
|
|
838
|
+
<div class="settings-field" id="ps-session-visibility-default-field" style="display:none">
|
|
839
|
+
<label class="settings-label" for="ps-session-visibility-default">New session visibility</label>
|
|
840
|
+
<div class="settings-hint">Applies to new project conversations and Issue Start work sessions. Existing sessions, hidden work, and delegated workflows keep their current visibility.</div>
|
|
841
|
+
<div class="settings-hint hidden" id="ps-session-visibility-default-inherited-hint">This worktree inherits the setting from its parent project. Change it there.</div>
|
|
842
|
+
<div class="settings-hint hidden" id="ps-session-visibility-default-non-current-hint">Open this project to change this setting.</div>
|
|
843
|
+
<select class="ps-select" id="ps-session-visibility-default">
|
|
844
|
+
<option value="private">Private</option>
|
|
845
|
+
<option value="shared">Shared with project members</option>
|
|
846
|
+
</select>
|
|
847
|
+
</div>
|
|
833
848
|
</div>
|
|
834
849
|
</div>
|
|
835
850
|
<!-- Defaults -->
|
|
@@ -20,6 +20,8 @@ import { beginDefaultAiConnection, requestDefaultAi } from './default-ai.js';
|
|
|
20
20
|
import { beginContextViewConnection, requestContextView } from './context-view-preference.js';
|
|
21
21
|
import { beginDefaultVendorConnection, requestDefaultVendor } from './default-vendor.js';
|
|
22
22
|
import { clearProjectSplitState } from './split-session-boundary.js';
|
|
23
|
+
import { clearPermissionModePending } from './permission-control.js';
|
|
24
|
+
import { clearMcpPermissionModePending } from './mcp-ui.js';
|
|
23
25
|
|
|
24
26
|
var reconnectTimer = null;
|
|
25
27
|
var reconnectDelay = 1000;
|
|
@@ -165,6 +167,8 @@ export function setStatus(status) {
|
|
|
165
167
|
} else if (status === "processing") {
|
|
166
168
|
store.set({ processing: true });
|
|
167
169
|
} else {
|
|
170
|
+
clearPermissionModePending();
|
|
171
|
+
clearMcpPermissionModePending();
|
|
168
172
|
store.set({ connected: false, processing: false });
|
|
169
173
|
}
|
|
170
174
|
}
|
|
@@ -12,7 +12,7 @@ import { saveToolState, resetToolState, restoreToolState } from './tools.js';
|
|
|
12
12
|
import { getSessionUsage, setSessionUsage, getContextData, setContextData, updateContextPanel, updateUsagePanel } from './app-panels.js';
|
|
13
13
|
import { onHistoryPrepended as onSessionSearchHistoryPrepended } from './session-search.js';
|
|
14
14
|
import { getCachedSessions } from './sidebar-sessions.js';
|
|
15
|
-
import {
|
|
15
|
+
import { bindPermissionControl, renderPermissionControl } from './permission-control.js';
|
|
16
16
|
|
|
17
17
|
// --- Module-owned state ---
|
|
18
18
|
var sessionInfoPopover = null;
|
|
@@ -24,37 +24,30 @@ export function initHeader() {
|
|
|
24
24
|
var headerInfoBtn = document.getElementById("header-info-btn");
|
|
25
25
|
var headerFullAccessBtn = document.getElementById("header-full-access-btn");
|
|
26
26
|
|
|
27
|
-
function
|
|
28
|
-
if (!headerFullAccessBtn) return;
|
|
27
|
+
function permissionControlState() {
|
|
29
28
|
var s = store.snap();
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
return {
|
|
30
|
+
projectSlug: s.currentSlug,
|
|
31
|
+
sessionId: s.activeSessionId,
|
|
32
|
+
vendor: s.currentVendor || "claude",
|
|
33
|
+
permissionMode: s.currentMode,
|
|
34
|
+
effectivePermissionMode: s.effectivePermissionMode,
|
|
35
|
+
permissionCapabilities: s.permissionCapabilities,
|
|
36
|
+
connected: s.connected,
|
|
37
|
+
globalPermissionModeForced: s.skipPermsEnabled === true,
|
|
38
|
+
visible: !s.dmMode && !s.splitPanes && s.activeSessionMode === "gui" && !!s.activeSessionId,
|
|
39
|
+
};
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (ws && ws.readyState === 1 && id) {
|
|
42
|
-
ws.send(JSON.stringify({ type: "set_session_full_access", id: id, enabled: enabled }));
|
|
43
|
-
}
|
|
42
|
+
function updateFullAccessButton() {
|
|
43
|
+
if (!headerFullAccessBtn) return;
|
|
44
|
+
renderPermissionControl(headerFullAccessBtn, permissionControlState());
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
if (headerFullAccessBtn) {
|
|
47
|
-
headerFullAccessBtn
|
|
48
|
-
if (store.get('sessionFullAccess')) {
|
|
49
|
-
sendFullAccess(false);
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
showConfirm("Skip permission prompts for this session? Tool requests will be approved automatically.", function () {
|
|
53
|
-
sendFullAccess(true);
|
|
54
|
-
}, "Skip Permissions", true);
|
|
55
|
-
});
|
|
48
|
+
bindPermissionControl(headerFullAccessBtn, permissionControlState);
|
|
56
49
|
store.subscribe(function (state, prev) {
|
|
57
|
-
if (state.
|
|
50
|
+
if (state.currentMode !== prev.currentMode || state.effectivePermissionMode !== prev.effectivePermissionMode || state.permissionCapabilities !== prev.permissionCapabilities || state.permissionModePendingByTarget !== prev.permissionModePendingByTarget || state.connected !== prev.connected || state.activeSessionId !== prev.activeSessionId || state.currentSlug !== prev.currentSlug || state.currentVendor !== prev.currentVendor ||
|
|
58
51
|
state.activeSessionMode !== prev.activeSessionMode ||
|
|
59
52
|
state.dmMode !== prev.dmMode || state.splitPanes !== prev.splitPanes ||
|
|
60
53
|
state.skipPermsEnabled !== prev.skipPermsEnabled) updateFullAccessButton();
|
|
@@ -38,6 +38,7 @@ import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handle
|
|
|
38
38
|
import { beginMarkdownTurn, finishMarkdownTurn, markdownPathFromToolInput } from './markdown-live-edit.js';
|
|
39
39
|
import { forwardPaneMarkdownPresentation } from './pane-bridge.js';
|
|
40
40
|
import { isProjectSettingsOpen, handleInstructionsRead, handleInstructionsWrite, handleProjectEnv, handleProjectEnvSaved, handleProjectSharedEnv, handleProjectSharedEnvSaved, handleProjectOwnerChanged } from './project-settings.js';
|
|
41
|
+
import { handleProjectSessionVisibilityDefault, refreshProjectSessionVisibilitySettings } from './project-session-visibility-settings.js';
|
|
41
42
|
import { updateSettingsStats, updateDaemonConfig, handleSetPinResult, handleKeepAwakeChanged, handleInheritGroupsChanged, handleAutoContinueChanged, handleRestartResult, handleShutdownResult, handleSharedEnv, handleSharedEnvSaved, handleGlobalClaudeMdRead, handleGlobalClaudeMdWrite } from './server-settings.js';
|
|
42
43
|
import { handleTermList, handleTermCreated, sendTerminalCommand, handleTermOutput, handleTermResized, handleTermExited, handleTermClosed } from './terminal.js';
|
|
43
44
|
import { attachTuiView, detachTuiView, setTuiSuspendedView, tuiHandleTermOutput, tuiHandleTermResized, tuiHandleTermExited, tuiHandleTermClosed } from './session-tui-view.js';
|
|
@@ -53,7 +54,8 @@ import { showRewindModal, onRewindComplete, setRewindMode, onRewindError, clearP
|
|
|
53
54
|
import { checkAdminAccess } from './admin.js';
|
|
54
55
|
import { mateAvatarUrl } from './avatar.js';
|
|
55
56
|
import { showImageModal, sendExtensionCommand, handleMcpToolCallMessage } from './app-misc.js';
|
|
56
|
-
import { handleMcpServersState } from './mcp-ui.js';
|
|
57
|
+
import { handleMcpServersState, handleMcpPermissionModeResult } from './mcp-ui.js';
|
|
58
|
+
import { handlePermissionModeResult } from './permission-control.js';
|
|
57
59
|
import { handleShellCommandResult } from './shell-command.js';
|
|
58
60
|
import { handleLoopRegistryUpdated, handleScheduleRunStarted, handleScheduleRunFinished, handleLoopScheduled, isSchedulerOpen, enterCraftingMode, exitCraftingMode, handleLoopRegistryFiles } from './scheduler.js';
|
|
59
61
|
import { handleScheduledTaskMessage, handleScheduledTaskSessionSwitched } from './scheduled-tasks.js';
|
|
@@ -103,6 +105,10 @@ export function processMessage(msg) {
|
|
|
103
105
|
if (handleScheduledTaskMessage(msg)) return;
|
|
104
106
|
if (handleDefaultVendorMessage(msg)) return;
|
|
105
107
|
if (handleContextViewMessage(msg)) return;
|
|
108
|
+
if (msg && msg.type === "set_project_session_visibility_default_result") {
|
|
109
|
+
handleProjectSessionVisibilityDefault(msg);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
106
112
|
if (msg && msg.type === "schedule_message_result") {
|
|
107
113
|
handleScheduleMessageResult(msg);
|
|
108
114
|
return;
|
|
@@ -369,12 +375,11 @@ export function processMessage(msg) {
|
|
|
369
375
|
if (msg.ownerLocked !== undefined) store.set({ ownerLocked: !!msg.ownerLocked });
|
|
370
376
|
if (msg.osUsers !== undefined) store.set({ isOsUsers: !!msg.osUsers });
|
|
371
377
|
if (msg.lanHost) window.__lanHost = msg.lanHost;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if (spBanner) spBanner.classList.remove("hidden");
|
|
376
|
-
}
|
|
378
|
+
store.set({ skipPermsEnabled: msg.dangerouslySkipPermissions === true });
|
|
379
|
+
var spBanner = document.getElementById("skip-perms-pill");
|
|
380
|
+
if (spBanner) spBanner.classList.toggle("hidden", msg.dangerouslySkipPermissions !== true);
|
|
377
381
|
updateProjectList(msg);
|
|
382
|
+
refreshProjectSessionVisibilitySettings(msg.projects);
|
|
378
383
|
break;
|
|
379
384
|
|
|
380
385
|
case "update_available":
|
|
@@ -549,6 +554,9 @@ export function processMessage(msg) {
|
|
|
549
554
|
}
|
|
550
555
|
}
|
|
551
556
|
if (msg.mode) _cs.currentMode = msg.mode;
|
|
557
|
+
if (Object.prototype.hasOwnProperty.call(msg, 'effectivePermissionMode')) _cs.effectivePermissionMode = msg.effectivePermissionMode;
|
|
558
|
+
if (msg.permissionCapabilities) _cs.permissionCapabilities = msg.permissionCapabilities;
|
|
559
|
+
if (msg.mcpPermissionModeOverrides) _cs.mcpPermissionModeOverrides = msg.mcpPermissionModeOverrides;
|
|
552
560
|
if (msg.effort) _cs.currentEffort = msg.effort;
|
|
553
561
|
if (msg.betas) _cs.currentBetas = msg.betas;
|
|
554
562
|
if (msg.thinking) _cs.currentThinking = msg.thinking;
|
|
@@ -817,7 +825,7 @@ export function processMessage(msg) {
|
|
|
817
825
|
var _effectiveTerminalId = (typeof msg.runtimeTerminalId === "number")
|
|
818
826
|
? msg.runtimeTerminalId
|
|
819
827
|
: (typeof msg.terminalId === "number" ? msg.terminalId : null);
|
|
820
|
-
store.set({ activeSessionId: msg.id, cliSessionId: msg.cliSessionId || null, currentModel: msg.model || "", currentEffort: msg.effort || store.get('currentEffort'), vendorCapabilities: msg.capabilities || {}, sessionIsProcessing: !!msg.isProcessing, activeSessionMode: _effectiveMode, activeTerminalId: _effectiveTerminalId, sessionHasHistory: !!msg.hasHistory, sessionVendorBound: !!msg.vendor || !!msg.hasHistory, sessionFullAccess: msg.permissionMode === "bypassPermissions", currentMode: msg.permissionMode || store.get('currentMode') });
|
|
828
|
+
store.set({ activeSessionId: msg.id, cliSessionId: msg.cliSessionId || null, currentModel: msg.model || "", currentEffort: msg.effort || store.get('currentEffort'), vendorCapabilities: msg.capabilities || {}, sessionIsProcessing: !!msg.isProcessing, activeSessionMode: _effectiveMode, activeTerminalId: _effectiveTerminalId, sessionHasHistory: !!msg.hasHistory, sessionVendorBound: !!msg.vendor || !!msg.hasHistory, sessionFullAccess: msg.permissionMode === "bypassPermissions", currentMode: msg.permissionMode || store.get('currentMode'), effectivePermissionMode: msg.effectivePermissionMode || null, permissionCapabilities: msg.permissionCapabilities || { auto: false, mcpOverride: false }, mcpPermissionModeOverrides: msg.mcpPermissionModeOverrides || {} });
|
|
821
829
|
handleScheduledTaskSessionSwitched(msg);
|
|
822
830
|
requestLoopInterviewState();
|
|
823
831
|
replayPendingMessages(msg.id);
|
|
@@ -1592,6 +1600,14 @@ export function processMessage(msg) {
|
|
|
1592
1600
|
handleMcpServersState(msg);
|
|
1593
1601
|
break;
|
|
1594
1602
|
|
|
1603
|
+
case "mcp_permission_mode_result":
|
|
1604
|
+
handleMcpPermissionModeResult(msg);
|
|
1605
|
+
break;
|
|
1606
|
+
|
|
1607
|
+
case "permission_mode_result":
|
|
1608
|
+
handlePermissionModeResult(msg);
|
|
1609
|
+
break;
|
|
1610
|
+
|
|
1595
1611
|
case "term_created":
|
|
1596
1612
|
// Login terminals never come through here: they are created by the
|
|
1597
1613
|
// server-owned flow and announced with vendor_login_ready instead.
|
|
@@ -1735,6 +1751,7 @@ export function processMessage(msg) {
|
|
|
1735
1751
|
|
|
1736
1752
|
case "projects_updated":
|
|
1737
1753
|
updateProjectList(msg);
|
|
1754
|
+
refreshProjectSessionVisibilitySettings(msg.projects);
|
|
1738
1755
|
renderUserStrip();
|
|
1739
1756
|
break;
|
|
1740
1757
|
|
|
@@ -8,6 +8,7 @@ import { getWs } from './ws-ref.js';
|
|
|
8
8
|
import { getContextView as getStoredContextView, getEffectiveContextView, setContextView as setStoredContextView } from './context-view-preference.js';
|
|
9
9
|
import { reportPaneContext } from './pane-bridge.js';
|
|
10
10
|
import { setupModelPicker, renderModelPicker, requestVendorModels, prepareModelPickerOpen, modelDisplayName } from './model-picker.js';
|
|
11
|
+
import { sendPermissionMode } from './permission-control.js';
|
|
11
12
|
|
|
12
13
|
// --- Module-owned state (not in store) ---
|
|
13
14
|
var sessionUsage = { cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, turns: 0 };
|
|
@@ -85,11 +86,12 @@ var contextMiniLabel = null;
|
|
|
85
86
|
|
|
86
87
|
// --- Constants ---
|
|
87
88
|
var MODE_OPTIONS = [
|
|
88
|
-
{ value: "default", label: "
|
|
89
|
+
{ value: "default", label: "Ask" },
|
|
89
90
|
{ value: "plan", label: "Plan" },
|
|
90
91
|
{ value: "acceptEdits", label: "Auto-accept edits" },
|
|
91
92
|
];
|
|
92
|
-
var MODE_FULL_AUTO = { value: "
|
|
93
|
+
var MODE_FULL_AUTO = { value: "auto", label: "Auto" };
|
|
94
|
+
var MODE_SKIP = { value: "bypassPermissions", label: "Skip permissions" };
|
|
93
95
|
var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
94
96
|
var EFFORT_LEVELS_BY_VENDOR = {
|
|
95
97
|
claude: ["low", "medium", "high", "xhigh", "max"],
|
|
@@ -142,7 +144,8 @@ function modeDisplayName(value) {
|
|
|
142
144
|
for (var i = 0; i < MODE_OPTIONS.length; i++) {
|
|
143
145
|
if (MODE_OPTIONS[i].value === value) return MODE_OPTIONS[i].label;
|
|
144
146
|
}
|
|
145
|
-
if (value === "
|
|
147
|
+
if (value === "auto") return "Auto";
|
|
148
|
+
if (value === "bypassPermissions") return "Skip permissions";
|
|
146
149
|
if (value === "dontAsk") return "Don\u2019t ask";
|
|
147
150
|
return value;
|
|
148
151
|
}
|
|
@@ -196,22 +199,29 @@ function rebuildModeList() {
|
|
|
196
199
|
if (!configModeList) return;
|
|
197
200
|
configModeList.innerHTML = "";
|
|
198
201
|
var options = MODE_OPTIONS.slice();
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
options.push(MODE_FULL_AUTO);
|
|
203
|
+
options.push(MODE_SKIP);
|
|
204
|
+
var autoSupported = store.get('permissionCapabilities').auto === true;
|
|
205
|
+
var globallyForced = store.get('skipPermsEnabled') === true;
|
|
202
206
|
for (var i = 0; i < options.length; i++) {
|
|
203
207
|
var opt = options[i];
|
|
204
208
|
var btn = document.createElement("button");
|
|
205
209
|
btn.className = "config-radio-item";
|
|
206
|
-
if (opt.value === store.get('currentMode')) btn.classList.add("active");
|
|
210
|
+
if (opt.value === (globallyForced ? "bypassPermissions" : store.get('currentMode'))) btn.classList.add("active");
|
|
207
211
|
btn.dataset.mode = opt.value;
|
|
208
212
|
btn.textContent = opt.label;
|
|
213
|
+
if (opt.value === "auto" && !autoSupported) {
|
|
214
|
+
btn.disabled = true;
|
|
215
|
+
btn.title = "Auto permissions require a Claude session.";
|
|
216
|
+
btn.setAttribute("aria-description", "Unavailable because this is not a Claude session.");
|
|
217
|
+
}
|
|
218
|
+
if (globallyForced) {
|
|
219
|
+
btn.disabled = true;
|
|
220
|
+
btn.title = "Skip permissions is forced by the server setting.";
|
|
221
|
+
}
|
|
209
222
|
btn.addEventListener("click", function () {
|
|
210
223
|
var mode = this.dataset.mode;
|
|
211
|
-
|
|
212
|
-
if (ws && ws.readyState === 1) {
|
|
213
|
-
ws.send(JSON.stringify({ type: "set_permission_mode", mode: mode }));
|
|
214
|
-
}
|
|
224
|
+
sendPermissionMode(store.get('currentSlug'), store.get('activeSessionId'), mode);
|
|
215
225
|
configPopover.classList.add("hidden");
|
|
216
226
|
configChip.classList.remove("active");
|
|
217
227
|
});
|
|
@@ -420,6 +430,7 @@ export function initPanels() {
|
|
|
420
430
|
state.codexApproval !== prev.codexApproval ||
|
|
421
431
|
state.codexSandbox !== prev.codexSandbox ||
|
|
422
432
|
state.codexWebSearch !== prev.codexWebSearch ||
|
|
433
|
+
state.skipPermsEnabled !== prev.skipPermsEnabled ||
|
|
423
434
|
state.sessionHasHistory !== prev.sessionHasHistory ||
|
|
424
435
|
state.activeSessionMode !== prev.activeSessionMode) {
|
|
425
436
|
updateConfigChip();
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { escapeHtml } from './utils.js';
|
|
4
4
|
import { refreshIcons } from './icons.js';
|
|
5
5
|
import { store } from './store.js';
|
|
6
|
+
import { forwardPaneFileReference } from './pane-bridge.js';
|
|
6
7
|
|
|
7
8
|
var openFileHandler = null;
|
|
8
9
|
var delegatedBinding = false;
|
|
@@ -99,6 +100,12 @@ function bindDelegatedHandler() {
|
|
|
99
100
|
document.addEventListener("click", function (event) {
|
|
100
101
|
var button = event.target && event.target.closest ? event.target.closest(".clay-file-link") : null;
|
|
101
102
|
if (!button) return;
|
|
103
|
+
if (store.get('paneMode') && forwardPaneFileReference(button.dataset.filePath, {
|
|
104
|
+
line: button.dataset.fileLine ? Number(button.dataset.fileLine) : null,
|
|
105
|
+
column: button.dataset.fileColumn ? Number(button.dataset.fileColumn) : null,
|
|
106
|
+
projectSlug: button.dataset.fileProjectSlug || null,
|
|
107
|
+
sessionId: button.dataset.fileSessionId || null
|
|
108
|
+
})) return;
|
|
102
109
|
if (!openFileHandler) { showUnavailable(button); return; }
|
|
103
110
|
var result = openFileHandler(button.dataset.filePath, {
|
|
104
111
|
line: button.dataset.fileLine ? Number(button.dataset.fileLine) : null,
|
|
@@ -31,6 +31,7 @@ function createIssueLink(ref, labelText) {
|
|
|
31
31
|
var button = document.createElement("button");
|
|
32
32
|
button.type = "button";
|
|
33
33
|
button.className = "clayos-issue-link";
|
|
34
|
+
button.classList.add("clayos-record-link");
|
|
34
35
|
button.contentEditable = "false";
|
|
35
36
|
button.dataset.issueRef = ref;
|
|
36
37
|
button.setAttribute("aria-label", "Open Project Issue" + (labelText ? " " + labelText : ""));
|