@istarry/dsh-assistant-profile 0.5.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -4
- package/cordis.patch.yml +2 -2
- package/lib/client.js +652 -39
- package/lib/client.js.map +1 -1
- package/lib/index.js +465 -53
- package/package.json +10 -8
package/lib/client.js
CHANGED
|
@@ -103,13 +103,13 @@ window.__ModuleLoader__.load({
|
|
|
103
103
|
];
|
|
104
104
|
//#endregion
|
|
105
105
|
//#region \0assistant-css:E:\Plugin\发布版\dsh-assistant-profile\src\client\ProfilePage.module.css.mjs
|
|
106
|
-
const css$
|
|
107
|
-
const tagId$
|
|
108
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$
|
|
106
|
+
const css$3 = "._2lYP0a_page{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);min-height:100%;padding:28px 32px 48px;overflow-y:auto}._2lYP0a_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:16px;margin:0 auto;display:flex}._2lYP0a_head{flex-direction:column;gap:4px;display:flex}._2lYP0a_title{margin:0;font-size:18px;font-weight:600;line-height:26px}._2lYP0a_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px;line-height:20px}._2lYP0a_form{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-xl);background:var(--dsw-alias-settings-card-fill);flex-direction:column;gap:18px;padding:20px;display:flex}._2lYP0a_row{grid-template-columns:minmax(0,1fr) minmax(0,240px);gap:16px;display:grid}@media (width<=640px){._2lYP0a_row{grid-template-columns:minmax(0,1fr)}}._2lYP0a_field{flex-direction:column;gap:6px;min-width:0;display:flex}._2lYP0a_label{font-size:13px;font-weight:500;line-height:18px}._2lYP0a_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}._2lYP0a_hintWarn{color:var(--dsw-alias-state-warn-label)}._2lYP0a_control{width:100%}._2lYP0a_textareaWrap{position:relative}._2lYP0a_textarea{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;min-height:168px;color:var(--dsw-alias-label-primary);resize:vertical;outline:none;padding:9px 12px 26px;font-family:inherit;font-size:13px;line-height:21px;display:block}._2lYP0a_textarea::placeholder{color:var(--dsw-alias-label-dimmed)}._2lYP0a_textarea:focus-visible{border-color:var(--dsw-alias-state-business-primary)}._2lYP0a_counter{color:var(--dsw-alias-label-tertiary);pointer-events:none;font-size:11px;line-height:14px;position:absolute;bottom:8px;right:10px}._2lYP0a_counter[data-long=true]{color:var(--dsw-alias-state-warn-label)}._2lYP0a_status{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}._2lYP0a_statusError{color:var(--dsw-alias-state-error-primary)}._2lYP0a_footer{align-items:center;gap:10px;padding-top:2px;display:flex}._2lYP0a_presets{scrollbar-width:thin;flex:1;align-items:center;gap:6px;min-width:0;padding:2px 0;display:flex;overflow-x:auto}._2lYP0a_chip{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:999px;flex:none;padding:3px 10px;font-size:12px;line-height:18px;transition:background .12s,border-color .12s,color .12s}._2lYP0a_chip:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}._2lYP0a_chip:disabled{cursor:default;opacity:.55}._2lYP0a_chipActive{border-color:var(--dsw-static-neutral-bluish-400);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}._2lYP0a_custom{border:.5px solid var(--dsw-alias-border-l3);background:0 0;border-radius:999px;flex:none;align-items:center;transition:background .12s,border-color .12s;display:inline-flex}._2lYP0a_custom:hover{background:var(--dsw-alias-interactive-bg-hover)}._2lYP0a_custom ._2lYP0a_chip{border:none;border-radius:999px 0 0 999px;padding-right:4px}._2lYP0a_customDelete{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:0 999px 999px 0;padding:3px 8px 3px 2px;font-size:13px;line-height:18px}._2lYP0a_customDelete:hover:not(:disabled){color:var(--dsw-alias-state-error-primary)}._2lYP0a_customDelete:disabled{cursor:default;opacity:.55}._2lYP0a_nameplate{box-sizing:border-box;color:var(--dsw-alias-label-primary);flex:none;justify-content:center;margin:12px 2px 0;padding:0 10px 8px;display:flex}._2lYP0a_nameplate[data-collapsed=true],._2lYP0a_nameplate[hidden]{display:none}._2lYP0a_nameplateText{text-align:center;text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;font-weight:500;line-height:22px;overflow:hidden}";
|
|
107
|
+
const tagId$3 = "@istarry/dsh-assistant-profile/ProfilePage.module.css";
|
|
108
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
|
|
109
109
|
const tag = document.createElement("style");
|
|
110
110
|
tag.dataset.plugin = "@istarry/dsh-assistant-profile";
|
|
111
|
-
tag.dataset.pluginCss = tagId$
|
|
112
|
-
tag.textContent = css$
|
|
111
|
+
tag.dataset.pluginCss = tagId$3;
|
|
112
|
+
tag.textContent = css$3;
|
|
113
113
|
document.head.appendChild(tag);
|
|
114
114
|
}
|
|
115
115
|
var ProfilePage_module_css_default = {
|
|
@@ -142,7 +142,7 @@ window.__ModuleLoader__.load({
|
|
|
142
142
|
//#endregion
|
|
143
143
|
//#region ../src/client/ProfilePage.tsx
|
|
144
144
|
/**
|
|
145
|
-
*
|
|
145
|
+
* Agent助理页:名称、语言、人格提示词三段,一次保存原子落盘。
|
|
146
146
|
*
|
|
147
147
|
* 页面只编辑、不预览:真正的注入发生在宿主侧(每个新会话的系统提示首段),
|
|
148
148
|
* 这里把「它会出现在哪里」写在每个字段的说明里,省掉一块会随时间漂移的假预览。
|
|
@@ -162,7 +162,7 @@ window.__ModuleLoader__.load({
|
|
|
162
162
|
"persona"
|
|
163
163
|
];
|
|
164
164
|
/**
|
|
165
|
-
*
|
|
165
|
+
* 渲染Agent助理页。
|
|
166
166
|
* @param props - 档案 store 与本地化文案。
|
|
167
167
|
* @returns 页面元素。
|
|
168
168
|
*/
|
|
@@ -680,13 +680,13 @@ window.__ModuleLoader__.load({
|
|
|
680
680
|
}
|
|
681
681
|
//#endregion
|
|
682
682
|
//#region \0assistant-css:E:\Plugin\发布版\dsh-assistant-profile\src\client\MemorySection.module.css.mjs
|
|
683
|
-
const css = "._26v_lW_page{flex-direction:column;gap:18px;padding:4px 0 24px;display:flex}._26v_lW_title{margin:0;font-size:16px;font-weight:600}._26v_lW_intro{opacity:.72;margin:0;font-size:13px;line-height:1.6}._26v_lW_group{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;gap:10px;padding:14px;display:flex}._26v_lW_groupHead{margin:0;font-size:13px;font-weight:600}._26v_lW_row{justify-content:space-between;align-items:center;gap:16px;padding:6px 0;display:flex}._26v_lW_rowText{flex-direction:column;gap:3px;display:flex}._26v_lW_rowTitle{font-size:13px}._26v_lW_rowDesc{opacity:.62;margin:0;font-size:12px;line-height:1.5}._26v_lW_switch{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-interactive-bg-default);cursor:pointer;border-radius:11px;flex:none;width:38px;height:22px;padding:0;transition:background .15s;position:relative}._26v_lW_switch:disabled{opacity:.45;cursor:not-allowed}._26v_lW_on{background:var(--dsw-static-deepseek-500,#4d6bfe);border-color:#0000}._26v_lW_knob{background:#fff;border-radius:50%;width:16px;height:16px;transition:transform .15s;position:absolute;top:2px;left:2px}._26v_lW_on ._26v_lW_knob{transform:translate(16px)}._26v_lW_editorHead{justify-content:space-between;align-items:baseline;display:flex}._26v_lW_textarea{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:100%;color:inherit;resize:vertical;border-radius:8px;padding:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.6}._26v_lW_actions{flex-wrap:wrap;align-items:center;gap:10px;display:flex}._26v_lW_primary,._26v_lW_secondary{cursor:pointer;border-radius:7px;padding:5px 14px;font-size:12px}._26v_lW_primary{background:var(--dsw-static-deepseek-500,#4d6bfe);color:#fff;border:1px solid #0000}._26v_lW_secondary{border:1px solid var(--dsw-alias-border-l1);color:inherit;background:0 0}._26v_lW_primary:disabled,._26v_lW_secondary:disabled{opacity:.45;cursor:not-allowed}._26v_lW_link{color:var(--dsw-static-deepseek-500,#4d6bfe);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}._26v_lW_ok{color:var(--dsw-static-deepseek-500,#4d6bfe);font-size:12px}._26v_lW_error{color:#d33;margin:0;font-size:12px}._26v_lW_overlay{z-index:60;background:#00000059;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}._26v_lW_dialog{background:var(--dsw-alias-bg-overlay,#fff);border-radius:12px;flex-direction:column;gap:12px;width:min(560px,92vw);max-height:84vh;padding:18px;display:flex;overflow:auto;box-shadow:0 12px 40px #00000038}._26v_lW_dialogTitle{margin:0;font-size:14px;font-weight:600}";
|
|
684
|
-
const tagId = "@istarry/dsh-assistant-profile/MemorySection.module.css";
|
|
685
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
683
|
+
const css$2 = "._26v_lW_page{flex-direction:column;gap:18px;padding:4px 0 24px;display:flex}._26v_lW_title{margin:0;font-size:16px;font-weight:600}._26v_lW_intro{opacity:.72;margin:0;font-size:13px;line-height:1.6}._26v_lW_group{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;gap:10px;padding:14px;display:flex}._26v_lW_groupHead{margin:0;font-size:13px;font-weight:600}._26v_lW_row{justify-content:space-between;align-items:center;gap:16px;padding:6px 0;display:flex}._26v_lW_rowText{flex-direction:column;gap:3px;display:flex}._26v_lW_rowTitle{font-size:13px}._26v_lW_rowDesc{opacity:.62;margin:0;font-size:12px;line-height:1.5}._26v_lW_switch{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-interactive-bg-default);cursor:pointer;border-radius:11px;flex:none;width:38px;height:22px;padding:0;transition:background .15s;position:relative}._26v_lW_switch:disabled{opacity:.45;cursor:not-allowed}._26v_lW_on{background:var(--dsw-static-deepseek-500,#4d6bfe);border-color:#0000}._26v_lW_knob{background:#fff;border-radius:50%;width:16px;height:16px;transition:transform .15s;position:absolute;top:2px;left:2px}._26v_lW_on ._26v_lW_knob{transform:translate(16px)}._26v_lW_editorHead{justify-content:space-between;align-items:baseline;display:flex}._26v_lW_textarea{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:100%;color:inherit;resize:vertical;border-radius:8px;padding:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.6}._26v_lW_actions{flex-wrap:wrap;align-items:center;gap:10px;display:flex}._26v_lW_primary,._26v_lW_secondary{cursor:pointer;border-radius:7px;padding:5px 14px;font-size:12px}._26v_lW_primary{background:var(--dsw-static-deepseek-500,#4d6bfe);color:#fff;border:1px solid #0000}._26v_lW_secondary{border:1px solid var(--dsw-alias-border-l1);color:inherit;background:0 0}._26v_lW_primary:disabled,._26v_lW_secondary:disabled{opacity:.45;cursor:not-allowed}._26v_lW_link{color:var(--dsw-static-deepseek-500,#4d6bfe);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}._26v_lW_ok{color:var(--dsw-static-deepseek-500,#4d6bfe);font-size:12px}._26v_lW_error{color:#d33;margin:0;font-size:12px}._26v_lW_overlay{z-index:60;background:#00000059;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}._26v_lW_dialog{background:var(--dsw-alias-bg-overlay,#fff);border-radius:12px;flex-direction:column;gap:12px;width:min(560px,92vw);max-height:84vh;padding:18px;display:flex;overflow:auto;box-shadow:0 12px 40px #00000038}._26v_lW_dialogTitle{margin:0;font-size:14px;font-weight:600}";
|
|
684
|
+
const tagId$2 = "@istarry/dsh-assistant-profile/MemorySection.module.css";
|
|
685
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
686
686
|
const tag = document.createElement("style");
|
|
687
687
|
tag.dataset.plugin = "@istarry/dsh-assistant-profile";
|
|
688
|
-
tag.dataset.pluginCss = tagId;
|
|
689
|
-
tag.textContent = css;
|
|
688
|
+
tag.dataset.pluginCss = tagId$2;
|
|
689
|
+
tag.textContent = css$2;
|
|
690
690
|
document.head.appendChild(tag);
|
|
691
691
|
}
|
|
692
692
|
var MemorySection_module_css_default = {
|
|
@@ -861,6 +861,556 @@ window.__ModuleLoader__.load({
|
|
|
861
861
|
});
|
|
862
862
|
}
|
|
863
863
|
//#endregion
|
|
864
|
+
//#region \0assistant-css:E:\Plugin\发布版\dsh-assistant-profile\src\client\MemoryActions.module.css.mjs
|
|
865
|
+
const css$1 = ".BvVp9q_action{width:calc(28px + var(--dsh-content-font-delta,0px));height:calc(28px + var(--dsh-content-font-delta,0px));border-radius:var(--dsw-radius-sm);color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:6px;display:inline-flex}.BvVp9q_action svg{width:calc(15px + var(--dsh-content-font-delta,0px));height:calc(15px + var(--dsh-content-font-delta,0px))}.BvVp9q_action:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.BvVp9q_note{color:var(--dsw-alias-label-tertiary);padding-left:4px;font-size:12px;line-height:20px}.BvVp9q_list{flex-direction:column;gap:2px;max-height:min(52vh,460px);margin:-4px -4px 0;padding:4px;display:flex;overflow-y:auto}.BvVp9q_row{border-radius:var(--dsw-radius-sm);cursor:pointer;align-items:flex-start;gap:8px;padding:7px 8px;display:flex}.BvVp9q_row:hover{background:var(--dsw-alias-interactive-bg-hover)}.BvVp9q_rowChecked{background:var(--dsw-alias-bg-module-platform)}.BvVp9q_check{accent-color:var(--dsw-alias-state-business-primary);flex:none;margin:3px 0 0}.BvVp9q_side{background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:999px;flex:none;padding:1px 6px;font-size:11px;line-height:17px}.BvVp9q_preview{min-width:0;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;font-size:13px;line-height:20px}.BvVp9q_empty{color:var(--dsw-alias-label-tertiary);text-align:center;padding:18px 8px;font-size:13px;line-height:20px}.BvVp9q_error{color:var(--dsw-alias-state-error-primary);margin-right:auto;font-size:12px;line-height:18px}";
|
|
866
|
+
const tagId$1 = "@istarry/dsh-assistant-profile/MemoryActions.module.css";
|
|
867
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
868
|
+
const tag = document.createElement("style");
|
|
869
|
+
tag.dataset.plugin = "@istarry/dsh-assistant-profile";
|
|
870
|
+
tag.dataset.pluginCss = tagId$1;
|
|
871
|
+
tag.textContent = css$1;
|
|
872
|
+
document.head.appendChild(tag);
|
|
873
|
+
}
|
|
874
|
+
var MemoryActions_module_css_default = {
|
|
875
|
+
"action": "BvVp9q_action",
|
|
876
|
+
"check": "BvVp9q_check",
|
|
877
|
+
"empty": "BvVp9q_empty",
|
|
878
|
+
"error": "BvVp9q_error",
|
|
879
|
+
"list": "BvVp9q_list",
|
|
880
|
+
"note": "BvVp9q_note",
|
|
881
|
+
"preview": "BvVp9q_preview",
|
|
882
|
+
"row": "BvVp9q_row",
|
|
883
|
+
"rowChecked": "BvVp9q_rowChecked",
|
|
884
|
+
"side": "BvVp9q_side"
|
|
885
|
+
};
|
|
886
|
+
//#endregion
|
|
887
|
+
//#region ../src/client/MemoryActions.tsx
|
|
888
|
+
/**
|
|
889
|
+
* 聊天里的「记住」:助理消息动作行上的一个按钮 + 一个勾选弹窗。
|
|
890
|
+
*
|
|
891
|
+
* 勾中的消息会按时间顺序拼成「用户:…/助手:…」的文本,交给宿主提炼成长期记忆。
|
|
892
|
+
* 按钮本身不写任何东西:只有人勾选并提交,才会产生一次提炼。
|
|
893
|
+
*
|
|
894
|
+
* @module @istarry/dsh-assistant-profile/client/MemoryActions
|
|
895
|
+
*/
|
|
896
|
+
/**
|
|
897
|
+
* 从助手消息的块里取正文。
|
|
898
|
+
* @param blocks - 消息块。
|
|
899
|
+
* @returns 拼起来的正文。
|
|
900
|
+
*/
|
|
901
|
+
function assistantText(blocks) {
|
|
902
|
+
return blocks.flatMap((block) => (block.kind === "text" || block.type === "text") && typeof block.text === "string" ? [block.text] : []).join("");
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* 从用户消息的内容里取正文。
|
|
906
|
+
* @param content - 消息内容块。
|
|
907
|
+
* @returns 拼起来的正文。
|
|
908
|
+
*/
|
|
909
|
+
function contentText(content) {
|
|
910
|
+
return content.flatMap((block) => block.type === "text" && typeof block.text === "string" ? [block.text] : []).join("");
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* 压成一行摘要。
|
|
914
|
+
* @param text - 原文。
|
|
915
|
+
* @param limit - 字数上限。
|
|
916
|
+
* @returns 摘要。
|
|
917
|
+
*/
|
|
918
|
+
function preview(text, limit = 120) {
|
|
919
|
+
const trimmed = text.replace(/\s+/g, " ").trim();
|
|
920
|
+
return trimmed.length <= limit ? trimmed : `${trimmed.slice(0, limit)}…`;
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* 从会话快照里挑出可提炼的消息:用户消息与已完成的助手消息,按时间线排序。
|
|
924
|
+
* 运行中的回复、工具行、命令与上下文快照都没有可复用的正文,跳过。
|
|
925
|
+
* @param snapshot - 当前会话的聊天快照。
|
|
926
|
+
* @returns 候选列表。
|
|
927
|
+
*/
|
|
928
|
+
function candidatesOf(snapshot) {
|
|
929
|
+
const store = snapshot?.nodes;
|
|
930
|
+
const nodes = store === void 0 || store === null ? [] : typeof store.values === "function" ? store.values() : store instanceof Map ? [...store.values()] : Object.values(store);
|
|
931
|
+
const list = [];
|
|
932
|
+
for (const raw of nodes) {
|
|
933
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
934
|
+
const node = raw;
|
|
935
|
+
const key = node.key ?? node.id;
|
|
936
|
+
if (key === void 0) continue;
|
|
937
|
+
if (node.visibility === "hidden") continue;
|
|
938
|
+
const anchorSeq = typeof node.anchorSeq === "number" ? node.anchorSeq : 0;
|
|
939
|
+
if (node.kind === "user" || node.kind === "steering") {
|
|
940
|
+
const content = node.data?.content ?? [];
|
|
941
|
+
const text = Array.isArray(content) ? contentText(content) : "";
|
|
942
|
+
if (text.trim() === "") continue;
|
|
943
|
+
list.push({
|
|
944
|
+
key,
|
|
945
|
+
anchorSeq,
|
|
946
|
+
side: "user",
|
|
947
|
+
preview: preview(text),
|
|
948
|
+
text
|
|
949
|
+
});
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
if (node.kind === "assistant-step") {
|
|
953
|
+
if (node.data?.status === "running") continue;
|
|
954
|
+
const blocks = node.data?.blocks ?? [];
|
|
955
|
+
const text = Array.isArray(blocks) ? assistantText(blocks) : "";
|
|
956
|
+
if (text.trim() === "") continue;
|
|
957
|
+
list.push({
|
|
958
|
+
key,
|
|
959
|
+
anchorSeq,
|
|
960
|
+
side: "assistant",
|
|
961
|
+
preview: preview(text),
|
|
962
|
+
text
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return list.sort((left, right) => left.anchorSeq - right.anchorSeq);
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* 动作行上的「记住」入口,以及提交成功后的短暂提示。
|
|
970
|
+
* @param props - 注入的提炼动作与会话运行时。
|
|
971
|
+
* @returns 按钮与可能展开的弹窗。
|
|
972
|
+
*/
|
|
973
|
+
function MemoryActions({ useChat, remember, t }) {
|
|
974
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
975
|
+
const [note, setNote] = (0, react.useState)(null);
|
|
976
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
977
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
978
|
+
label: t("remember.action"),
|
|
979
|
+
side: "bottom",
|
|
980
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
981
|
+
type: "button",
|
|
982
|
+
className: MemoryActions_module_css_default.action,
|
|
983
|
+
"aria-label": t("remember.action"),
|
|
984
|
+
onClick: () => {
|
|
985
|
+
setNote(null);
|
|
986
|
+
setOpen(true);
|
|
987
|
+
},
|
|
988
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSparkleRegular, {})
|
|
989
|
+
})
|
|
990
|
+
}),
|
|
991
|
+
note !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
992
|
+
className: MemoryActions_module_css_default.note,
|
|
993
|
+
role: "status",
|
|
994
|
+
children: note
|
|
995
|
+
}),
|
|
996
|
+
open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoryDialog, {
|
|
997
|
+
useChat,
|
|
998
|
+
remember,
|
|
999
|
+
t,
|
|
1000
|
+
onClose: (saved) => {
|
|
1001
|
+
setOpen(false);
|
|
1002
|
+
if (saved) setNote(t("remember.saved"));
|
|
1003
|
+
}
|
|
1004
|
+
})
|
|
1005
|
+
] });
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* 消息勾选弹窗。
|
|
1009
|
+
* @param props - 会话选择器、提炼动作与文案。
|
|
1010
|
+
* @returns 弹窗元素。
|
|
1011
|
+
*/
|
|
1012
|
+
function MemoryDialog({ useChat, remember, onClose, t }) {
|
|
1013
|
+
const snapshot = useChat((state) => state);
|
|
1014
|
+
const candidates = (0, react.useMemo)(() => candidatesOf(snapshot), [snapshot]);
|
|
1015
|
+
const [checked, setChecked] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
1016
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
1017
|
+
const [error, setError] = (0, react.useState)(null);
|
|
1018
|
+
const toggle = (key) => {
|
|
1019
|
+
const next = new Set(checked);
|
|
1020
|
+
if (next.has(key)) next.delete(key);
|
|
1021
|
+
else next.add(key);
|
|
1022
|
+
setChecked(next);
|
|
1023
|
+
};
|
|
1024
|
+
/** 把宿主的结果原因翻成界面文案。 */
|
|
1025
|
+
const outcomeText = (outcome) => {
|
|
1026
|
+
if (outcome === "session not open") return t("remember.notOpen");
|
|
1027
|
+
if (outcome === "no provider/model available") return t("remember.noModel");
|
|
1028
|
+
if (outcome === "empty selection" || outcome === "nothing worth remembering") return t("remember.nothing");
|
|
1029
|
+
if (outcome === "timed out") return t("remember.timeout");
|
|
1030
|
+
if (outcome === "settings write refused") return t("remember.refused");
|
|
1031
|
+
return outcome === "" ? t("remember.failed") : outcome;
|
|
1032
|
+
};
|
|
1033
|
+
const submit = async () => {
|
|
1034
|
+
if (busy) return;
|
|
1035
|
+
const text = candidates.filter((item) => checked.has(item.key)).map((item) => `${item.side === "user" ? t("remember.side.user") : t("remember.side.assistant")}:${item.text}`).join("\n\n");
|
|
1036
|
+
if (text.trim() === "") return;
|
|
1037
|
+
setBusy(true);
|
|
1038
|
+
setError(null);
|
|
1039
|
+
try {
|
|
1040
|
+
const result = await remember(text);
|
|
1041
|
+
if (result.saved) {
|
|
1042
|
+
onClose(true);
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
setError(outcomeText(result.outcome));
|
|
1046
|
+
} catch (caught) {
|
|
1047
|
+
setError(caught instanceof Error ? caught.message : t("remember.failed"));
|
|
1048
|
+
} finally {
|
|
1049
|
+
setBusy(false);
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1053
|
+
open: true,
|
|
1054
|
+
onClose: () => {
|
|
1055
|
+
if (!busy) onClose(false);
|
|
1056
|
+
},
|
|
1057
|
+
closeLabel: t("remember.cancel"),
|
|
1058
|
+
title: t("remember.title"),
|
|
1059
|
+
description: t("remember.subtitle"),
|
|
1060
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1061
|
+
error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1062
|
+
className: MemoryActions_module_css_default.error,
|
|
1063
|
+
role: "alert",
|
|
1064
|
+
children: error
|
|
1065
|
+
}),
|
|
1066
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1067
|
+
variant: "outline",
|
|
1068
|
+
disabled: busy,
|
|
1069
|
+
onClick: () => {
|
|
1070
|
+
onClose(false);
|
|
1071
|
+
},
|
|
1072
|
+
children: t("remember.cancel")
|
|
1073
|
+
}),
|
|
1074
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1075
|
+
variant: "primary",
|
|
1076
|
+
disabled: busy || checked.size === 0,
|
|
1077
|
+
onClick: () => {
|
|
1078
|
+
submit();
|
|
1079
|
+
},
|
|
1080
|
+
children: busy ? t("remember.submitting") : t("remember.submit")
|
|
1081
|
+
})
|
|
1082
|
+
] }),
|
|
1083
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1084
|
+
className: MemoryActions_module_css_default.list,
|
|
1085
|
+
children: [candidates.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1086
|
+
className: MemoryActions_module_css_default.empty,
|
|
1087
|
+
children: t("remember.empty")
|
|
1088
|
+
}), candidates.map((item) => {
|
|
1089
|
+
const active = checked.has(item.key);
|
|
1090
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1091
|
+
className: active ? `${MemoryActions_module_css_default.row} ${MemoryActions_module_css_default.rowChecked}` : MemoryActions_module_css_default.row,
|
|
1092
|
+
children: [
|
|
1093
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1094
|
+
type: "checkbox",
|
|
1095
|
+
className: MemoryActions_module_css_default.check,
|
|
1096
|
+
checked: active,
|
|
1097
|
+
onChange: () => {
|
|
1098
|
+
toggle(item.key);
|
|
1099
|
+
}
|
|
1100
|
+
}),
|
|
1101
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1102
|
+
className: MemoryActions_module_css_default.side,
|
|
1103
|
+
children: item.side === "user" ? t("remember.side.user") : t("remember.side.assistant")
|
|
1104
|
+
}),
|
|
1105
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1106
|
+
className: MemoryActions_module_css_default.preview,
|
|
1107
|
+
children: item.preview
|
|
1108
|
+
})
|
|
1109
|
+
]
|
|
1110
|
+
}, item.key);
|
|
1111
|
+
})]
|
|
1112
|
+
})
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
//#endregion
|
|
1116
|
+
//#region \0assistant-css:E:\Plugin\发布版\dsh-assistant-profile\src\client\DeleteSession.module.css.mjs
|
|
1117
|
+
const css = "._2WqL1q_body{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;margin:0;font-size:14px;line-height:22px}._2WqL1q_hint{color:var(--dsw-alias-label-tertiary);margin:8px 0 0;font-size:12px;line-height:18px}._2WqL1q_error{color:var(--dsw-alias-state-error-primary);margin-right:auto;font-size:12px;line-height:18px}";
|
|
1118
|
+
const tagId = "@istarry/dsh-assistant-profile/DeleteSession.module.css";
|
|
1119
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1120
|
+
const tag = document.createElement("style");
|
|
1121
|
+
tag.dataset.plugin = "@istarry/dsh-assistant-profile";
|
|
1122
|
+
tag.dataset.pluginCss = tagId;
|
|
1123
|
+
tag.textContent = css;
|
|
1124
|
+
document.head.appendChild(tag);
|
|
1125
|
+
}
|
|
1126
|
+
var DeleteSession_module_css_default = {
|
|
1127
|
+
"body": "_2WqL1q_body",
|
|
1128
|
+
"error": "_2WqL1q_error",
|
|
1129
|
+
"hint": "_2WqL1q_hint"
|
|
1130
|
+
};
|
|
1131
|
+
//#endregion
|
|
1132
|
+
//#region ../src/client/DeleteSession.tsx
|
|
1133
|
+
/**
|
|
1134
|
+
* 侧栏会话行菜单里的「删除」:删的是这个会话本身——磁盘日志、索引、工作区登记,
|
|
1135
|
+
* 以及前端的那一行。
|
|
1136
|
+
*
|
|
1137
|
+
* 菜单项和确认框必须分开:菜单一关,菜单里的组件就整棵卸载了,弹窗会跟着消失。
|
|
1138
|
+
* 所以菜单项只把「想删谁」写进模块级的 pending,确认框挂在 `shell.overlay` 上读它——
|
|
1139
|
+
* 官方「停止并归档」也是这个结构。
|
|
1140
|
+
*
|
|
1141
|
+
* @module @istarry/dsh-assistant-profile/client/DeleteSession
|
|
1142
|
+
*/
|
|
1143
|
+
/** 模块级待办:菜单项写,确认框读(两个插槽不共享 React 树)。 */
|
|
1144
|
+
let pending;
|
|
1145
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1146
|
+
/** 通知订阅者。 */
|
|
1147
|
+
function publish() {
|
|
1148
|
+
for (const listener of [...listeners]) listener();
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* 记下要删的会话并唤起确认框。
|
|
1152
|
+
* @param sessionId - 目标会话。
|
|
1153
|
+
* @param title - 行标题。
|
|
1154
|
+
*/
|
|
1155
|
+
function askDelete(sessionId, title) {
|
|
1156
|
+
pending = {
|
|
1157
|
+
sessionId,
|
|
1158
|
+
title
|
|
1159
|
+
};
|
|
1160
|
+
publish();
|
|
1161
|
+
}
|
|
1162
|
+
/** 关掉确认框。 */
|
|
1163
|
+
function dismiss() {
|
|
1164
|
+
pending = void 0;
|
|
1165
|
+
publish();
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* 订阅待办。
|
|
1169
|
+
* @param listener - 变更回调。
|
|
1170
|
+
* @returns 取消订阅。
|
|
1171
|
+
*/
|
|
1172
|
+
function subscribe(listener) {
|
|
1173
|
+
listeners.add(listener);
|
|
1174
|
+
return () => {
|
|
1175
|
+
listeners.delete(listener);
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
/** @returns 当前待办(引用稳定,只有开关时变)。 */
|
|
1179
|
+
const snapshot = () => pending;
|
|
1180
|
+
/**
|
|
1181
|
+
* 菜单行(order 600,带分隔线)。只负责关菜单并把目标交给确认框。
|
|
1182
|
+
* @param props - 会话 id、标题、菜单开合与文案。
|
|
1183
|
+
* @returns 一行「删除」。
|
|
1184
|
+
*/
|
|
1185
|
+
function DeleteSessionMenuItem({ sessionId, displayTitle, useMenuOpenState, t }) {
|
|
1186
|
+
const [, setMenuOpen] = useMenuOpenState();
|
|
1187
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MenuItemButton, {
|
|
1188
|
+
danger: true,
|
|
1189
|
+
separatorBefore: true,
|
|
1190
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 14 }),
|
|
1191
|
+
onSelect: () => {
|
|
1192
|
+
setMenuOpen(false);
|
|
1193
|
+
askDelete(sessionId, displayTitle);
|
|
1194
|
+
},
|
|
1195
|
+
children: t("delete.menu")
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* `shell.overlay` 上的确认框。
|
|
1200
|
+
* @param props - 删除动作与文案。
|
|
1201
|
+
* @returns 弹窗;没有待办时什么都不渲染。
|
|
1202
|
+
*/
|
|
1203
|
+
function DeleteSessionDialog({ deleteSession, t }) {
|
|
1204
|
+
const target = (0, react.useSyncExternalStore)(subscribe, snapshot);
|
|
1205
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
1206
|
+
const [error, setError] = (0, react.useState)(null);
|
|
1207
|
+
if (target === void 0) return null;
|
|
1208
|
+
/** 把宿主的原因码翻成界面文案。 */
|
|
1209
|
+
const reasonOf = (outcome) => {
|
|
1210
|
+
if (outcome === "running") return t("delete.running");
|
|
1211
|
+
if (outcome === "open") return t("delete.open");
|
|
1212
|
+
if (outcome === "not found") return t("delete.notFound");
|
|
1213
|
+
if (outcome === "delete failed") return t("delete.ioError");
|
|
1214
|
+
if (outcome === "timed out") return t("delete.timeout");
|
|
1215
|
+
if (outcome === "settings write refused") return t("delete.refused");
|
|
1216
|
+
return outcome === "" ? t("delete.failed") : outcome;
|
|
1217
|
+
};
|
|
1218
|
+
const close = () => {
|
|
1219
|
+
if (busy) return;
|
|
1220
|
+
setError(null);
|
|
1221
|
+
dismiss();
|
|
1222
|
+
};
|
|
1223
|
+
const submit = async () => {
|
|
1224
|
+
if (busy) return;
|
|
1225
|
+
setBusy(true);
|
|
1226
|
+
setError(null);
|
|
1227
|
+
try {
|
|
1228
|
+
const result = await deleteSession(target.sessionId);
|
|
1229
|
+
if (result.ok) {
|
|
1230
|
+
dismiss();
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
setError(reasonOf(result.outcome));
|
|
1234
|
+
} catch (caught) {
|
|
1235
|
+
setError(caught instanceof Error ? caught.message : t("delete.failed"));
|
|
1236
|
+
} finally {
|
|
1237
|
+
setBusy(false);
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1241
|
+
open: true,
|
|
1242
|
+
title: t("delete.title"),
|
|
1243
|
+
closeLabel: t("delete.cancel"),
|
|
1244
|
+
onClose: close,
|
|
1245
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1246
|
+
error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1247
|
+
className: DeleteSession_module_css_default.error,
|
|
1248
|
+
role: "alert",
|
|
1249
|
+
children: error
|
|
1250
|
+
}),
|
|
1251
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1252
|
+
variant: "outline",
|
|
1253
|
+
disabled: busy,
|
|
1254
|
+
onClick: close,
|
|
1255
|
+
children: t("delete.cancel")
|
|
1256
|
+
}),
|
|
1257
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1258
|
+
variant: "primary",
|
|
1259
|
+
disabled: busy,
|
|
1260
|
+
onClick: () => {
|
|
1261
|
+
submit();
|
|
1262
|
+
},
|
|
1263
|
+
children: busy ? t("delete.busy") : t("delete.confirm")
|
|
1264
|
+
})
|
|
1265
|
+
] }),
|
|
1266
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1267
|
+
className: DeleteSession_module_css_default.body,
|
|
1268
|
+
children: t("delete.body", { title: target.title })
|
|
1269
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1270
|
+
className: DeleteSession_module_css_default.hint,
|
|
1271
|
+
children: t("delete.hint")
|
|
1272
|
+
})]
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
//#endregion
|
|
1276
|
+
//#region ../src/client/delete.ts
|
|
1277
|
+
/** 信箱字段名。 */
|
|
1278
|
+
const SIGNAL_FIELD$1 = "deleteSignal";
|
|
1279
|
+
/** 等结果的上限:删文件是毫秒级的,给足余量但不至于让人干等。 */
|
|
1280
|
+
const TIMEOUT_MS$1 = 2e4;
|
|
1281
|
+
/**
|
|
1282
|
+
* 解析信箱字段。
|
|
1283
|
+
* @param value - 字段原文。
|
|
1284
|
+
* @returns 解析结果;坏数据当空。
|
|
1285
|
+
*/
|
|
1286
|
+
function parseSignal$1(value) {
|
|
1287
|
+
if (typeof value !== "string" || value.trim() === "") return void 0;
|
|
1288
|
+
try {
|
|
1289
|
+
const parsed = JSON.parse(value);
|
|
1290
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
1291
|
+
return parsed;
|
|
1292
|
+
} catch {
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* 请求删除一个会话并等待宿主回话。
|
|
1298
|
+
* @param form - 本插件的设置表(`deleteSignal` 所在命名空间)。
|
|
1299
|
+
* @param sessionId - 要删除的会话。
|
|
1300
|
+
* @returns 删除结果。
|
|
1301
|
+
*/
|
|
1302
|
+
async function requestDelete(form, sessionId) {
|
|
1303
|
+
const id = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1304
|
+
if (!await form.set(SIGNAL_FIELD$1, JSON.stringify({
|
|
1305
|
+
id,
|
|
1306
|
+
phase: "request",
|
|
1307
|
+
sessionId
|
|
1308
|
+
}))) return {
|
|
1309
|
+
ok: false,
|
|
1310
|
+
outcome: "settings write refused"
|
|
1311
|
+
};
|
|
1312
|
+
return await new Promise((resolve) => {
|
|
1313
|
+
let settled = false;
|
|
1314
|
+
let timer;
|
|
1315
|
+
let unsubscribe = () => {};
|
|
1316
|
+
const finish = (value) => {
|
|
1317
|
+
if (settled) return;
|
|
1318
|
+
settled = true;
|
|
1319
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1320
|
+
unsubscribe();
|
|
1321
|
+
resolve(value);
|
|
1322
|
+
};
|
|
1323
|
+
const check = () => {
|
|
1324
|
+
const signal = parseSignal$1(form.getSnapshot().value?.[SIGNAL_FIELD$1]);
|
|
1325
|
+
if (signal?.phase !== "done" || signal.id !== id) return;
|
|
1326
|
+
finish({
|
|
1327
|
+
ok: signal.ok === true,
|
|
1328
|
+
outcome: signal.outcome ?? ""
|
|
1329
|
+
});
|
|
1330
|
+
};
|
|
1331
|
+
unsubscribe = form.subscribe(check);
|
|
1332
|
+
timer = setTimeout(() => {
|
|
1333
|
+
finish({
|
|
1334
|
+
ok: false,
|
|
1335
|
+
outcome: "timed out"
|
|
1336
|
+
});
|
|
1337
|
+
}, TIMEOUT_MS$1);
|
|
1338
|
+
check();
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
//#endregion
|
|
1342
|
+
//#region ../src/client/remember.ts
|
|
1343
|
+
/** 信箱字段名。 */
|
|
1344
|
+
const SIGNAL_FIELD = "rememberSignal";
|
|
1345
|
+
/** 等结果的上限:提炼是一次完整模型调用,给足时间。 */
|
|
1346
|
+
const TIMEOUT_MS = 12e4;
|
|
1347
|
+
/** 一次请求最多带多少字符,与宿主的截断保持一致。 */
|
|
1348
|
+
const TEXT_LIMIT = 2e4;
|
|
1349
|
+
/**
|
|
1350
|
+
* 解析信箱字段。
|
|
1351
|
+
* @param value - 字段原文。
|
|
1352
|
+
* @returns 解析结果;坏数据当空。
|
|
1353
|
+
*/
|
|
1354
|
+
function parseSignal(value) {
|
|
1355
|
+
if (typeof value !== "string" || value.trim() === "") return void 0;
|
|
1356
|
+
try {
|
|
1357
|
+
const parsed = JSON.parse(value);
|
|
1358
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
1359
|
+
return parsed;
|
|
1360
|
+
} catch {
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* 投递一次提炼请求并等待宿主回话。
|
|
1366
|
+
* @param form - 本插件的设置表(`rememberSignal` 所在的命名空间)。
|
|
1367
|
+
* @param sessionId - 用哪个会话的模型跑这次提炼。
|
|
1368
|
+
* @param text - 选中的对话文本(已带「用户:/助手:」前缀)。
|
|
1369
|
+
* @returns 提炼结果;超时或被拒绝时给失败结果。
|
|
1370
|
+
*/
|
|
1371
|
+
async function requestRemember(form, sessionId, text) {
|
|
1372
|
+
const id = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1373
|
+
const request = JSON.stringify({
|
|
1374
|
+
id,
|
|
1375
|
+
phase: "request",
|
|
1376
|
+
sessionId,
|
|
1377
|
+
text: text.slice(0, TEXT_LIMIT)
|
|
1378
|
+
});
|
|
1379
|
+
if (!await form.set(SIGNAL_FIELD, request)) return {
|
|
1380
|
+
saved: false,
|
|
1381
|
+
outcome: "settings write refused"
|
|
1382
|
+
};
|
|
1383
|
+
return await new Promise((resolve) => {
|
|
1384
|
+
let settled = false;
|
|
1385
|
+
let timer;
|
|
1386
|
+
let unsubscribe = () => {};
|
|
1387
|
+
const finish = (value) => {
|
|
1388
|
+
if (settled) return;
|
|
1389
|
+
settled = true;
|
|
1390
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1391
|
+
unsubscribe();
|
|
1392
|
+
resolve(value);
|
|
1393
|
+
};
|
|
1394
|
+
const check = () => {
|
|
1395
|
+
const signal = parseSignal(form.getSnapshot().value?.[SIGNAL_FIELD]);
|
|
1396
|
+
if (signal?.phase !== "done" || signal.id !== id) return;
|
|
1397
|
+
finish({
|
|
1398
|
+
saved: signal.saved === true,
|
|
1399
|
+
...signal.entries === void 0 ? {} : { entries: signal.entries },
|
|
1400
|
+
outcome: signal.outcome ?? ""
|
|
1401
|
+
});
|
|
1402
|
+
};
|
|
1403
|
+
unsubscribe = form.subscribe(check);
|
|
1404
|
+
timer = setTimeout(() => {
|
|
1405
|
+
finish({
|
|
1406
|
+
saved: false,
|
|
1407
|
+
outcome: "timed out"
|
|
1408
|
+
});
|
|
1409
|
+
}, TIMEOUT_MS);
|
|
1410
|
+
check();
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
//#endregion
|
|
864
1414
|
//#region ../src/client/memoryStore.ts
|
|
865
1415
|
/** 把持久化配置解码成三个字段(形状不对时给安全默认值)。 */
|
|
866
1416
|
function decodeConfig(section) {
|
|
@@ -950,15 +1500,15 @@ window.__ModuleLoader__.load({
|
|
|
950
1500
|
//#endregion
|
|
951
1501
|
//#region ../src/client/locales.ts
|
|
952
1502
|
/**
|
|
953
|
-
*
|
|
1503
|
+
* 词典:Agent助理页与侧栏入口的文案。
|
|
954
1504
|
*
|
|
955
1505
|
* 只放界面文案——名称、语言、人格是用户自己的内容,不进词典;
|
|
956
1506
|
* 三个内置人格的标签是产品名,也在这里。
|
|
957
1507
|
*/
|
|
958
1508
|
/** 中文词典(键的来源)。 */
|
|
959
1509
|
const zh = {
|
|
960
|
-
"panel": "
|
|
961
|
-
"title": "
|
|
1510
|
+
"panel": "Agent助理",
|
|
1511
|
+
"title": "Agent助理",
|
|
962
1512
|
"intro": "这份人格会出现在每个新会话的系统提示最前面,标准 / PTC / 极简 / 创造四个模式通用。",
|
|
963
1513
|
"name.label": "名称",
|
|
964
1514
|
"name.placeholder": "例如:大肥鱼",
|
|
@@ -996,25 +1546,45 @@ window.__ModuleLoader__.load({
|
|
|
996
1546
|
"memory.editor": "记忆内容",
|
|
997
1547
|
"memory.editor.hint": "每行一条,格式「- [时间] 内容」;也可以写普通段落,模型照样会读到。",
|
|
998
1548
|
"memory.editor.placeholder": "- [2026-10-24 12:00] 用户偏好简洁的中文回答",
|
|
999
|
-
"memory.reload": "重新载入",
|
|
1000
1549
|
"memory.save": "保存记忆",
|
|
1001
1550
|
"memory.saving": "保存中…",
|
|
1002
1551
|
"memory.saved": "已保存",
|
|
1003
1552
|
"memory.saveFailed": "保存失败",
|
|
1004
|
-
"
|
|
1005
|
-
"
|
|
1006
|
-
"
|
|
1007
|
-
"
|
|
1008
|
-
"
|
|
1009
|
-
"
|
|
1010
|
-
"
|
|
1011
|
-
"
|
|
1012
|
-
"
|
|
1553
|
+
"delete.menu": "删除",
|
|
1554
|
+
"delete.title": "删除这个会话?",
|
|
1555
|
+
"delete.body": "“{title}”的聊天记录会从本地磁盘删除,和它一起消失的还有索引与工作区登记。",
|
|
1556
|
+
"delete.hint": "这一步不能撤销;只想让它从侧栏消失的话,用「归档会话」。",
|
|
1557
|
+
"delete.confirm": "删除",
|
|
1558
|
+
"delete.cancel": "取消",
|
|
1559
|
+
"delete.busy": "正在删除…",
|
|
1560
|
+
"delete.failed": "删除失败",
|
|
1561
|
+
"delete.running": "会话还在运行,先停止再删。",
|
|
1562
|
+
"delete.open": "这个会话还开着,先关掉它再删。",
|
|
1563
|
+
"delete.notFound": "找不到这个会话的记录,可能已经被删掉了。",
|
|
1564
|
+
"delete.ioError": "文件删除失败,可能被其他程序占用。",
|
|
1565
|
+
"delete.timeout": "等太久了,没收到结果。",
|
|
1566
|
+
"delete.refused": "设置通道拒绝了这次请求。",
|
|
1567
|
+
"remember.action": "记住",
|
|
1568
|
+
"remember.title": "记进长期记忆",
|
|
1569
|
+
"remember.subtitle": "勾选要提炼的消息,模型会把它们压成几条稳定的事实,追加到助理记忆里。",
|
|
1570
|
+
"remember.submit": "提炼并记住",
|
|
1571
|
+
"remember.submitting": "提炼中…",
|
|
1572
|
+
"remember.cancel": "取消",
|
|
1573
|
+
"remember.empty": "这个会话还没有可提炼的消息。",
|
|
1574
|
+
"remember.saved": "已记住",
|
|
1575
|
+
"remember.failed": "没能记住",
|
|
1576
|
+
"remember.side.user": "用户",
|
|
1577
|
+
"remember.side.assistant": "助手",
|
|
1578
|
+
"remember.notOpen": "会话不在线,先回到这个会话再试。",
|
|
1579
|
+
"remember.noModel": "取不到这个会话的模型配置。",
|
|
1580
|
+
"remember.nothing": "这段没有值得记住的内容。",
|
|
1581
|
+
"remember.timeout": "等太久了,没收到结果。",
|
|
1582
|
+
"remember.refused": "设置通道拒绝了这次请求。"
|
|
1013
1583
|
};
|
|
1014
1584
|
/** 英文词典:与中文键一一对应。 */
|
|
1015
1585
|
const en = {
|
|
1016
|
-
"panel": "Assistant
|
|
1017
|
-
"title": "Assistant
|
|
1586
|
+
"panel": "Agent Assistant",
|
|
1587
|
+
"title": "Agent Assistant",
|
|
1018
1588
|
"intro": "This persona opens every new session: it rides the very top of the system prompt and applies to all four modes.",
|
|
1019
1589
|
"name.label": "Name",
|
|
1020
1590
|
"name.placeholder": "e.g. Big Fat Fish",
|
|
@@ -1052,20 +1622,40 @@ window.__ModuleLoader__.load({
|
|
|
1052
1622
|
"memory.editor": "Memory content",
|
|
1053
1623
|
"memory.editor.hint": "One entry per line, in the shape \"- [time] content\"; plain prose works too, the model reads all of it.",
|
|
1054
1624
|
"memory.editor.placeholder": "- [2026-10-24 12:00] Prefers concise Chinese answers",
|
|
1055
|
-
"memory.reload": "Reload",
|
|
1056
1625
|
"memory.save": "Save memory",
|
|
1057
1626
|
"memory.saving": "Saving…",
|
|
1058
1627
|
"memory.saved": "Saved",
|
|
1059
1628
|
"memory.saveFailed": "Save failed",
|
|
1060
|
-
"
|
|
1061
|
-
"
|
|
1062
|
-
"
|
|
1063
|
-
"
|
|
1064
|
-
"
|
|
1065
|
-
"
|
|
1066
|
-
"
|
|
1067
|
-
"
|
|
1068
|
-
"
|
|
1629
|
+
"delete.menu": "Delete",
|
|
1630
|
+
"delete.title": "Delete this session?",
|
|
1631
|
+
"delete.body": "The chat log of “{title}” will be deleted from local storage, along with its index rows and workspace registration.",
|
|
1632
|
+
"delete.hint": "This cannot be undone. To merely hide it from the sidebar, use Archive instead.",
|
|
1633
|
+
"delete.confirm": "Delete",
|
|
1634
|
+
"delete.cancel": "Cancel",
|
|
1635
|
+
"delete.busy": "Deleting…",
|
|
1636
|
+
"delete.failed": "Delete failed",
|
|
1637
|
+
"delete.running": "This session is still running. Stop it first.",
|
|
1638
|
+
"delete.open": "This session is still open. Close it first.",
|
|
1639
|
+
"delete.notFound": "No log found for this session; it may already be gone.",
|
|
1640
|
+
"delete.ioError": "Could not remove the files; another program may hold them.",
|
|
1641
|
+
"delete.timeout": "Timed out waiting for the result.",
|
|
1642
|
+
"delete.refused": "The settings channel refused the request.",
|
|
1643
|
+
"remember.action": "Remember",
|
|
1644
|
+
"remember.title": "Remember into long-term memory",
|
|
1645
|
+
"remember.subtitle": "Tick the messages to distil; the model condenses them into a few stable facts and appends them to the assistant memory.",
|
|
1646
|
+
"remember.submit": "Distil and remember",
|
|
1647
|
+
"remember.submitting": "Distilling…",
|
|
1648
|
+
"remember.cancel": "Cancel",
|
|
1649
|
+
"remember.empty": "This session has no messages to distil yet.",
|
|
1650
|
+
"remember.saved": "Remembered",
|
|
1651
|
+
"remember.failed": "Could not remember",
|
|
1652
|
+
"remember.side.user": "User",
|
|
1653
|
+
"remember.side.assistant": "Assistant",
|
|
1654
|
+
"remember.notOpen": "This session is not open any more.",
|
|
1655
|
+
"remember.noModel": "No model configuration for this session.",
|
|
1656
|
+
"remember.nothing": "Nothing worth remembering here.",
|
|
1657
|
+
"remember.timeout": "Timed out waiting for the result.",
|
|
1658
|
+
"remember.refused": "The settings channel refused the request."
|
|
1069
1659
|
};
|
|
1070
1660
|
//#endregion
|
|
1071
1661
|
//#region ../src/client/index.ts
|
|
@@ -1082,7 +1672,7 @@ window.__ModuleLoader__.load({
|
|
|
1082
1672
|
"configForms"
|
|
1083
1673
|
];
|
|
1084
1674
|
/**
|
|
1085
|
-
*
|
|
1675
|
+
* 挂载Agent助理的界面。
|
|
1086
1676
|
* @param ctx - 浏览器根上下文。
|
|
1087
1677
|
*/
|
|
1088
1678
|
function apply(ctx) {
|
|
@@ -1106,10 +1696,33 @@ window.__ModuleLoader__.load({
|
|
|
1106
1696
|
locale: NS,
|
|
1107
1697
|
inject: () => ({ profile })
|
|
1108
1698
|
}, ProfilePage));
|
|
1109
|
-
const
|
|
1699
|
+
const memoryForm = ctx.configForms.get(PANEL_ID);
|
|
1700
|
+
const signalForm = ctx.configForms.get(PANEL_ID);
|
|
1701
|
+
const controller = new MemorySettingsController(memoryForm);
|
|
1110
1702
|
ctx.effect(() => () => {
|
|
1111
1703
|
controller.dispose();
|
|
1112
1704
|
}, "assistant-profile: memory form");
|
|
1705
|
+
const deleteAction = { deleteSession: (sessionId) => requestDelete(signalForm, sessionId) };
|
|
1706
|
+
ctx.slots.inject("sidebar.workspaces.session.menu.item", () => ctx.slots.register({
|
|
1707
|
+
name: "sidebar.workspaces.session.menu.item",
|
|
1708
|
+
id: "assistant-profile-delete",
|
|
1709
|
+
order: 600,
|
|
1710
|
+
locale: NS
|
|
1711
|
+
}, DeleteSessionMenuItem));
|
|
1712
|
+
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
1713
|
+
name: "shell.overlay",
|
|
1714
|
+
id: "assistant-profile-delete-dialog",
|
|
1715
|
+
order: 50,
|
|
1716
|
+
locale: NS,
|
|
1717
|
+
inject: () => deleteAction
|
|
1718
|
+
}, DeleteSessionDialog));
|
|
1719
|
+
ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register({
|
|
1720
|
+
name: "conversation.chat.assistant-actions",
|
|
1721
|
+
id: "assistant-profile-remember",
|
|
1722
|
+
order: 20,
|
|
1723
|
+
locale: NS,
|
|
1724
|
+
inject: (sessionId) => ({ remember: (text) => requestRemember(signalForm, String(sessionId ?? ""), text) })
|
|
1725
|
+
}, MemoryActions));
|
|
1113
1726
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
1114
1727
|
name: "settings.section",
|
|
1115
1728
|
id: "assistant-memory",
|