iris-chatbot 5.1.0 → 5.3.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.
- package/package.json +1 -1
- package/template/eslint.config.mjs +1 -0
- package/template/next-env.d.ts +1 -1
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/src/app/api/chat/route.ts +772 -84
- package/template/src/app/api/contacts/search/route.ts +71 -0
- package/template/src/app/api/tool-approval/route.ts +30 -2
- package/template/src/app/globals.css +230 -6
- package/template/src/components/ChatView.tsx +247 -27
- package/template/src/components/Composer.tsx +11 -8
- package/template/src/components/MessageCard.tsx +550 -30
- package/template/src/components/SettingsModal.tsx +7 -0
- package/template/src/lib/data.ts +5 -0
- package/template/src/lib/tooling/approvals.ts +24 -9
- package/template/src/lib/tooling/tools/communication.ts +178 -31
- package/template/src/lib/types.ts +12 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
autocompleteContacts,
|
|
3
|
+
type ContactAutocompleteMode,
|
|
4
|
+
} from "../../../../lib/tooling/tools/communication";
|
|
5
|
+
|
|
6
|
+
export const runtime = "nodejs";
|
|
7
|
+
export const dynamic = "force-dynamic";
|
|
8
|
+
|
|
9
|
+
function isContactsPermissionError(message: string): boolean {
|
|
10
|
+
const normalized = message.toLowerCase();
|
|
11
|
+
return (
|
|
12
|
+
normalized.includes("not authorized") ||
|
|
13
|
+
normalized.includes("not permitted") ||
|
|
14
|
+
normalized.includes("permission") ||
|
|
15
|
+
normalized.includes("operation not permitted")
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function POST(request: Request) {
|
|
20
|
+
try {
|
|
21
|
+
const body = (await request.json()) as {
|
|
22
|
+
query?: unknown;
|
|
23
|
+
mode?: unknown;
|
|
24
|
+
};
|
|
25
|
+
const query = typeof body.query === "string" ? body.query.trim() : "";
|
|
26
|
+
const mode: ContactAutocompleteMode =
|
|
27
|
+
body.mode === "email" ? "email" : "message";
|
|
28
|
+
|
|
29
|
+
if (!query) {
|
|
30
|
+
return new Response(
|
|
31
|
+
JSON.stringify({ ok: true, suggestions: [] }),
|
|
32
|
+
{ headers: { "Content-Type": "application/json" } },
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const suggestions = await autocompleteContacts({
|
|
37
|
+
query,
|
|
38
|
+
mode,
|
|
39
|
+
signal: request.signal,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return new Response(
|
|
43
|
+
JSON.stringify({ ok: true, suggestions }),
|
|
44
|
+
{ headers: { "Content-Type": "application/json" } },
|
|
45
|
+
);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const message = error instanceof Error ? error.message : "Contacts lookup failed.";
|
|
48
|
+
if (isContactsPermissionError(message)) {
|
|
49
|
+
return new Response(
|
|
50
|
+
JSON.stringify({
|
|
51
|
+
ok: false,
|
|
52
|
+
permissionRequired: true,
|
|
53
|
+
error:
|
|
54
|
+
"Contacts access is required for suggestions. macOS should prompt the app running this server (Terminal/VS Code/Cursor).",
|
|
55
|
+
}),
|
|
56
|
+
{
|
|
57
|
+
status: 403,
|
|
58
|
+
headers: { "Content-Type": "application/json" },
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return new Response(
|
|
63
|
+
JSON.stringify({ ok: false, error: message }),
|
|
64
|
+
{
|
|
65
|
+
status: 500,
|
|
66
|
+
headers: { "Content-Type": "application/json" },
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
@@ -8,6 +8,9 @@ export async function POST(request: Request) {
|
|
|
8
8
|
const body = (await request.json()) as {
|
|
9
9
|
approvalId?: unknown;
|
|
10
10
|
decision?: unknown;
|
|
11
|
+
args?: unknown;
|
|
12
|
+
source?: unknown;
|
|
13
|
+
reasonCode?: unknown;
|
|
11
14
|
};
|
|
12
15
|
|
|
13
16
|
if (typeof body.approvalId !== "string" || !body.approvalId.trim()) {
|
|
@@ -17,7 +20,7 @@ export async function POST(request: Request) {
|
|
|
17
20
|
});
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
if (body.decision !== "approve" && body.decision !== "deny") {
|
|
23
|
+
if (body.decision !== "approve" && body.decision !== "deny" && body.decision !== "supersede") {
|
|
21
24
|
return new Response(JSON.stringify({ ok: false, error: "Invalid decision." }), {
|
|
22
25
|
status: 400,
|
|
23
26
|
headers: { "Content-Type": "application/json" },
|
|
@@ -25,8 +28,33 @@ export async function POST(request: Request) {
|
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
const approvalId = body.approvalId.trim();
|
|
31
|
+
const args =
|
|
32
|
+
body.args && typeof body.args === "object" && !Array.isArray(body.args)
|
|
33
|
+
? (body.args as Record<string, unknown>)
|
|
34
|
+
: undefined;
|
|
35
|
+
if (body.args !== undefined && !args) {
|
|
36
|
+
return new Response(JSON.stringify({ ok: false, error: "Invalid args payload." }), {
|
|
37
|
+
status: 400,
|
|
38
|
+
headers: { "Content-Type": "application/json" },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const source =
|
|
43
|
+
body.source === "user" || body.source === "system"
|
|
44
|
+
? body.source
|
|
45
|
+
: "user";
|
|
46
|
+
const reasonCode =
|
|
47
|
+
body.reasonCode === "user_cancel" ||
|
|
48
|
+
body.reasonCode === "internal_replace" ||
|
|
49
|
+
body.reasonCode === "timeout" ||
|
|
50
|
+
body.reasonCode === "other"
|
|
51
|
+
? body.reasonCode
|
|
52
|
+
: undefined;
|
|
28
53
|
|
|
29
|
-
const resolved = resolveApprovalDecision(approvalId, body.decision
|
|
54
|
+
const resolved = resolveApprovalDecision(approvalId, body.decision, args, {
|
|
55
|
+
source,
|
|
56
|
+
reasonCode,
|
|
57
|
+
});
|
|
30
58
|
if (!resolved) {
|
|
31
59
|
return new Response(JSON.stringify({ ok: false, error: "Unable to resolve approval." }), {
|
|
32
60
|
status: 404,
|
|
@@ -172,13 +172,12 @@ button:focus-visible {
|
|
|
172
172
|
top: 2px;
|
|
173
173
|
right: 0;
|
|
174
174
|
opacity: 0;
|
|
175
|
-
pointer-events:
|
|
175
|
+
pointer-events: auto;
|
|
176
176
|
transition: opacity 0.2s ease;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
.assistant-
|
|
179
|
+
.assistant-collapse-row:hover {
|
|
180
180
|
opacity: 1;
|
|
181
|
-
pointer-events: auto;
|
|
182
181
|
}
|
|
183
182
|
|
|
184
183
|
.assistant-collapse-toggle {
|
|
@@ -465,6 +464,9 @@ button:focus-visible {
|
|
|
465
464
|
text-align: left;
|
|
466
465
|
border-right: 1px solid var(--border-strong);
|
|
467
466
|
border-bottom: 1px solid var(--border-strong);
|
|
467
|
+
/* Keep words intact: wrap at spaces; only break long words when necessary */
|
|
468
|
+
overflow-wrap: break-word;
|
|
469
|
+
word-break: normal;
|
|
468
470
|
}
|
|
469
471
|
|
|
470
472
|
.message-content tr> :last-child {
|
|
@@ -502,8 +504,8 @@ button:focus-visible {
|
|
|
502
504
|
}
|
|
503
505
|
|
|
504
506
|
.message-content strong {
|
|
505
|
-
font-weight:
|
|
506
|
-
color:
|
|
507
|
+
font-weight: 600;
|
|
508
|
+
color: inherit;
|
|
507
509
|
}
|
|
508
510
|
|
|
509
511
|
.message-content em {
|
|
@@ -614,6 +616,220 @@ button:focus-visible {
|
|
|
614
616
|
color: #111111;
|
|
615
617
|
}
|
|
616
618
|
|
|
619
|
+
.draft-approval-card {
|
|
620
|
+
border: 1px solid var(--border);
|
|
621
|
+
border-radius: 14px;
|
|
622
|
+
background: var(--panel-2);
|
|
623
|
+
padding: 10px;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.draft-approval-header {
|
|
627
|
+
display: flex;
|
|
628
|
+
align-items: center;
|
|
629
|
+
justify-content: space-between;
|
|
630
|
+
gap: 8px;
|
|
631
|
+
margin-bottom: 10px;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.draft-approval-title {
|
|
635
|
+
font-size: 13px;
|
|
636
|
+
font-weight: 600;
|
|
637
|
+
color: var(--text-secondary);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.draft-approval-actions {
|
|
641
|
+
display: flex;
|
|
642
|
+
align-items: center;
|
|
643
|
+
gap: 6px;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.draft-approval-icon-btn {
|
|
647
|
+
height: 28px;
|
|
648
|
+
width: 28px;
|
|
649
|
+
border-radius: 999px;
|
|
650
|
+
border: 1px solid var(--border);
|
|
651
|
+
background: var(--panel);
|
|
652
|
+
color: var(--text-secondary);
|
|
653
|
+
display: inline-flex;
|
|
654
|
+
align-items: center;
|
|
655
|
+
justify-content: center;
|
|
656
|
+
transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.draft-approval-icon-btn:hover {
|
|
660
|
+
border-color: var(--border-strong);
|
|
661
|
+
color: var(--text-primary);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.draft-approval-icon-btn.primary {
|
|
665
|
+
background: #ffffff;
|
|
666
|
+
color: #101010;
|
|
667
|
+
border-color: transparent;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
[data-theme="light"] .draft-approval-icon-btn.primary {
|
|
671
|
+
background: #161616;
|
|
672
|
+
color: #ffffff;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.draft-approval-icon-btn:disabled {
|
|
676
|
+
opacity: 0.55;
|
|
677
|
+
cursor: not-allowed;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.draft-approval-field {
|
|
681
|
+
display: flex;
|
|
682
|
+
flex-direction: column;
|
|
683
|
+
gap: 6px;
|
|
684
|
+
margin-bottom: 10px;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.draft-approval-label {
|
|
688
|
+
font-size: 11px;
|
|
689
|
+
text-transform: uppercase;
|
|
690
|
+
letter-spacing: 0.08em;
|
|
691
|
+
color: var(--text-muted);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
.draft-recipient-input-wrap {
|
|
695
|
+
min-height: 38px;
|
|
696
|
+
border: 1px solid var(--border);
|
|
697
|
+
border-radius: 10px;
|
|
698
|
+
background: var(--panel);
|
|
699
|
+
padding: 5px 6px;
|
|
700
|
+
display: flex;
|
|
701
|
+
align-items: center;
|
|
702
|
+
flex-wrap: wrap;
|
|
703
|
+
gap: 5px;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
.draft-recipient-field-stack {
|
|
707
|
+
position: relative;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.draft-recipient-chip {
|
|
711
|
+
max-width: 100%;
|
|
712
|
+
border-radius: 999px;
|
|
713
|
+
border: 1px solid var(--border);
|
|
714
|
+
background: var(--panel-2);
|
|
715
|
+
color: var(--text-secondary);
|
|
716
|
+
font-size: 11px;
|
|
717
|
+
padding: 4px 8px;
|
|
718
|
+
display: inline-flex;
|
|
719
|
+
align-items: center;
|
|
720
|
+
gap: 4px;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.draft-recipient-chip-remove {
|
|
724
|
+
border: none;
|
|
725
|
+
background: transparent;
|
|
726
|
+
color: var(--text-muted);
|
|
727
|
+
display: inline-flex;
|
|
728
|
+
align-items: center;
|
|
729
|
+
justify-content: center;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.draft-recipient-chip-remove:hover {
|
|
733
|
+
color: var(--text-primary);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.draft-recipient-input {
|
|
737
|
+
border: none;
|
|
738
|
+
background: transparent;
|
|
739
|
+
color: var(--text-primary);
|
|
740
|
+
font-size: 12px;
|
|
741
|
+
flex: 1;
|
|
742
|
+
min-width: 120px;
|
|
743
|
+
padding: 4px 6px;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.draft-recipient-input:focus {
|
|
747
|
+
outline: none;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.draft-contact-suggestions {
|
|
751
|
+
margin-top: 4px;
|
|
752
|
+
border: 1px solid var(--border);
|
|
753
|
+
border-radius: 10px;
|
|
754
|
+
background: var(--panel);
|
|
755
|
+
max-height: 180px;
|
|
756
|
+
overflow-y: auto;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
.draft-contact-suggestion-item {
|
|
760
|
+
width: 100%;
|
|
761
|
+
border: none;
|
|
762
|
+
border-bottom: 1px solid var(--border);
|
|
763
|
+
background: transparent;
|
|
764
|
+
text-align: left;
|
|
765
|
+
padding: 8px 10px;
|
|
766
|
+
display: flex;
|
|
767
|
+
flex-direction: column;
|
|
768
|
+
gap: 2px;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.draft-contact-suggestion-item:last-child {
|
|
772
|
+
border-bottom: none;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.draft-contact-suggestion-item:hover {
|
|
776
|
+
background: var(--panel-2);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
.draft-contact-suggestion-name {
|
|
780
|
+
font-size: 12px;
|
|
781
|
+
color: var(--text-primary);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.draft-contact-suggestion-detail {
|
|
785
|
+
font-size: 11px;
|
|
786
|
+
color: var(--text-muted);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.draft-contact-suggestion-hint {
|
|
790
|
+
padding: 8px 10px;
|
|
791
|
+
font-size: 11px;
|
|
792
|
+
color: var(--text-muted);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.draft-approval-text-input,
|
|
796
|
+
.draft-approval-textarea {
|
|
797
|
+
width: 100%;
|
|
798
|
+
border: 1px solid var(--border);
|
|
799
|
+
border-radius: 10px;
|
|
800
|
+
background: var(--panel);
|
|
801
|
+
color: var(--text-primary);
|
|
802
|
+
font-size: 13px;
|
|
803
|
+
line-height: 1.5;
|
|
804
|
+
padding: 8px 10px;
|
|
805
|
+
resize: vertical;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
.draft-approval-text-input:focus,
|
|
809
|
+
.draft-approval-textarea:focus {
|
|
810
|
+
outline: none;
|
|
811
|
+
border-color: var(--border-strong);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
.draft-approval-footer {
|
|
815
|
+
display: flex;
|
|
816
|
+
justify-content: flex-end;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
.draft-approval-cancel {
|
|
820
|
+
border-radius: 999px;
|
|
821
|
+
border: 1px solid var(--border);
|
|
822
|
+
background: var(--panel);
|
|
823
|
+
color: var(--text-secondary);
|
|
824
|
+
font-size: 11px;
|
|
825
|
+
padding: 4px 10px;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.draft-approval-cancel:hover {
|
|
829
|
+
border-color: var(--border-strong);
|
|
830
|
+
color: var(--text-primary);
|
|
831
|
+
}
|
|
832
|
+
|
|
617
833
|
.message-content hr {
|
|
618
834
|
border: 0;
|
|
619
835
|
border-top: 1px solid var(--border);
|
|
@@ -871,6 +1087,14 @@ button:focus-visible {
|
|
|
871
1087
|
.message-card.user {
|
|
872
1088
|
max-width: 92%;
|
|
873
1089
|
}
|
|
1090
|
+
|
|
1091
|
+
.draft-approval-header {
|
|
1092
|
+
align-items: flex-start;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
.draft-recipient-input {
|
|
1096
|
+
min-width: 92px;
|
|
1097
|
+
}
|
|
874
1098
|
}
|
|
875
1099
|
|
|
876
1100
|
.model-menu {
|
|
@@ -1101,4 +1325,4 @@ button:focus-visible {
|
|
|
1101
1325
|
|
|
1102
1326
|
[data-theme="light"] .quoted-context-dismiss:hover {
|
|
1103
1327
|
background: rgba(0, 0, 0, 0.06);
|
|
1104
|
-
}
|
|
1328
|
+
}
|