@saltcorn/agents 0.8.3 → 0.8.5
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/agent-view.js +50 -302
- package/agents.css +401 -0
- package/common.js +12 -3
- package/package.json +1 -1
- package/skills/Fetch.js +100 -5
- package/skills/LongTermMemory.js +229 -0
package/agent-view.js
CHANGED
|
@@ -49,11 +49,13 @@ const {
|
|
|
49
49
|
get_initial_interactions,
|
|
50
50
|
get_skill_instances,
|
|
51
51
|
saveInteractions,
|
|
52
|
+
extractText,
|
|
52
53
|
} = require("./common");
|
|
53
54
|
const MarkdownIt = require("markdown-it"),
|
|
54
55
|
md = new MarkdownIt({ html: true, breaks: true, linkify: true });
|
|
55
56
|
const { isWeb, escapeHtml } = require("@saltcorn/data/utils");
|
|
56
57
|
const path = require("path");
|
|
58
|
+
const fs = require("fs");
|
|
57
59
|
|
|
58
60
|
const configuration_workflow = (req) =>
|
|
59
61
|
new Workflow({
|
|
@@ -259,6 +261,11 @@ const realTimeCollabScript = (viewname, rndid, layout) => {
|
|
|
259
261
|
);
|
|
260
262
|
};
|
|
261
263
|
|
|
264
|
+
const agents_css = fs.readFileSync(
|
|
265
|
+
path.resolve(__dirname, "agents.css"),
|
|
266
|
+
"utf8",
|
|
267
|
+
);
|
|
268
|
+
|
|
262
269
|
const run = async (
|
|
263
270
|
table_id,
|
|
264
271
|
viewname,
|
|
@@ -300,12 +307,13 @@ const run = async (
|
|
|
300
307
|
},
|
|
301
308
|
{ orderBy: "started_at", orderDesc: true, limit: 30 },
|
|
302
309
|
)
|
|
303
|
-
).filter((r) => r.context.interactions)
|
|
310
|
+
).filter((r) => r.context.interactions || r.context.html_interactions)
|
|
304
311
|
: null;
|
|
305
312
|
|
|
306
313
|
const cfgMsg = incompleteCfgMsg();
|
|
307
314
|
if (cfgMsg) return cfgMsg;
|
|
308
315
|
let runInteractions = "";
|
|
316
|
+
let hasInputForm = true;
|
|
309
317
|
|
|
310
318
|
const initial_q = state.run_id ? undefined : state._q;
|
|
311
319
|
if (state.run_id) {
|
|
@@ -319,6 +327,9 @@ const run = async (
|
|
|
319
327
|
const interactMarkups = [];
|
|
320
328
|
if (run.context.html_interactions) {
|
|
321
329
|
interactMarkups.push(...run.context.html_interactions);
|
|
330
|
+
// no input if interactions deleted
|
|
331
|
+
if (!run.context.interactions && run.context.html_interactions.length)
|
|
332
|
+
hasInputForm = false;
|
|
322
333
|
} else
|
|
323
334
|
for (const interact of run.context.interactions) {
|
|
324
335
|
//legacy
|
|
@@ -590,11 +601,11 @@ const run = async (
|
|
|
590
601
|
),
|
|
591
602
|
prevRuns.map((run) => {
|
|
592
603
|
const isActive = state.run_id && +state.run_id === run.id;
|
|
593
|
-
const
|
|
594
|
-
run.context.interactions
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
);
|
|
604
|
+
const previewHtml =
|
|
605
|
+
(run.context.interactions || []).find(
|
|
606
|
+
(ix) => typeof ix?.content === "string",
|
|
607
|
+
)?.content || extractText(run.context.html_interactions[0] || "");
|
|
608
|
+
const preview = escapeHtml(previewHtml?.substring?.(0, 80));
|
|
598
609
|
return isModernSidebar
|
|
599
610
|
? div(
|
|
600
611
|
{
|
|
@@ -655,301 +666,8 @@ const run = async (
|
|
|
655
666
|
),
|
|
656
667
|
div({ id: "copilotinteractions" }, runInteractions),
|
|
657
668
|
stream ? div({ class: "next_response_scratch" }) : "",
|
|
658
|
-
input_form,
|
|
659
|
-
style(
|
|
660
|
-
`div.interaction-segment:not(:first-child) {border-top: 1px solid #e7e7e7; }
|
|
661
|
-
div.interaction-segment {padding-top: 5px;padding-bottom: 5px;}
|
|
662
|
-
div.interaction-segment p {margin-bottom: 0px;}
|
|
663
|
-
div.interaction-segment div.card {margin-top: 0.5rem;}
|
|
664
|
-
div.interaction-segment.to-right {
|
|
665
|
-
display: flex;
|
|
666
|
-
flex-direction: row-reverse;
|
|
667
|
-
}
|
|
668
|
-
div.interaction-segment.to-right div.badgewrap {
|
|
669
|
-
display: flex;
|
|
670
|
-
flex-direction: row-reverse;
|
|
671
|
-
}
|
|
672
|
-
div.prevcopilotrun:hover {cursor: pointer; background-color: var(--tblr-secondary-bg-subtle, var(--bs-secondary-bg-subtle, gray));}
|
|
673
|
-
div.prevcopilotrun i.fa-trash-alt {display: none;}
|
|
674
|
-
div.prevcopilotrun:hover i.fa-trash-alt {display: block;}
|
|
675
|
-
.copilot-entry .submit-button:hover { cursor: pointer}
|
|
676
|
-
.copilot-entry span.attach_agent_image_wrap i:hover { cursor: pointer}
|
|
677
|
-
|
|
678
|
-
.copilot-entry .submit-button {
|
|
679
|
-
position: relative;
|
|
680
|
-
top: -1.8rem;
|
|
681
|
-
left: 0.1rem;
|
|
682
|
-
}
|
|
683
|
-
.copilot-entry #audioinputicon {
|
|
684
|
-
position: relative;
|
|
685
|
-
top: -1.8rem;
|
|
686
|
-
right: 0.7rem;
|
|
687
|
-
cursor: pointer;
|
|
688
|
-
float: right;
|
|
689
|
-
}
|
|
690
|
-
.copilot-entry .debugicon {
|
|
691
|
-
position: relative;
|
|
692
|
-
top: -1.8rem;
|
|
693
|
-
left: 0.1rem;
|
|
694
|
-
cursor: pointer;
|
|
695
|
-
}
|
|
696
|
-
.copilot-entry .cancelbtn {
|
|
697
|
-
position: relative;
|
|
698
|
-
top: -1.8rem;
|
|
699
|
-
left: 0.1rem;
|
|
700
|
-
cursor: pointer;
|
|
701
|
-
}
|
|
702
|
-
.copilot-entry .skill-form-widget {
|
|
703
|
-
position: relative;
|
|
704
|
-
top: -2rem;
|
|
705
|
-
left: 0.4rem;
|
|
706
|
-
display: inline;
|
|
707
|
-
}
|
|
708
|
-
.session-open-sessions, .open-prev-runs {
|
|
709
|
-
cursor: pointer;
|
|
710
|
-
}
|
|
711
|
-
.copilot-entry span.attach_agent_image_wrap {
|
|
712
|
-
position: relative;
|
|
713
|
-
top: -1.8rem;
|
|
714
|
-
left: 0.2rem;
|
|
715
|
-
}
|
|
716
|
-
.copilot-entry.dragover {
|
|
717
|
-
outline: 2px dashed var(--tblr-primary, #0054a6);
|
|
718
|
-
outline-offset: -2px;
|
|
719
|
-
background: var(--tblr-primary-bg-subtle, rgba(0, 84, 166, 0.05));
|
|
720
|
-
border-radius: 0.25rem;
|
|
721
|
-
}
|
|
722
|
-
.copilot-entry .explainer {
|
|
723
|
-
position: relative;
|
|
724
|
-
top: -1.2rem;
|
|
725
|
-
display: block;
|
|
726
|
-
}
|
|
727
|
-
.col-0 {
|
|
728
|
-
width: 0%
|
|
729
|
-
}
|
|
730
|
-
.copilot-entry {margin-bottom: -1.25rem; margin-top: 1rem;}
|
|
731
|
-
p.prevrun_content {
|
|
732
|
-
white-space: nowrap;
|
|
733
|
-
overflow: hidden;
|
|
734
|
-
margin-bottom: 0px;
|
|
735
|
-
display: block;
|
|
736
|
-
text-overflow: ellipsis;}
|
|
737
|
-
/* Typing / Waiting Indicator */
|
|
738
|
-
.agent-waiting-indicator { display:flex; align-items:center; padding:0.75rem 1rem; }
|
|
739
|
-
.typing-dots { display:flex; gap:4px; align-items:center; }
|
|
740
|
-
.typing-dots span { width:8px; height:8px; border-radius:50%; background:#6c757d; animation:typingBounce 1.4s infinite ease-in-out both; }
|
|
741
|
-
.typing-dots span:nth-child(1) { animation-delay:-0.32s; }
|
|
742
|
-
.typing-dots span:nth-child(2) { animation-delay:-0.16s; }
|
|
743
|
-
.typing-dots span:nth-child(3) { animation-delay:0s; }
|
|
744
|
-
@keyframes typingBounce { 0%,80%,100%{transform:scale(.6);opacity:.4} 40%{transform:scale(1);opacity:1} }
|
|
745
|
-
/* Modern Chat Layout */
|
|
746
|
-
.modern-chat-layout {
|
|
747
|
-
display: flex;
|
|
748
|
-
flex-direction: column;
|
|
749
|
-
height: 100%;
|
|
750
|
-
}
|
|
751
|
-
.modern-chat-layout #copilotinteractions {
|
|
752
|
-
max-height: 70vh;
|
|
753
|
-
overflow-y: auto;
|
|
754
|
-
padding: 1rem;
|
|
755
|
-
display: flex;
|
|
756
|
-
flex-direction: column;
|
|
757
|
-
gap: 0.75rem;
|
|
758
|
-
}
|
|
759
|
-
.modern-chat-layout .chat-message {
|
|
760
|
-
display: flex;
|
|
761
|
-
gap: 0.5rem;
|
|
762
|
-
max-width: 85%;
|
|
763
|
-
align-items: flex-start;
|
|
764
|
-
}
|
|
765
|
-
.modern-chat-layout .chat-message.chat-user {
|
|
766
|
-
align-self: flex-end;
|
|
767
|
-
flex-direction: row-reverse;
|
|
768
|
-
}
|
|
769
|
-
.modern-chat-layout .chat-message.chat-assistant {
|
|
770
|
-
align-self: flex-start;
|
|
771
|
-
}
|
|
772
|
-
.modern-chat-layout .chat-avatar {
|
|
773
|
-
width: 2rem;
|
|
774
|
-
height: 2rem;
|
|
775
|
-
border-radius: 50%;
|
|
776
|
-
display: flex;
|
|
777
|
-
align-items: center;
|
|
778
|
-
justify-content: center;
|
|
779
|
-
flex-shrink: 0;
|
|
780
|
-
font-size: 0.85rem;
|
|
781
|
-
background: var(--tblr-secondary-bg-subtle, var(--bs-secondary-bg-subtle, #e9ecef));
|
|
782
|
-
color: var(--tblr-secondary-color, var(--bs-secondary-color, #6c757d));
|
|
783
|
-
}
|
|
784
|
-
.modern-chat-layout .chat-user .chat-avatar {
|
|
785
|
-
background: #0d6efd;
|
|
786
|
-
color: #fff;
|
|
787
|
-
}
|
|
788
|
-
.modern-chat-layout .chat-bubble {
|
|
789
|
-
padding: 0.6rem 1rem;
|
|
790
|
-
border-radius: 1rem;
|
|
791
|
-
line-height: 1.5;
|
|
792
|
-
word-wrap: break-word;
|
|
793
|
-
overflow-wrap: break-word;
|
|
794
|
-
}
|
|
795
|
-
.modern-chat-layout .chat-user .chat-bubble {
|
|
796
|
-
background: #0d6efd;
|
|
797
|
-
color: #fff;
|
|
798
|
-
border-bottom-right-radius: 0.25rem;
|
|
799
|
-
}
|
|
800
|
-
.modern-chat-layout .chat-assistant .chat-bubble {
|
|
801
|
-
background: var(--tblr-secondary-bg-subtle, var(--bs-secondary-bg-subtle, #f0f2f5));
|
|
802
|
-
color: var(--tblr-body-color, var(--bs-body-color, #212529));
|
|
803
|
-
border-bottom-left-radius: 0.25rem;
|
|
804
|
-
}
|
|
805
|
-
/* Markdown content inside bubbles */
|
|
806
|
-
.modern-chat-layout .chat-bubble h1,
|
|
807
|
-
.modern-chat-layout .chat-bubble h2,
|
|
808
|
-
.modern-chat-layout .chat-bubble h3,
|
|
809
|
-
.modern-chat-layout .chat-bubble h4 {
|
|
810
|
-
margin-top: 0.5rem;
|
|
811
|
-
margin-bottom: 0.25rem;
|
|
812
|
-
}
|
|
813
|
-
.modern-chat-layout .chat-bubble h1 { font-size: 1.3rem; }
|
|
814
|
-
.modern-chat-layout .chat-bubble h2 { font-size: 1.15rem; }
|
|
815
|
-
.modern-chat-layout .chat-bubble h3 { font-size: 1.05rem; }
|
|
816
|
-
.modern-chat-layout .chat-bubble h4 { font-size: 1rem; }
|
|
817
|
-
.modern-chat-layout .chat-bubble p {
|
|
818
|
-
margin-bottom: 0.4rem;
|
|
819
|
-
}
|
|
820
|
-
.modern-chat-layout .chat-bubble p:last-child {
|
|
821
|
-
margin-bottom: 0;
|
|
822
|
-
}
|
|
823
|
-
.modern-chat-layout .chat-bubble ul,
|
|
824
|
-
.modern-chat-layout .chat-bubble ol {
|
|
825
|
-
padding-left: 1.5rem;
|
|
826
|
-
margin-bottom: 0.4rem;
|
|
827
|
-
}
|
|
828
|
-
.modern-chat-layout .chat-bubble table {
|
|
829
|
-
width: 100%;
|
|
830
|
-
border-collapse: collapse;
|
|
831
|
-
margin: 0.5rem 0;
|
|
832
|
-
font-size: 0.9em;
|
|
833
|
-
}
|
|
834
|
-
.modern-chat-layout .chat-bubble table th,
|
|
835
|
-
.modern-chat-layout .chat-bubble table td {
|
|
836
|
-
border: 1px solid rgba(0,0,0,0.15);
|
|
837
|
-
padding: 0.3rem 0.5rem;
|
|
838
|
-
}
|
|
839
|
-
.modern-chat-layout .chat-bubble table th {
|
|
840
|
-
background: rgba(0,0,0,0.05);
|
|
841
|
-
font-weight: 600;
|
|
842
|
-
}
|
|
843
|
-
.modern-chat-layout .chat-bubble pre {
|
|
844
|
-
background: rgba(0,0,0,0.06);
|
|
845
|
-
padding: 0.5rem;
|
|
846
|
-
border-radius: 0.5rem;
|
|
847
|
-
overflow-x: auto;
|
|
848
|
-
margin: 0.4rem 0;
|
|
849
|
-
}
|
|
850
|
-
.modern-chat-layout .chat-bubble code {
|
|
851
|
-
font-size: 0.88em;
|
|
852
|
-
}
|
|
853
|
-
.modern-chat-layout .chat-bubble p > code {
|
|
854
|
-
background: rgba(0,0,0,0.06);
|
|
855
|
-
padding: 0.1rem 0.3rem;
|
|
856
|
-
border-radius: 0.25rem;
|
|
857
|
-
}
|
|
858
|
-
.modern-chat-layout .chat-user .chat-bubble pre {
|
|
859
|
-
background: rgba(255,255,255,0.15);
|
|
860
|
-
}
|
|
861
|
-
.modern-chat-layout .chat-user .chat-bubble p > code {
|
|
862
|
-
background: rgba(255,255,255,0.15);
|
|
863
|
-
}
|
|
864
|
-
.modern-chat-layout .chat-user .chat-bubble table th,
|
|
865
|
-
.modern-chat-layout .chat-user .chat-bubble table td {
|
|
866
|
-
border-color: rgba(255,255,255,0.25);
|
|
867
|
-
}
|
|
868
|
-
.modern-chat-layout .chat-user .chat-bubble table th {
|
|
869
|
-
background: rgba(255,255,255,0.1);
|
|
870
|
-
}
|
|
871
|
-
/* Skill attribution badge */
|
|
872
|
-
.modern-chat-layout .chat-bubble .badge.bg-info {
|
|
873
|
-
display: inline-block;
|
|
874
|
-
margin-bottom: 6px;
|
|
875
|
-
font-size: 0.7rem;
|
|
876
|
-
font-weight: 600;
|
|
877
|
-
letter-spacing: 0.3px;
|
|
878
|
-
text-transform: uppercase;
|
|
879
|
-
opacity: 0.85;
|
|
880
|
-
}
|
|
881
|
-
.modern-chat-layout .chat-bubble .card.bg-secondary-subtle {
|
|
882
|
-
border: none;
|
|
883
|
-
background-color: rgba(0,0,0,0.03) !important;
|
|
884
|
-
margin-bottom: 0.5rem;
|
|
885
|
-
}
|
|
886
|
-
/* Input area for modern chat */
|
|
887
|
-
.modern-chat-layout .copilot-entry {
|
|
888
|
-
border-top: 1px solid var(--tblr-border-color, var(--bs-border-color, #dee2e6));
|
|
889
|
-
padding-top: 0.75rem;
|
|
890
|
-
margin-top: 0.5rem;
|
|
891
|
-
}
|
|
892
|
-
.modern-chat-layout .copilot-entry textarea {
|
|
893
|
-
border-radius: 1.5rem;
|
|
894
|
-
padding: 0.6rem 1rem;
|
|
895
|
-
resize: none;
|
|
896
|
-
}
|
|
897
|
-
/* Streaming scratch in modern chat */
|
|
898
|
-
.modern-chat-layout .next_response_scratch {
|
|
899
|
-
padding: 0 1rem;
|
|
900
|
-
}
|
|
901
|
-
.modern-chat-layout .next_response_scratch:not(:empty) {
|
|
902
|
-
margin-bottom: 0.5rem;
|
|
903
|
-
}
|
|
904
|
-
/* Interaction segment (tool cards) inside modern chat */
|
|
905
|
-
.modern-chat-layout .interaction-segment {
|
|
906
|
-
border-top: none;
|
|
907
|
-
}
|
|
908
|
-
/* Modern Sessions Sidebar */
|
|
909
|
-
.modern-sessions-header {
|
|
910
|
-
display: flex;
|
|
911
|
-
align-items: center;
|
|
912
|
-
justify-content: space-between;
|
|
913
|
-
padding: 0.6rem 0.75rem;
|
|
914
|
-
margin-bottom: 0.75rem;
|
|
915
|
-
background: var(--tblr-secondary-bg-subtle, var(--bs-secondary-bg-subtle, #f8f9fa));
|
|
916
|
-
border-radius: 0.75rem;
|
|
917
|
-
border-bottom: 1px solid var(--tblr-border-color, var(--bs-border-color, #dee2e6));
|
|
918
|
-
position: sticky;
|
|
919
|
-
top: 0;
|
|
920
|
-
z-index: 1;
|
|
921
|
-
}
|
|
922
|
-
.modern-sessions .modern-session-item {
|
|
923
|
-
border-radius: 0.75rem;
|
|
924
|
-
padding: 0.65rem 0.75rem;
|
|
925
|
-
margin-bottom: 0.4rem;
|
|
926
|
-
border: 1px solid var(--tblr-border-color, var(--bs-border-color, #dee2e6));
|
|
927
|
-
cursor: pointer;
|
|
928
|
-
transition: all 0.15s ease;
|
|
929
|
-
}
|
|
930
|
-
.modern-sessions .modern-session-item:hover {
|
|
931
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.07);
|
|
932
|
-
background-color: var(--tblr-secondary-bg-subtle, var(--bs-secondary-bg-subtle, #f8f9fa));
|
|
933
|
-
}
|
|
934
|
-
.modern-sessions .modern-session-item.active-session {
|
|
935
|
-
border-left: 3px solid #0d6efd;
|
|
936
|
-
background-color: rgba(13, 110, 253, 0.05);
|
|
937
|
-
}
|
|
938
|
-
.modern-sessions .modern-session-item i.fa-trash-alt {
|
|
939
|
-
display: none;
|
|
940
|
-
font-size: 0.8em;
|
|
941
|
-
}
|
|
942
|
-
.modern-sessions .modern-session-item:hover i.fa-trash-alt {
|
|
943
|
-
display: inline;
|
|
944
|
-
}
|
|
945
|
-
.modern-sessions .modern-session-item .prevrun_content {
|
|
946
|
-
font-size: 0.85em;
|
|
947
|
-
color: var(--tblr-secondary-color, var(--bs-secondary-color, #6c757d));
|
|
948
|
-
white-space: nowrap;
|
|
949
|
-
overflow: hidden;
|
|
950
|
-
text-overflow: ellipsis;
|
|
951
|
-
}`,
|
|
952
|
-
),
|
|
669
|
+
hasInputForm && input_form,
|
|
670
|
+
style(agents_css),
|
|
953
671
|
script(domReady(`$( "#inputuserinput" ).autogrow({paddingBottom: 20});`)),
|
|
954
672
|
script(
|
|
955
673
|
`
|
|
@@ -1154,7 +872,37 @@ const run = async (
|
|
|
1154
872
|
: '<div class="agent-waiting-indicator"><div class="typing-dots"><span></span><span></span><span></span></div></div>';
|
|
1155
873
|
$('div.next_response_scratch').before(indicator);
|
|
1156
874
|
scrollAgentToBottom();
|
|
1157
|
-
}
|
|
875
|
+
};
|
|
876
|
+
document.addEventListener('click', async (e) => {
|
|
877
|
+
const target = e.target.closest('.copy-to-clipboard-elem');
|
|
878
|
+
if (!target) return;
|
|
879
|
+
|
|
880
|
+
// Check if the click was in the top-right corner where the icon is
|
|
881
|
+
const rect = target.getBoundingClientRect();
|
|
882
|
+
const clickX = e.clientX - rect.left;
|
|
883
|
+
const clickY = e.clientY - rect.top;
|
|
884
|
+
|
|
885
|
+
// Icon is at top: 4px, right: 4px, ~16px size — give a generous hit area
|
|
886
|
+
const iconHitArea = 24;
|
|
887
|
+
const isIconClick =
|
|
888
|
+
clickX >= rect.width - iconHitArea &&
|
|
889
|
+
clickX <= rect.width &&
|
|
890
|
+
clickY >= 0 &&
|
|
891
|
+
clickY <= iconHitArea;
|
|
892
|
+
|
|
893
|
+
if (!isIconClick) return;
|
|
894
|
+
|
|
895
|
+
e.stopPropagation();
|
|
896
|
+
e.preventDefault();
|
|
897
|
+
|
|
898
|
+
try {
|
|
899
|
+
await navigator.clipboard.writeText(target.innerText);
|
|
900
|
+
target.classList.add('copy-success');
|
|
901
|
+
setTimeout(() => target.classList.remove('copy-success'), 1000);
|
|
902
|
+
} catch (err) {
|
|
903
|
+
console.error('Failed to copy:', err);
|
|
904
|
+
}
|
|
905
|
+
});`,
|
|
1158
906
|
stream &&
|
|
1159
907
|
domReady(
|
|
1160
908
|
`$('form.agent-view input[name=page_load_tag]').val(window._sc_pageloadtag)`,
|
package/agents.css
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
div.interaction-segment:not(:first-child) {
|
|
2
|
+
border-top: 1px solid #e7e7e7;
|
|
3
|
+
}
|
|
4
|
+
div.interaction-segment {
|
|
5
|
+
padding-top: 5px;
|
|
6
|
+
padding-bottom: 5px;
|
|
7
|
+
}
|
|
8
|
+
div.interaction-segment p {
|
|
9
|
+
margin-bottom: 0px;
|
|
10
|
+
}
|
|
11
|
+
div.interaction-segment div.card {
|
|
12
|
+
margin-top: 0.5rem;
|
|
13
|
+
}
|
|
14
|
+
div.interaction-segment.to-right {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: row-reverse;
|
|
17
|
+
}
|
|
18
|
+
div.interaction-segment.to-right div.badgewrap {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: row-reverse;
|
|
21
|
+
}
|
|
22
|
+
div.prevcopilotrun:hover {
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
background-color: var(
|
|
25
|
+
--tblr-secondary-bg-subtle,
|
|
26
|
+
var(--bs-secondary-bg-subtle, gray)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
div.prevcopilotrun i.fa-trash-alt {
|
|
30
|
+
display: none;
|
|
31
|
+
}
|
|
32
|
+
div.prevcopilotrun:hover i.fa-trash-alt {
|
|
33
|
+
display: block;
|
|
34
|
+
}
|
|
35
|
+
.copilot-entry .submit-button:hover {
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
}
|
|
38
|
+
.copilot-entry span.attach_agent_image_wrap i:hover {
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.copilot-entry .submit-button {
|
|
43
|
+
position: relative;
|
|
44
|
+
top: -1.8rem;
|
|
45
|
+
left: 0.1rem;
|
|
46
|
+
}
|
|
47
|
+
.copilot-entry #audioinputicon {
|
|
48
|
+
position: relative;
|
|
49
|
+
top: -1.8rem;
|
|
50
|
+
right: 0.7rem;
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
float: right;
|
|
53
|
+
}
|
|
54
|
+
.copilot-entry .debugicon {
|
|
55
|
+
position: relative;
|
|
56
|
+
top: -1.8rem;
|
|
57
|
+
left: 0.1rem;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
}
|
|
60
|
+
.copilot-entry .cancelbtn {
|
|
61
|
+
position: relative;
|
|
62
|
+
top: -1.8rem;
|
|
63
|
+
left: 0.1rem;
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
}
|
|
66
|
+
.copilot-entry .skill-form-widget {
|
|
67
|
+
position: relative;
|
|
68
|
+
top: -2rem;
|
|
69
|
+
left: 0.4rem;
|
|
70
|
+
display: inline;
|
|
71
|
+
}
|
|
72
|
+
.session-open-sessions,
|
|
73
|
+
.open-prev-runs {
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
}
|
|
76
|
+
.copilot-entry span.attach_agent_image_wrap {
|
|
77
|
+
position: relative;
|
|
78
|
+
top: -1.8rem;
|
|
79
|
+
left: 0.2rem;
|
|
80
|
+
}
|
|
81
|
+
.copilot-entry.dragover {
|
|
82
|
+
outline: 2px dashed var(--tblr-primary, #0054a6);
|
|
83
|
+
outline-offset: -2px;
|
|
84
|
+
background: var(--tblr-primary-bg-subtle, rgba(0, 84, 166, 0.05));
|
|
85
|
+
border-radius: 0.25rem;
|
|
86
|
+
}
|
|
87
|
+
.copilot-entry .explainer {
|
|
88
|
+
position: relative;
|
|
89
|
+
top: -1.2rem;
|
|
90
|
+
display: block;
|
|
91
|
+
}
|
|
92
|
+
.col-0 {
|
|
93
|
+
width: 0%;
|
|
94
|
+
}
|
|
95
|
+
.copilot-entry {
|
|
96
|
+
margin-bottom: -1.25rem;
|
|
97
|
+
margin-top: 1rem;
|
|
98
|
+
}
|
|
99
|
+
p.prevrun_content {
|
|
100
|
+
white-space: nowrap;
|
|
101
|
+
overflow: hidden;
|
|
102
|
+
margin-bottom: 0px;
|
|
103
|
+
display: block;
|
|
104
|
+
text-overflow: ellipsis;
|
|
105
|
+
}
|
|
106
|
+
/* Typing / Waiting Indicator */
|
|
107
|
+
.agent-waiting-indicator {
|
|
108
|
+
display: flex;
|
|
109
|
+
align-items: center;
|
|
110
|
+
padding: 0.75rem 1rem;
|
|
111
|
+
}
|
|
112
|
+
.typing-dots {
|
|
113
|
+
display: flex;
|
|
114
|
+
gap: 4px;
|
|
115
|
+
align-items: center;
|
|
116
|
+
}
|
|
117
|
+
.typing-dots span {
|
|
118
|
+
width: 8px;
|
|
119
|
+
height: 8px;
|
|
120
|
+
border-radius: 50%;
|
|
121
|
+
background: #6c757d;
|
|
122
|
+
animation: typingBounce 1.4s infinite ease-in-out both;
|
|
123
|
+
}
|
|
124
|
+
.typing-dots span:nth-child(1) {
|
|
125
|
+
animation-delay: -0.32s;
|
|
126
|
+
}
|
|
127
|
+
.typing-dots span:nth-child(2) {
|
|
128
|
+
animation-delay: -0.16s;
|
|
129
|
+
}
|
|
130
|
+
.typing-dots span:nth-child(3) {
|
|
131
|
+
animation-delay: 0s;
|
|
132
|
+
}
|
|
133
|
+
@keyframes typingBounce {
|
|
134
|
+
0%,
|
|
135
|
+
80%,
|
|
136
|
+
100% {
|
|
137
|
+
transform: scale(0.6);
|
|
138
|
+
opacity: 0.4;
|
|
139
|
+
}
|
|
140
|
+
40% {
|
|
141
|
+
transform: scale(1);
|
|
142
|
+
opacity: 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/* Modern Chat Layout */
|
|
146
|
+
.modern-chat-layout {
|
|
147
|
+
display: flex;
|
|
148
|
+
flex-direction: column;
|
|
149
|
+
height: 100%;
|
|
150
|
+
}
|
|
151
|
+
.modern-chat-layout #copilotinteractions {
|
|
152
|
+
/*max-height: 70vh;*/
|
|
153
|
+
overflow-y: auto;
|
|
154
|
+
padding: 1rem;
|
|
155
|
+
display: flex;
|
|
156
|
+
flex-direction: column;
|
|
157
|
+
gap: 0.75rem;
|
|
158
|
+
}
|
|
159
|
+
.modern-chat-layout .chat-message {
|
|
160
|
+
display: flex;
|
|
161
|
+
gap: 0.5rem;
|
|
162
|
+
max-width: 85%;
|
|
163
|
+
align-items: flex-start;
|
|
164
|
+
}
|
|
165
|
+
.modern-chat-layout .chat-message.chat-user {
|
|
166
|
+
align-self: flex-end;
|
|
167
|
+
flex-direction: row-reverse;
|
|
168
|
+
}
|
|
169
|
+
.modern-chat-layout .chat-message.chat-assistant {
|
|
170
|
+
align-self: flex-start;
|
|
171
|
+
}
|
|
172
|
+
.modern-chat-layout .chat-avatar {
|
|
173
|
+
width: 2rem;
|
|
174
|
+
height: 2rem;
|
|
175
|
+
border-radius: 50%;
|
|
176
|
+
display: flex;
|
|
177
|
+
align-items: center;
|
|
178
|
+
justify-content: center;
|
|
179
|
+
flex-shrink: 0;
|
|
180
|
+
font-size: 0.85rem;
|
|
181
|
+
background: var(
|
|
182
|
+
--tblr-secondary-bg-subtle,
|
|
183
|
+
var(--bs-secondary-bg-subtle, #e9ecef)
|
|
184
|
+
);
|
|
185
|
+
color: var(--tblr-secondary-color, var(--bs-secondary-color, #6c757d));
|
|
186
|
+
}
|
|
187
|
+
.modern-chat-layout .chat-user .chat-avatar {
|
|
188
|
+
background: #0d6efd;
|
|
189
|
+
color: #fff;
|
|
190
|
+
}
|
|
191
|
+
.modern-chat-layout .chat-bubble {
|
|
192
|
+
padding: 0.6rem 1rem;
|
|
193
|
+
border-radius: 1rem;
|
|
194
|
+
line-height: 1.5;
|
|
195
|
+
word-wrap: break-word;
|
|
196
|
+
overflow-wrap: break-word;
|
|
197
|
+
}
|
|
198
|
+
.modern-chat-layout .chat-user .chat-bubble {
|
|
199
|
+
background: #0d6efd;
|
|
200
|
+
color: #fff;
|
|
201
|
+
border-bottom-right-radius: 0.25rem;
|
|
202
|
+
}
|
|
203
|
+
.modern-chat-layout .chat-assistant .chat-bubble {
|
|
204
|
+
background: var(
|
|
205
|
+
--tblr-secondary-bg-subtle,
|
|
206
|
+
var(--bs-secondary-bg-subtle, #f0f2f5)
|
|
207
|
+
);
|
|
208
|
+
color: var(--tblr-body-color, var(--bs-body-color, #212529));
|
|
209
|
+
border-bottom-left-radius: 0.25rem;
|
|
210
|
+
}
|
|
211
|
+
/* Markdown content inside bubbles */
|
|
212
|
+
.modern-chat-layout .chat-bubble h1,
|
|
213
|
+
.modern-chat-layout .chat-bubble h2,
|
|
214
|
+
.modern-chat-layout .chat-bubble h3,
|
|
215
|
+
.modern-chat-layout .chat-bubble h4 {
|
|
216
|
+
margin-top: 0.5rem;
|
|
217
|
+
margin-bottom: 0.25rem;
|
|
218
|
+
}
|
|
219
|
+
.modern-chat-layout .chat-bubble h1 {
|
|
220
|
+
font-size: 1.3rem;
|
|
221
|
+
}
|
|
222
|
+
.modern-chat-layout .chat-bubble h2 {
|
|
223
|
+
font-size: 1.15rem;
|
|
224
|
+
}
|
|
225
|
+
.modern-chat-layout .chat-bubble h3 {
|
|
226
|
+
font-size: 1.05rem;
|
|
227
|
+
}
|
|
228
|
+
.modern-chat-layout .chat-bubble h4 {
|
|
229
|
+
font-size: 1rem;
|
|
230
|
+
}
|
|
231
|
+
.modern-chat-layout .chat-bubble p {
|
|
232
|
+
margin-bottom: 0.4rem;
|
|
233
|
+
}
|
|
234
|
+
.modern-chat-layout .chat-bubble p:last-child {
|
|
235
|
+
margin-bottom: 0;
|
|
236
|
+
}
|
|
237
|
+
.modern-chat-layout .chat-bubble ul,
|
|
238
|
+
.modern-chat-layout .chat-bubble ol {
|
|
239
|
+
padding-left: 1.5rem;
|
|
240
|
+
margin-bottom: 0.4rem;
|
|
241
|
+
}
|
|
242
|
+
.modern-chat-layout .chat-bubble table {
|
|
243
|
+
width: 100%;
|
|
244
|
+
border-collapse: collapse;
|
|
245
|
+
margin: 0.5rem 0;
|
|
246
|
+
font-size: 0.9em;
|
|
247
|
+
}
|
|
248
|
+
.modern-chat-layout .chat-bubble table th,
|
|
249
|
+
.modern-chat-layout .chat-bubble table td {
|
|
250
|
+
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
251
|
+
padding: 0.3rem 0.5rem;
|
|
252
|
+
}
|
|
253
|
+
.modern-chat-layout .chat-bubble table th {
|
|
254
|
+
background: rgba(0, 0, 0, 0.05);
|
|
255
|
+
font-weight: 600;
|
|
256
|
+
}
|
|
257
|
+
.modern-chat-layout .chat-bubble pre {
|
|
258
|
+
background: rgba(0, 0, 0, 0.06);
|
|
259
|
+
padding: 0.5rem;
|
|
260
|
+
border-radius: 0.5rem;
|
|
261
|
+
overflow-x: auto;
|
|
262
|
+
margin: 0.4rem 0;
|
|
263
|
+
}
|
|
264
|
+
.modern-chat-layout .chat-bubble code {
|
|
265
|
+
font-size: 0.88em;
|
|
266
|
+
}
|
|
267
|
+
.modern-chat-layout .chat-bubble p > code {
|
|
268
|
+
background: rgba(0, 0, 0, 0.06);
|
|
269
|
+
padding: 0.1rem 0.3rem;
|
|
270
|
+
border-radius: 0.25rem;
|
|
271
|
+
}
|
|
272
|
+
.modern-chat-layout .chat-user .chat-bubble pre {
|
|
273
|
+
background: rgba(255, 255, 255, 0.15);
|
|
274
|
+
}
|
|
275
|
+
.modern-chat-layout .chat-user .chat-bubble p > code {
|
|
276
|
+
background: rgba(255, 255, 255, 0.15);
|
|
277
|
+
}
|
|
278
|
+
.modern-chat-layout .chat-user .chat-bubble table th,
|
|
279
|
+
.modern-chat-layout .chat-user .chat-bubble table td {
|
|
280
|
+
border-color: rgba(255, 255, 255, 0.25);
|
|
281
|
+
}
|
|
282
|
+
.modern-chat-layout .chat-user .chat-bubble table th {
|
|
283
|
+
background: rgba(255, 255, 255, 0.1);
|
|
284
|
+
}
|
|
285
|
+
/* Skill attribution badge */
|
|
286
|
+
.modern-chat-layout .chat-bubble .badge.bg-info {
|
|
287
|
+
display: inline-block;
|
|
288
|
+
margin-bottom: 6px;
|
|
289
|
+
font-size: 0.7rem;
|
|
290
|
+
font-weight: 600;
|
|
291
|
+
letter-spacing: 0.3px;
|
|
292
|
+
text-transform: uppercase;
|
|
293
|
+
opacity: 0.85;
|
|
294
|
+
}
|
|
295
|
+
.modern-chat-layout .chat-bubble .card.bg-secondary-subtle {
|
|
296
|
+
border: none;
|
|
297
|
+
background-color: rgba(0, 0, 0, 0.03) !important;
|
|
298
|
+
margin-bottom: 0.5rem;
|
|
299
|
+
}
|
|
300
|
+
/* Input area for modern chat */
|
|
301
|
+
.modern-chat-layout .copilot-entry {
|
|
302
|
+
border-top: 1px solid
|
|
303
|
+
var(--tblr-border-color, var(--bs-border-color, #dee2e6));
|
|
304
|
+
padding-top: 0.75rem;
|
|
305
|
+
margin-top: 0.5rem;
|
|
306
|
+
}
|
|
307
|
+
.modern-chat-layout .copilot-entry textarea {
|
|
308
|
+
border-radius: 1.5rem;
|
|
309
|
+
padding: 0.6rem 1rem;
|
|
310
|
+
resize: none;
|
|
311
|
+
}
|
|
312
|
+
/* Streaming scratch in modern chat */
|
|
313
|
+
.modern-chat-layout .next_response_scratch {
|
|
314
|
+
padding: 0 1rem;
|
|
315
|
+
}
|
|
316
|
+
.modern-chat-layout .next_response_scratch:not(:empty) {
|
|
317
|
+
margin-bottom: 0.5rem;
|
|
318
|
+
}
|
|
319
|
+
/* Interaction segment (tool cards) inside modern chat */
|
|
320
|
+
.modern-chat-layout .interaction-segment {
|
|
321
|
+
border-top: none;
|
|
322
|
+
}
|
|
323
|
+
/* Modern Sessions Sidebar */
|
|
324
|
+
.modern-sessions-header {
|
|
325
|
+
display: flex;
|
|
326
|
+
align-items: center;
|
|
327
|
+
justify-content: space-between;
|
|
328
|
+
padding: 0.6rem 0.75rem;
|
|
329
|
+
margin-bottom: 0.75rem;
|
|
330
|
+
background: var(
|
|
331
|
+
--tblr-secondary-bg-subtle,
|
|
332
|
+
var(--bs-secondary-bg-subtle, #f8f9fa)
|
|
333
|
+
);
|
|
334
|
+
border-radius: 0.75rem;
|
|
335
|
+
border-bottom: 1px solid
|
|
336
|
+
var(--tblr-border-color, var(--bs-border-color, #dee2e6));
|
|
337
|
+
position: sticky;
|
|
338
|
+
top: 0;
|
|
339
|
+
z-index: 1;
|
|
340
|
+
}
|
|
341
|
+
.modern-sessions .modern-session-item {
|
|
342
|
+
border-radius: 0.75rem;
|
|
343
|
+
padding: 0.65rem 0.75rem;
|
|
344
|
+
margin-bottom: 0.4rem;
|
|
345
|
+
border: 1px solid var(--tblr-border-color, var(--bs-border-color, #dee2e6));
|
|
346
|
+
cursor: pointer;
|
|
347
|
+
transition: all 0.15s ease;
|
|
348
|
+
}
|
|
349
|
+
.modern-sessions .modern-session-item:hover {
|
|
350
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
|
|
351
|
+
background-color: var(
|
|
352
|
+
--tblr-secondary-bg-subtle,
|
|
353
|
+
var(--bs-secondary-bg-subtle, #f8f9fa)
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
.modern-sessions .modern-session-item.active-session {
|
|
357
|
+
border-left: 3px solid #0d6efd;
|
|
358
|
+
background-color: rgba(13, 110, 253, 0.05);
|
|
359
|
+
}
|
|
360
|
+
.modern-sessions .modern-session-item i.fa-trash-alt {
|
|
361
|
+
display: none;
|
|
362
|
+
font-size: 0.8em;
|
|
363
|
+
}
|
|
364
|
+
.modern-sessions .modern-session-item:hover i.fa-trash-alt {
|
|
365
|
+
display: inline;
|
|
366
|
+
}
|
|
367
|
+
.modern-sessions .modern-session-item .prevrun_content {
|
|
368
|
+
font-size: 0.85em;
|
|
369
|
+
color: var(--tblr-secondary-color, var(--bs-secondary-color, #6c757d));
|
|
370
|
+
white-space: nowrap;
|
|
371
|
+
overflow: hidden;
|
|
372
|
+
text-overflow: ellipsis;
|
|
373
|
+
}
|
|
374
|
+
.copy-to-clipboard-elem {
|
|
375
|
+
position: relative;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.copy-to-clipboard-elem::before {
|
|
379
|
+
content: "📋";
|
|
380
|
+
position: absolute;
|
|
381
|
+
top: 4px;
|
|
382
|
+
right: 4px;
|
|
383
|
+
font-size: 16px;
|
|
384
|
+
line-height: 1;
|
|
385
|
+
cursor: pointer;
|
|
386
|
+
opacity: 0;
|
|
387
|
+
pointer-events: none;
|
|
388
|
+
transition: opacity 0.15s ease-in-out;
|
|
389
|
+
z-index: 10;
|
|
390
|
+
user-select: none;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.copy-to-clipboard-elem:hover::before {
|
|
394
|
+
opacity: 1;
|
|
395
|
+
pointer-events: auto;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.copy-to-clipboard-elem.copy-success::before {
|
|
399
|
+
content: "✓";
|
|
400
|
+
color: green;
|
|
401
|
+
}
|
package/common.js
CHANGED
|
@@ -43,6 +43,7 @@ const get_skills = () => {
|
|
|
43
43
|
require("./skills/Subagent"),
|
|
44
44
|
require("./skills/ExternalSkill"),
|
|
45
45
|
require("./skills/PlanApproval"),
|
|
46
|
+
require("./skills/LongTermMemory"),
|
|
46
47
|
//require("./skills/AdaptiveFeedback"),
|
|
47
48
|
...exchange_skills,
|
|
48
49
|
];
|
|
@@ -57,8 +58,10 @@ const get_skill_instances = (config) => {
|
|
|
57
58
|
const instances = [];
|
|
58
59
|
for (const skillCfg of config.skills) {
|
|
59
60
|
const klass = get_skill_class(skillCfg.skill_type);
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
if (klass) {
|
|
62
|
+
const skill = new klass(skillCfg);
|
|
63
|
+
instances.push(skill);
|
|
64
|
+
}
|
|
62
65
|
}
|
|
63
66
|
return instances;
|
|
64
67
|
};
|
|
@@ -220,7 +223,7 @@ const wrapSegment = (html, who, to_right, layout, user) =>
|
|
|
220
223
|
: layout && layout.startsWith("Modern chat")
|
|
221
224
|
? `<div class="chat-message ${to_right ? "chat-user" : "chat-assistant"}">` +
|
|
222
225
|
`<div class="chat-avatar"${user ? ` title="${user.email} at ${new Date().toString()}"` : ""}><i class="fas ${to_right ? "fa-user" : "fa-robot"}"></i></div>` +
|
|
223
|
-
`<div class="chat-bubble">${html}</div>` +
|
|
226
|
+
`<div class="chat-bubble${" copy-to-clipboard-elem"}">${html}</div>` +
|
|
224
227
|
`</div>`
|
|
225
228
|
: `<div class="interaction-segment ${to_right ? "to-right" : ""}"><div><div class="badgewrap"><span class="badge bg-secondary">` +
|
|
226
229
|
who +
|
|
@@ -237,6 +240,10 @@ const wrapCard = (title, ...inners) =>
|
|
|
237
240
|
|
|
238
241
|
const is_debug_mode = (config, user) => user?.role_id === 1;
|
|
239
242
|
|
|
243
|
+
function extractText(html) {
|
|
244
|
+
return html.replace(/<[^>]*>/g, '');
|
|
245
|
+
}
|
|
246
|
+
|
|
240
247
|
const process_interaction = async (
|
|
241
248
|
run,
|
|
242
249
|
config,
|
|
@@ -414,6 +421,7 @@ const process_interaction = async (
|
|
|
414
421
|
myHasResult = true;
|
|
415
422
|
let result = await tool.tool.process(tool_call.input, {
|
|
416
423
|
req,
|
|
424
|
+
run,
|
|
417
425
|
});
|
|
418
426
|
const tool_response = result.add_response || result;
|
|
419
427
|
toolResults[tool_call.tool_call_id] = result;
|
|
@@ -744,4 +752,5 @@ module.exports = {
|
|
|
744
752
|
is_debug_mode,
|
|
745
753
|
get_initial_interactions,
|
|
746
754
|
nubBy,
|
|
755
|
+
extractText
|
|
747
756
|
};
|
package/package.json
CHANGED
package/skills/Fetch.js
CHANGED
|
@@ -15,9 +15,69 @@ const { features } = require("@saltcorn/data/db/state");
|
|
|
15
15
|
const { button } = require("@saltcorn/markup/tags");
|
|
16
16
|
const { validID } = require("@saltcorn/markup/layout_utils");
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
class CookieJar {
|
|
19
|
+
constructor(source) {
|
|
20
|
+
this.cookies = new Map();
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
if (source instanceof CookieJar) {
|
|
23
|
+
// Copy from another CookieJar instance
|
|
24
|
+
for (const [name, value] of source.cookies) {
|
|
25
|
+
this.cookies.set(name, value);
|
|
26
|
+
}
|
|
27
|
+
} else if (source && typeof source === "object") {
|
|
28
|
+
// Hydrate from a plain object (e.g. output of toObject())
|
|
29
|
+
for (const [name, value] of Object.entries(source)) {
|
|
30
|
+
this.cookies.set(name, String(value));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// If source is undefined/null, start empty
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Parse Set-Cookie headers from a response and store them
|
|
37
|
+
storeFromResponse(response) {
|
|
38
|
+
const setCookieHeaders = response.headers.getSetCookie
|
|
39
|
+
? response.headers.getSetCookie()
|
|
40
|
+
: [response.headers.get("set-cookie")].filter(Boolean);
|
|
41
|
+
|
|
42
|
+
for (const header of setCookieHeaders) {
|
|
43
|
+
this._parseAndStore(header);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Parse a single "name=value; Path=/; HttpOnly; ..." string
|
|
48
|
+
_parseAndStore(header) {
|
|
49
|
+
const [nameValue] = header.split(";");
|
|
50
|
+
const eqIdx = nameValue.indexOf("=");
|
|
51
|
+
if (eqIdx > 0) {
|
|
52
|
+
const name = nameValue.slice(0, eqIdx).trim();
|
|
53
|
+
const value = nameValue.slice(eqIdx + 1).trim();
|
|
54
|
+
this.cookies.set(name, value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Build a Cookie header string suitable for outgoing requests
|
|
59
|
+
toHeader() {
|
|
60
|
+
return Array.from(this.cookies.entries())
|
|
61
|
+
.map(([name, value]) => `${name}=${value}`)
|
|
62
|
+
.join("; ");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Apply cookies to a headers object (mutates and returns it)
|
|
66
|
+
applyTo(headers = {}) {
|
|
67
|
+
if (this.cookies.size > 0) {
|
|
68
|
+
headers["Cookie"] = this.toHeader();
|
|
69
|
+
}
|
|
70
|
+
return headers;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get size() {
|
|
74
|
+
return this.cookies.size;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
toObject() {
|
|
78
|
+
return Object.fromEntries(this.cookies);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
21
81
|
|
|
22
82
|
class FetchSkill {
|
|
23
83
|
static skill_name = "Fetch";
|
|
@@ -31,7 +91,7 @@ class FetchSkill {
|
|
|
31
91
|
}
|
|
32
92
|
|
|
33
93
|
static async configFields() {
|
|
34
|
-
return [];
|
|
94
|
+
return [{ name: "cookiejar", label: "Cookie Jar", type: "Bool" }];
|
|
35
95
|
}
|
|
36
96
|
systemPrompt() {
|
|
37
97
|
return "If you need to retrieve the contents of a web page, use the fetch_web_page to make a GET request to a specified URL.";
|
|
@@ -40,8 +100,29 @@ class FetchSkill {
|
|
|
40
100
|
provideTools = () => {
|
|
41
101
|
return {
|
|
42
102
|
type: "function",
|
|
43
|
-
process: async (row) => {
|
|
44
|
-
const
|
|
103
|
+
process: async (row, { run }) => {
|
|
104
|
+
const opts = { headers: {} };
|
|
105
|
+
const jar = new CookieJar(run.context.cookiejar || {});
|
|
106
|
+
if (this.cookiejar) {
|
|
107
|
+
opts.headers = jar.applyTo();
|
|
108
|
+
opts.credentials = "same-origin";
|
|
109
|
+
opts.redirect = "manual";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (row.method) opts.method = row.method;
|
|
113
|
+
if (row.content_type) opts.headers["Content-Type"] = row.content_type;
|
|
114
|
+
if (row.body) opts.body = row.body;
|
|
115
|
+
|
|
116
|
+
let resp = await fetch(row.url, opts);
|
|
117
|
+
if (resp.status == 302 && (!row.method || row.method === "GET")) {
|
|
118
|
+
if (this.cookiejar) jar.storeFromResponse(resp);
|
|
119
|
+
resp = await fetch(resp.headers.get("location"), opts);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (this.cookiejar) {
|
|
123
|
+
jar.storeFromResponse(resp);
|
|
124
|
+
run.context.cookiejar = jar.toObject();
|
|
125
|
+
}
|
|
45
126
|
return await resp.text();
|
|
46
127
|
},
|
|
47
128
|
function: {
|
|
@@ -55,6 +136,20 @@ class FetchSkill {
|
|
|
55
136
|
description: "The URL to fetch with HTTP",
|
|
56
137
|
type: "string",
|
|
57
138
|
},
|
|
139
|
+
method: {
|
|
140
|
+
description: "The HTTP method",
|
|
141
|
+
type: "string",
|
|
142
|
+
enum: ["GET", "POST", "PUT", "DELETE"],
|
|
143
|
+
},
|
|
144
|
+
body: {
|
|
145
|
+
description: "The request body as a string (POST and PUT only)",
|
|
146
|
+
type: "string",
|
|
147
|
+
},
|
|
148
|
+
content_type: {
|
|
149
|
+
description:
|
|
150
|
+
"The request body content type, e.g. application/x-www-form-urlencoded or application/json (POST and PUT only)",
|
|
151
|
+
type: "string",
|
|
152
|
+
},
|
|
58
153
|
},
|
|
59
154
|
},
|
|
60
155
|
},
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
const { div, pre } = require("@saltcorn/markup/tags");
|
|
2
|
+
const Workflow = require("@saltcorn/data/models/workflow");
|
|
3
|
+
const Form = require("@saltcorn/data/models/form");
|
|
4
|
+
const Table = require("@saltcorn/data/models/table");
|
|
5
|
+
const Field = require("@saltcorn/data/models/field");
|
|
6
|
+
const View = require("@saltcorn/data/models/view");
|
|
7
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
8
|
+
const db = require("@saltcorn/data/db");
|
|
9
|
+
const { interpolate } = require("@saltcorn/data/utils");
|
|
10
|
+
const { nubBy } = require("../common");
|
|
11
|
+
|
|
12
|
+
class LongTermMemory {
|
|
13
|
+
static skill_name = "Memory";
|
|
14
|
+
|
|
15
|
+
get skill_label() {
|
|
16
|
+
return `Memory`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
constructor(cfg) {
|
|
20
|
+
Object.assign(this, cfg);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
systemPrompt() {
|
|
24
|
+
return `You have access to a memory bank you can read from or write to. You should search the memory bank with the search_memory tool with any search terms that might be relevant to the user's query or the result of a tool call. When you learn something noteworthy (from the user or from the result of a tool call) store it in memory with the store_in_memory tool. Mark it as personal if it is only true or relevant for the specific user. Don't tell the user when you are storing to and retrieving from memory. ${
|
|
25
|
+
this.add_sys_prompt
|
|
26
|
+
? ` Additional instructions for the memory tools: ${this.add_sys_prompt}`
|
|
27
|
+
: ""
|
|
28
|
+
}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static async configFields() {
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
name: "add_sys_prompt",
|
|
35
|
+
label: "Additional prompt",
|
|
36
|
+
type: "String",
|
|
37
|
+
fieldview: "textarea",
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async get_table() {
|
|
43
|
+
const table0 = Table.findOne("AgentLongTermMemory");
|
|
44
|
+
if (table0) return table0;
|
|
45
|
+
const tables = await Table.find({ name: "AgentLongTermMemory" });
|
|
46
|
+
if (tables.length) return tables[0];
|
|
47
|
+
|
|
48
|
+
//does not exist, create it
|
|
49
|
+
const table = await Table.create("AgentLongTermMemory", {});
|
|
50
|
+
await getState().refresh_tables(true);
|
|
51
|
+
await Field.create({
|
|
52
|
+
table,
|
|
53
|
+
name: "run_id",
|
|
54
|
+
label: "Run ID",
|
|
55
|
+
type: "Integer",
|
|
56
|
+
});
|
|
57
|
+
const uid_field = await Field.create({
|
|
58
|
+
table,
|
|
59
|
+
name: "user_id",
|
|
60
|
+
label: "User ID",
|
|
61
|
+
type: "Key to users",
|
|
62
|
+
attributes: {
|
|
63
|
+
on_delete: "Set null",
|
|
64
|
+
include_fts: false,
|
|
65
|
+
summary_field: "email",
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
await Field.create({
|
|
69
|
+
table,
|
|
70
|
+
name: "written_at",
|
|
71
|
+
label: "Written at",
|
|
72
|
+
type: "Date",
|
|
73
|
+
});
|
|
74
|
+
await Field.create({
|
|
75
|
+
table,
|
|
76
|
+
name: "agent_trigger_id",
|
|
77
|
+
label: "Agent trigger ID",
|
|
78
|
+
type: "Integer",
|
|
79
|
+
});
|
|
80
|
+
await Field.create({
|
|
81
|
+
table,
|
|
82
|
+
name: "memory_type",
|
|
83
|
+
label: "Memory type",
|
|
84
|
+
type: "String",
|
|
85
|
+
});
|
|
86
|
+
await Field.create({
|
|
87
|
+
table,
|
|
88
|
+
name: "personal",
|
|
89
|
+
label: "Personal",
|
|
90
|
+
type: "Bool",
|
|
91
|
+
});
|
|
92
|
+
await Field.create({
|
|
93
|
+
table,
|
|
94
|
+
name: "topic",
|
|
95
|
+
label: "Topic",
|
|
96
|
+
type: "String",
|
|
97
|
+
});
|
|
98
|
+
await Field.create({
|
|
99
|
+
table,
|
|
100
|
+
name: "contents",
|
|
101
|
+
label: "Contents",
|
|
102
|
+
type: "String",
|
|
103
|
+
});
|
|
104
|
+
await table.update({ ownership_field_id: uid_field.id });
|
|
105
|
+
await getState().refresh_tables();
|
|
106
|
+
return Table.findOne("AgentLongTermMemory");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
provideTools() {
|
|
110
|
+
return [
|
|
111
|
+
{
|
|
112
|
+
type: "function",
|
|
113
|
+
function: {
|
|
114
|
+
name: "store_in_memory",
|
|
115
|
+
description: `Store a fact or observation in long-term memory`,
|
|
116
|
+
parameters: {
|
|
117
|
+
type: "object",
|
|
118
|
+
required: ["contents"],
|
|
119
|
+
properties: {
|
|
120
|
+
contents: {
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "The contents of the fact or observations",
|
|
123
|
+
},
|
|
124
|
+
personal: {
|
|
125
|
+
type: "boolean",
|
|
126
|
+
description:
|
|
127
|
+
"Is this a fact or observation specifically about the person interacting with you now, which may not be true or relevant for another person",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
process: async (arg, { req, run }) => {
|
|
133
|
+
const table = await this.get_table();
|
|
134
|
+
await table.insertRow({
|
|
135
|
+
run_id: run.id,
|
|
136
|
+
user_id: req.user?.id,
|
|
137
|
+
written_at: new Date(),
|
|
138
|
+
agent_trigger_id: run.trigger_id,
|
|
139
|
+
memory_type: "Episodic",
|
|
140
|
+
contents: arg.contents,
|
|
141
|
+
personal: arg.personal,
|
|
142
|
+
});
|
|
143
|
+
return "Recorded";
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: "function",
|
|
148
|
+
process: async (arg, { req }) => {
|
|
149
|
+
const table = await this.get_table();
|
|
150
|
+
|
|
151
|
+
const scState = getState();
|
|
152
|
+
const language = scState.pg_ts_config;
|
|
153
|
+
const use_websearch = scState.getConfig(
|
|
154
|
+
"search_use_websearch",
|
|
155
|
+
false,
|
|
156
|
+
);
|
|
157
|
+
let rows = [];
|
|
158
|
+
const user_id = req.user?.id;
|
|
159
|
+
const phrases =
|
|
160
|
+
typeof arg.phrases === "string" ? [arg.phrases] : arg.phrases;
|
|
161
|
+
|
|
162
|
+
if (use_websearch)
|
|
163
|
+
rows = await table.getRows({
|
|
164
|
+
_fts: {
|
|
165
|
+
fields: table.fields,
|
|
166
|
+
searchTerm: phrases.join(" OR "),
|
|
167
|
+
language,
|
|
168
|
+
use_websearch,
|
|
169
|
+
table: table.name,
|
|
170
|
+
schema: db.isSQLite ? undefined : db.getTenantSchema(),
|
|
171
|
+
},
|
|
172
|
+
...(user_id
|
|
173
|
+
? { or: [{ personal: false }, { personal: true, user_id }] }
|
|
174
|
+
: [{ personal: false }]),
|
|
175
|
+
});
|
|
176
|
+
else
|
|
177
|
+
for (const phrase of phrases) {
|
|
178
|
+
const my_rows = await table.getRows({
|
|
179
|
+
_fts: {
|
|
180
|
+
fields: table.fields,
|
|
181
|
+
searchTerm: phrase,
|
|
182
|
+
language,
|
|
183
|
+
use_websearch,
|
|
184
|
+
table: table.name,
|
|
185
|
+
schema: db.isSQLite ? undefined : db.getTenantSchema(),
|
|
186
|
+
},
|
|
187
|
+
...(user_id
|
|
188
|
+
? { or: [{ personal: false }, { personal: true, user_id }] }
|
|
189
|
+
: [{ personal: false }]),
|
|
190
|
+
});
|
|
191
|
+
rows.push(...my_rows);
|
|
192
|
+
}
|
|
193
|
+
const pk = table.pk_name;
|
|
194
|
+
rows = nubBy((r) => r[pk], rows);
|
|
195
|
+
//TODO sort most recent, only N memories
|
|
196
|
+
if (rows.length)
|
|
197
|
+
return (
|
|
198
|
+
"These memories were retrieved:\n\n" +
|
|
199
|
+
rows.map((r) => r.contents).join("\n")
|
|
200
|
+
);
|
|
201
|
+
else
|
|
202
|
+
return "There are no memories related to: " + phrases.join(" or ");
|
|
203
|
+
},
|
|
204
|
+
function: {
|
|
205
|
+
name: "search_memory",
|
|
206
|
+
description: `Search the memory bank by a search phrase`,
|
|
207
|
+
parameters: {
|
|
208
|
+
type: "object",
|
|
209
|
+
required: ["phrases"],
|
|
210
|
+
description:
|
|
211
|
+
"Search the memory bank by any of a number of phrases. This will return any memories that matches one or the other of the phrases",
|
|
212
|
+
properties: {
|
|
213
|
+
phrases: {
|
|
214
|
+
type: "array",
|
|
215
|
+
description:
|
|
216
|
+
"A phrase to search the memory bank with. The search phrase is the synatx used by web search engines: use double quotes for exact match, unquoted text for words in any order, dash (minus sign) to exclude a word. Do not use SQL or any other formal query language.",
|
|
217
|
+
items: {
|
|
218
|
+
type: "string",
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
module.exports = LongTermMemory;
|