dsh-retrace 0.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/HUMANS.txt +18 -0
- package/LICENSE +21 -0
- package/README.md +317 -0
- package/README.zh.md +284 -0
- package/cordis.patch.yml +17 -0
- package/lib/artifact-store.js +239 -0
- package/lib/client.bundle.js +637 -0
- package/lib/client.js +752 -0
- package/lib/dynamic-client.js +647 -0
- package/lib/dynamic-host.js +399 -0
- package/lib/host-core.js +379 -0
- package/lib/http.js +186 -0
- package/lib/index.js +75 -0
- package/lib/projection/versions.js +69 -0
- package/lib/types/client.d.ts +14 -0
- package/lib/types/index.d.ts +65 -0
- package/lib/version-index.js +310 -0
- package/lib/versioning.js +193 -0
- package/package.json +100 -0
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED FILE — do not edit by hand.
|
|
3
|
+
* Source of truth: lib/client.js + the wire wrapper below (scripts/generate-dynamic.mjs).
|
|
4
|
+
*/
|
|
5
|
+
return {
|
|
6
|
+
inject: ['slots', 'locale', 'conversationEvents'],
|
|
7
|
+
apply(ctx) {
|
|
8
|
+
const mod = (() => {
|
|
9
|
+
var module = { exports: {} }
|
|
10
|
+
var exports = module.exports
|
|
11
|
+
const require = (name) =>
|
|
12
|
+
name === 'react' ? React : (() => { throw new Error('dsh-retrace: unknown module "' + name + '" in dynamic client') })()
|
|
13
|
+
var __defProp = Object.defineProperty;
|
|
14
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
15
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
16
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
17
|
+
var __export = (target, all) => {
|
|
18
|
+
for (var name2 in all)
|
|
19
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// lib/client.js
|
|
32
|
+
var client_exports = {};
|
|
33
|
+
__export(client_exports, {
|
|
34
|
+
__setMessageEditorWire: () => __setMessageEditorWire,
|
|
35
|
+
apply: () => apply,
|
|
36
|
+
inject: () => inject,
|
|
37
|
+
name: () => name
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(client_exports);
|
|
40
|
+
var import_react = require("react");
|
|
41
|
+
var name = "dsh-retrace";
|
|
42
|
+
var inject = ["slots", "locale", "conversationEvents"];
|
|
43
|
+
var NS = "retrace";
|
|
44
|
+
var ROUTE_BASE = "/api/plugins/retrace";
|
|
45
|
+
var MARKER_PREFIX = "retrace";
|
|
46
|
+
var CONFIG_KEY = "dsh-retrace:config";
|
|
47
|
+
var zh = {
|
|
48
|
+
"action.edit": "\u7F16\u8F91",
|
|
49
|
+
"action.editAria": "\u7F16\u8F91\u8FD9\u6761\u6D88\u606F",
|
|
50
|
+
"action.recall": "\u64A4\u56DE",
|
|
51
|
+
"action.recallAssistant": "\u64A4\u56DE\u8FD9\u6761\u56DE\u590D",
|
|
52
|
+
"action.recallUser": "\u64A4\u56DE\u8FD9\u6761\u6D88\u606F",
|
|
53
|
+
"action.regenerate": "\u91CD\u65B0\u751F\u6210",
|
|
54
|
+
"action.send": "\u53D1\u9001",
|
|
55
|
+
"action.cancel": "\u53D6\u6D88",
|
|
56
|
+
"marker.recall": "\u5DF2\u64A4\u56DE\u8FD9\u6761\u6D88\u606F\u53CA\u5176\u540E\u7684\u5BF9\u8BDD",
|
|
57
|
+
"marker.recallMany": "\u5DF2\u64A4\u56DE {count} \u6761\u6D88\u606F",
|
|
58
|
+
"marker.recallOne": "\u5DF2\u64A4\u56DE 1 \u6761\u6D88\u606F",
|
|
59
|
+
"marker.edit": "\u5DF2\u7F16\u8F91\u6B64\u6D88\u606F\u5E76\u91CD\u65B0\u53D1\u9001\uFF0C\u5BF9\u8BDD\u4ECE\u65B0\u6D88\u606F\u7EE7\u7EED",
|
|
60
|
+
"marker.regenerate": "\u5DF2\u91CD\u65B0\u751F\u6210\u56DE\u590D",
|
|
61
|
+
"marker.originalLabel": "\u539F\u8F93\u5165",
|
|
62
|
+
"marker.referenceHint": "\u70B9\u51FB\u5C55\u5F00\u67E5\u770B\u539F\u63D0\u95EE\uFF08\u4EC5\u4F5C\u5BF9\u7167\uFF0C\u4E0D\u4F1A\u8FDB\u5165\u6A21\u578B\u4E0A\u4E0B\u6587\uFF09",
|
|
63
|
+
"options.title": "\u6D88\u606F\u7F16\u8F91\u63D2\u4EF6",
|
|
64
|
+
"options.showOriginalInput": "\u7F16\u8F91\u540E\u663E\u793A\u539F\u63D0\u95EE\u5BF9\u7167",
|
|
65
|
+
"options.editFromScratch": "\u7F16\u8F91\u540E\u4ECE\u65B0\u5BF9\u8BDD\u5F00\u59CB\uFF08\u9690\u85CF\u6B64\u524D\u7684\u6D88\u606F\uFF09",
|
|
66
|
+
"options.versioning": "\u7248\u672C\u4E0E\u4EA7\u7269\u5FEB\u7167",
|
|
67
|
+
"options.versioningDesc": "\u5F00\uFF1A\u6BCF\u6B21\u64A4\u56DE/\u7F16\u8F91\u8BB0\u5F55\u4E00\u4E2A\u7248\u672C\uFF08\u6D88\u606F\u4E0E\u89E6\u78B0\u6587\u4EF6\uFF09\uFF0C\u63D0\u4F9B\u65F6\u95F4\u7EBF\u4E0E\u4EA7\u7269\u56DE\u9000\uFF1B\u5173\uFF1A\u4EC5\u56DE\u9000\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u8BB0\u5F55\u7248\u672C\u3001\u4E0D\u8FFD\u8E2A\u4EA7\u7269\uFF08\u6700\u7701\u8D44\u6E90\uFF09\u3002",
|
|
68
|
+
"options.git": "\u542F\u7528 git \u96C6\u6210",
|
|
69
|
+
"options.gitDesc": "\u5F00\uFF1A\u5DE5\u4F5C\u533A\u662F git \u4ED3\u5E93\u65F6\u7528 git \u8BB0\u5F55\u4E0E\u56DE\u9000\uFF08\u4E0D\u81EA\u52A8\u63D0\u4EA4\u3001\u4E0D\u52A8\u4F60\u7684\u5206\u652F\uFF09\uFF0C\u975E\u4ED3\u5E93\u53EF\u5728\u65F6\u95F4\u7EBF\u91CC\u4E00\u952E\u542F\u7528\uFF1B\u5173\uFF1A\u4E00\u5F8B\u7528\u5185\u7F6E\u5FEB\u7167\uFF08\u5B58\u4E8E ~/.dsh\uFF09\uFF0C\u4E0D\u89E6\u78B0\u5DE5\u4F5C\u533A git \u72B6\u6001\uFF0C\u529F\u80FD\u7B49\u4EF7\u3002",
|
|
70
|
+
"options.retention": "\u7248\u672C\u4FDD\u7559\u4E0A\u9650",
|
|
71
|
+
"options.retentionDesc": "\u6587\u4EF6\u5FEB\u7167\u53EA\u4FDD\u7559\u6700\u8FD1 N \u4E2A\u7248\u672C\uFF0C\u8D85\u51FA\u81EA\u52A8\u6E05\u7406\u6700\u65E7\u7684\uFF1B\u65F6\u95F4\u7EBF\u8BB0\u5F55\u4E0E\u5BA1\u8BA1\u75D5\u8FF9\u59CB\u7EC8\u4FDD\u7559\u3002",
|
|
72
|
+
"error.generic": "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
|
|
73
|
+
"error.busy": "\u8BF7\u5148\u505C\u6B62\u5F53\u524D\u56DE\u590D\u518D\u64CD\u4F5C"
|
|
74
|
+
};
|
|
75
|
+
var en = {
|
|
76
|
+
"action.edit": "Edit",
|
|
77
|
+
"action.editAria": "Edit this message",
|
|
78
|
+
"action.recall": "Recall",
|
|
79
|
+
"action.recallAssistant": "Recall this reply",
|
|
80
|
+
"action.recallUser": "Recall this message",
|
|
81
|
+
"action.regenerate": "Regenerate",
|
|
82
|
+
"action.send": "Send",
|
|
83
|
+
"action.cancel": "Cancel",
|
|
84
|
+
"marker.recall": "This message and the following conversation were recalled",
|
|
85
|
+
"marker.recallMany": "{count} messages were recalled",
|
|
86
|
+
"marker.recallOne": "1 message recalled",
|
|
87
|
+
"marker.edit": "Edited and re-sent; the conversation continues from the new message",
|
|
88
|
+
"marker.regenerate": "Reply regenerated",
|
|
89
|
+
"marker.originalLabel": "Original input",
|
|
90
|
+
"marker.referenceHint": "Click to expand the original input (reference only, never sent to the model)",
|
|
91
|
+
"options.title": "Message editor plugin",
|
|
92
|
+
"options.showOriginalInput": "Show the original input after editing",
|
|
93
|
+
"options.editFromScratch": "Start a fresh conversation after editing (hide earlier messages)",
|
|
94
|
+
"options.versioning": "Version & artifact snapshots",
|
|
95
|
+
"options.versioningDesc": "On: every recall/edit records a version (messages and touched files) powering the timeline and artifact rollback. Off: only rewinds context \u2014 no version records, no artifact tracking (lightest).",
|
|
96
|
+
"options.git": "Git integration",
|
|
97
|
+
"options.gitDesc": "On: uses git to record and roll back when the workspace is a repository (never auto-commits, never touches your branches); non-repo workspaces can enable git from the timeline. Off: built-in snapshots under ~/.dsh only \u2014 the plugin never touches the workspace git state; equivalent features.",
|
|
98
|
+
"options.retention": "Version retention limit",
|
|
99
|
+
"options.retentionDesc": "File snapshots are kept for the most recent N versions; older ones are pruned automatically (timeline records and the audit trail are always kept).",
|
|
100
|
+
"error.generic": "Operation failed; please try again",
|
|
101
|
+
"error.busy": "Stop the current reply before recalling or editing"
|
|
102
|
+
};
|
|
103
|
+
var SURFACE_TYPES = /* @__PURE__ */ new Set(["user/message", "assistant/message", "tool/result"]);
|
|
104
|
+
function isReplacementSurfaceEvent(event) {
|
|
105
|
+
return SURFACE_TYPES.has(event.type) && event.surfaceOp !== void 0 && event.surfaceOp !== "append";
|
|
106
|
+
}
|
|
107
|
+
var wire = null;
|
|
108
|
+
function __setMessageEditorWire(fn) {
|
|
109
|
+
wire = fn;
|
|
110
|
+
}
|
|
111
|
+
function callOp(op, payload) {
|
|
112
|
+
if (typeof wire === "function") return wire(op, payload);
|
|
113
|
+
return fetch(`${ROUTE_BASE}/${op}`, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: { "Content-Type": "application/json", ...retraceConfigHeaders() },
|
|
116
|
+
body: JSON.stringify(payload)
|
|
117
|
+
}).then((res) => {
|
|
118
|
+
if (res.status < 200 || res.status >= 300) throw new Error(`HTTP ${res.status}`);
|
|
119
|
+
return res.json();
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function retraceConfigHeaders() {
|
|
123
|
+
const { versioning, git, retentionLimit } = getConfig();
|
|
124
|
+
return { "x-retrace-config": JSON.stringify({ versioning, git, retentionLimit }) };
|
|
125
|
+
}
|
|
126
|
+
var CONFIG_DEFAULTS = { showOriginalInput: true, editFromScratch: true, versioning: true, git: true, retentionLimit: 50 };
|
|
127
|
+
var configListeners = /* @__PURE__ */ new Set();
|
|
128
|
+
var configCache = readConfig();
|
|
129
|
+
var editReferences = /* @__PURE__ */ new Map();
|
|
130
|
+
function readConfig() {
|
|
131
|
+
try {
|
|
132
|
+
const raw = localStorage.getItem(CONFIG_KEY);
|
|
133
|
+
return { ...CONFIG_DEFAULTS, ...raw ? JSON.parse(raw) : {} };
|
|
134
|
+
} catch {
|
|
135
|
+
return { ...CONFIG_DEFAULTS };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function getConfig() {
|
|
139
|
+
return configCache;
|
|
140
|
+
}
|
|
141
|
+
function setConfig(patch) {
|
|
142
|
+
configCache = { ...configCache, ...patch };
|
|
143
|
+
try {
|
|
144
|
+
localStorage.setItem(CONFIG_KEY, JSON.stringify(configCache));
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
for (const listener of configListeners) listener(configCache);
|
|
148
|
+
}
|
|
149
|
+
function subscribeConfig(listener) {
|
|
150
|
+
configListeners.add(listener);
|
|
151
|
+
return () => {
|
|
152
|
+
configListeners.delete(listener);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function useConfig() {
|
|
156
|
+
const [, force] = (0, import_react.useState)(0);
|
|
157
|
+
(0, import_react.useEffect)(() => subscribeConfig(() => force((x) => x + 1)), []);
|
|
158
|
+
return getConfig();
|
|
159
|
+
}
|
|
160
|
+
function chatNodeLike(context, kind, anchorSeq, data) {
|
|
161
|
+
return {
|
|
162
|
+
key: context.key,
|
|
163
|
+
kind,
|
|
164
|
+
id: context.id,
|
|
165
|
+
target: "chat",
|
|
166
|
+
anchorSeq,
|
|
167
|
+
location: context.start?.location ?? context.matches[0]?.location ?? { kind: "unresolved" },
|
|
168
|
+
visibility: "visible",
|
|
169
|
+
data
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
var userActionsDefinition = {
|
|
173
|
+
kind: "retrace-actions",
|
|
174
|
+
target: "chat",
|
|
175
|
+
match: (event) => event.type === "user/message" && event.surfaceOp === "append" && event.data.source?.kind === "user" ? { id: String(event.data.id), role: "start" } : null,
|
|
176
|
+
start: (_context, match) => {
|
|
177
|
+
const event = match.event;
|
|
178
|
+
return {
|
|
179
|
+
seq: event.seq,
|
|
180
|
+
time: event.time,
|
|
181
|
+
messageId: String(event.data.id),
|
|
182
|
+
content: event.data.content
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
update: (context) => context.state,
|
|
186
|
+
buildViewNode: (context) => {
|
|
187
|
+
if (context.state === void 0) return null;
|
|
188
|
+
return chatNodeLike(context, "user-actions", context.state.seq, context.state);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
var userReferenceDefinition = {
|
|
192
|
+
kind: "retrace-reference",
|
|
193
|
+
target: "chat",
|
|
194
|
+
match: (event) => event.type === "user/message" && event.surfaceOp === "append" && event.data.source?.kind === "user" ? { id: `ref:${String(event.data.id)}`, role: "start" } : null,
|
|
195
|
+
start: (_context, match) => {
|
|
196
|
+
const event = match.event;
|
|
197
|
+
return {
|
|
198
|
+
seq: event.seq,
|
|
199
|
+
time: event.time,
|
|
200
|
+
messageId: String(event.data.id),
|
|
201
|
+
content: event.data.content
|
|
202
|
+
};
|
|
203
|
+
},
|
|
204
|
+
update: (context) => context.state,
|
|
205
|
+
buildViewNode: (context) => {
|
|
206
|
+
if (context.state === void 0) return null;
|
|
207
|
+
return chatNodeLike(context, "retrace-reference", context.state.seq - 0.5, context.state);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
function markerOpFromId(id) {
|
|
211
|
+
if (id.startsWith(`${MARKER_PREFIX}-recall-`)) return "recall";
|
|
212
|
+
if (id.startsWith(`${MARKER_PREFIX}-edit-`)) return "edit";
|
|
213
|
+
if (id.startsWith(`${MARKER_PREFIX}-regenerate-`)) return "regenerate";
|
|
214
|
+
return "edit";
|
|
215
|
+
}
|
|
216
|
+
var recallMarkerDefinition = {
|
|
217
|
+
kind: "recall-marker",
|
|
218
|
+
target: "chat",
|
|
219
|
+
match: (event) => {
|
|
220
|
+
if (event.type !== "assistant/message" || !isReplacementSurfaceEvent(event)) return null;
|
|
221
|
+
const id = event.data?.message?.id;
|
|
222
|
+
if (typeof id !== "string" || !id.startsWith(`${MARKER_PREFIX}-`)) return null;
|
|
223
|
+
return { id: `marker:${id}`, role: "start" };
|
|
224
|
+
},
|
|
225
|
+
start: (_context, match) => {
|
|
226
|
+
const event = match.event;
|
|
227
|
+
return {
|
|
228
|
+
seq: event.seq,
|
|
229
|
+
time: event.time,
|
|
230
|
+
op: markerOpFromId(String(event.data.message.id)),
|
|
231
|
+
shadowedSeqs: Array.isArray(event.sourceEventSeqs) ? event.sourceEventSeqs.slice() : [],
|
|
232
|
+
targetSeq: event.data?.editor?.targetSeq,
|
|
233
|
+
text: event.data?.editor?.text
|
|
234
|
+
};
|
|
235
|
+
},
|
|
236
|
+
update: (context) => context.state,
|
|
237
|
+
buildViewNode: (context) => {
|
|
238
|
+
if (context.state === void 0) return null;
|
|
239
|
+
return chatNodeLike(context, "recall-marker", context.state.seq, context.state);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
function textOf(content) {
|
|
243
|
+
if (!Array.isArray(content)) return "";
|
|
244
|
+
return content.filter((block) => block && block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
|
|
245
|
+
}
|
|
246
|
+
function useMessageSeq(useSession, messageId) {
|
|
247
|
+
return useSession((snapshot) => {
|
|
248
|
+
for (const node of snapshot.chat.nodes.values()) {
|
|
249
|
+
if (node.kind === "assistant-step" && node.data?.finalNode?.messageId === messageId) {
|
|
250
|
+
return node.data.finalNode.seq;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return void 0;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function useShadowed(useSession, seq) {
|
|
257
|
+
return useSession((snapshot) => {
|
|
258
|
+
if (seq === void 0 || seq === null) return false;
|
|
259
|
+
for (const node of snapshot.chat.nodes.values()) {
|
|
260
|
+
if (node.kind === "recall-marker" && Array.isArray(node.data?.shadowedSeqs) && node.data.shadowedSeqs.includes(seq)) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
function useHiddenKeys(useSession, shadowedSeqs) {
|
|
268
|
+
return useSession((snapshot) => {
|
|
269
|
+
if (!Array.isArray(shadowedSeqs) || shadowedSeqs.length === 0) return null;
|
|
270
|
+
const hidden = new Set(shadowedSeqs);
|
|
271
|
+
const keys = [];
|
|
272
|
+
for (const node of snapshot.chat.nodes.values()) {
|
|
273
|
+
if (node.kind === "recall-marker") continue;
|
|
274
|
+
if (node.kind === "turn-tail") {
|
|
275
|
+
const closingSeq = node.data?.closing?.finalNode?.seq;
|
|
276
|
+
if (typeof closingSeq === "number" && hidden.has(closingSeq)) keys.push(node.key);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (node.kind === "tool-call") {
|
|
280
|
+
const resultSeq = node.data?.root?.seq;
|
|
281
|
+
if (typeof resultSeq === "number" && hidden.has(resultSeq)) keys.push(node.key);
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (typeof node.anchorSeq === "number" && hidden.has(node.anchorSeq)) keys.push(node.key);
|
|
285
|
+
}
|
|
286
|
+
return keys.length === 0 ? null : keys;
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function useMarkerDismissed(useSession, markerSeq, op) {
|
|
290
|
+
return useSession((snapshot) => {
|
|
291
|
+
if (typeof markerSeq !== "number") return false;
|
|
292
|
+
let after = 0;
|
|
293
|
+
for (const node of snapshot.chat.nodes.values()) {
|
|
294
|
+
if (node.kind === "user-actions" && typeof node.data?.seq === "number" && node.data.seq > markerSeq) {
|
|
295
|
+
after += 1;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return op === "edit" ? after >= 2 : after >= 1;
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function useEditReference(useSession, mySeq) {
|
|
302
|
+
return useSession((snapshot) => {
|
|
303
|
+
if (typeof mySeq !== "number") return null;
|
|
304
|
+
let latestMarkerSeq = -1;
|
|
305
|
+
let referenceText = null;
|
|
306
|
+
let prevUserSeq = -1;
|
|
307
|
+
for (const node of snapshot.chat.nodes.values()) {
|
|
308
|
+
if (node.kind === "recall-marker" && node.data?.op === "edit" && typeof node.data.seq === "number" && node.data.seq < mySeq && node.data.seq > latestMarkerSeq) {
|
|
309
|
+
latestMarkerSeq = node.data.seq;
|
|
310
|
+
referenceText = typeof node.data.text === "string" && node.data.text.length > 0 ? node.data.text : null;
|
|
311
|
+
}
|
|
312
|
+
if (node.kind === "user-actions" && typeof node.data?.seq === "number" && node.data.seq < mySeq && node.data.seq > prevUserSeq) {
|
|
313
|
+
prevUserSeq = node.data.seq;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (latestMarkerSeq === -1 || referenceText === null) return null;
|
|
317
|
+
if (prevUserSeq > latestMarkerSeq) return null;
|
|
318
|
+
return referenceText;
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
function AssistantActions({ messageId, sessionId, useSession, t }) {
|
|
322
|
+
const seq = useMessageSeq(useSession, messageId);
|
|
323
|
+
const shadowed = useShadowed(useSession, seq);
|
|
324
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
325
|
+
const [failure, setFailure] = (0, import_react.useState)(null);
|
|
326
|
+
if (shadowed || seq === void 0) return null;
|
|
327
|
+
const run = (op) => {
|
|
328
|
+
setBusy(true);
|
|
329
|
+
setFailure(null);
|
|
330
|
+
callOp(op, { sessionId, messageId }).then(
|
|
331
|
+
(result) => {
|
|
332
|
+
setBusy(false);
|
|
333
|
+
if (!result || result.ok !== true) {
|
|
334
|
+
const message = result?.error?.message || "Operation failed; please try again";
|
|
335
|
+
const code = result?.error?.code;
|
|
336
|
+
setFailure(code === "agent-busy" ? t("error.busy") : message);
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
(error) => {
|
|
340
|
+
setBusy(false);
|
|
341
|
+
setFailure(error?.message ?? t("error.generic"));
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
};
|
|
345
|
+
return (0, import_react.createElement)("span", { className: "dsh-rt-strip" }, [
|
|
346
|
+
(0, import_react.createElement)("button", {
|
|
347
|
+
key: "recall",
|
|
348
|
+
type: "button",
|
|
349
|
+
className: "dsh-rt-icon",
|
|
350
|
+
title: t("action.recallAssistant"),
|
|
351
|
+
"aria-label": t("action.recallAssistant"),
|
|
352
|
+
disabled: busy,
|
|
353
|
+
onClick: () => run("recall")
|
|
354
|
+
}, "\u21A9"),
|
|
355
|
+
(0, import_react.createElement)("button", {
|
|
356
|
+
key: "regenerate",
|
|
357
|
+
type: "button",
|
|
358
|
+
className: "dsh-rt-icon",
|
|
359
|
+
title: t("action.regenerate"),
|
|
360
|
+
"aria-label": t("action.regenerate"),
|
|
361
|
+
disabled: busy,
|
|
362
|
+
onClick: () => run("regenerate")
|
|
363
|
+
}, "\u21BB"),
|
|
364
|
+
failure !== null && (0, import_react.createElement)("span", { key: "error", className: "dsh-rt-error", role: "status" }, failure)
|
|
365
|
+
]);
|
|
366
|
+
}
|
|
367
|
+
function ReferenceRow({ node, useSession, t }) {
|
|
368
|
+
const { seq, messageId } = node.data;
|
|
369
|
+
const shadowed = useShadowed(useSession, seq);
|
|
370
|
+
const markerRef = useEditReference(useSession, seq);
|
|
371
|
+
const referenceText = editReferences.get(messageId) ?? markerRef;
|
|
372
|
+
const config = useConfig();
|
|
373
|
+
if (shadowed) return null;
|
|
374
|
+
if (referenceText === null || !config.showOriginalInput) return null;
|
|
375
|
+
return (0, import_react.createElement)("div", { className: "dsh-rt-user-row" }, [
|
|
376
|
+
(0, import_react.createElement)("details", { className: "dsh-rt-reference" }, [
|
|
377
|
+
(0, import_react.createElement)(
|
|
378
|
+
"summary",
|
|
379
|
+
{ title: t("marker.referenceHint") },
|
|
380
|
+
`${t("marker.originalLabel")}\uFF1A${referenceText.length > 60 ? `${referenceText.slice(0, 60)}\u2026` : referenceText}`
|
|
381
|
+
),
|
|
382
|
+
(0, import_react.createElement)("div", { className: "dsh-rt-reference-text" }, referenceText)
|
|
383
|
+
])
|
|
384
|
+
]);
|
|
385
|
+
}
|
|
386
|
+
function UserActionsRow({ node, sessionId, useSession, inputActions, t }) {
|
|
387
|
+
const { seq, messageId, content } = node.data;
|
|
388
|
+
const shadowed = useShadowed(useSession, seq);
|
|
389
|
+
const [editing, setEditing] = (0, import_react.useState)(false);
|
|
390
|
+
const [draft, setDraft] = (0, import_react.useState)("");
|
|
391
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
392
|
+
const [failure, setFailure] = (0, import_react.useState)(null);
|
|
393
|
+
if (shadowed) return null;
|
|
394
|
+
const openEditor = () => {
|
|
395
|
+
setDraft(textOf(content));
|
|
396
|
+
setFailure(null);
|
|
397
|
+
setEditing(true);
|
|
398
|
+
};
|
|
399
|
+
const closeEditor = () => {
|
|
400
|
+
setEditing(false);
|
|
401
|
+
setFailure(null);
|
|
402
|
+
};
|
|
403
|
+
const settle = (result, op) => {
|
|
404
|
+
setBusy(false);
|
|
405
|
+
if (!result || result.ok !== true) {
|
|
406
|
+
const code = result?.error?.code;
|
|
407
|
+
setFailure(code === "agent-busy" ? t("error.busy") : result?.error?.message ?? t("error.generic"));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (op === "recall") {
|
|
411
|
+
const echoed = typeof result.value?.text === "string" && result.value.text.length > 0 ? result.value.text : textOf(content);
|
|
412
|
+
if (echoed && inputActions && typeof inputActions.setDraft === "function") {
|
|
413
|
+
inputActions.setDraft(echoed);
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (op === "editAndResend") {
|
|
418
|
+
if (result.value?.resendMessageId && typeof result.value?.originalText === "string") {
|
|
419
|
+
editReferences.set(result.value.resendMessageId, result.value.originalText);
|
|
420
|
+
}
|
|
421
|
+
setEditing(false);
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
const run = (op, extra = {}) => {
|
|
425
|
+
setBusy(true);
|
|
426
|
+
setFailure(null);
|
|
427
|
+
callOp(op, { sessionId, messageId, ...extra }).then(
|
|
428
|
+
(result) => settle(result, op),
|
|
429
|
+
(error) => {
|
|
430
|
+
setBusy(false);
|
|
431
|
+
setFailure(error?.message ?? t("error.generic"));
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
};
|
|
435
|
+
return (0, import_react.createElement)("div", { className: "dsh-rt-user-row" }, [
|
|
436
|
+
editing ? (0, import_react.createElement)("div", { key: "editor", className: "dsh-rt-editor" }, [
|
|
437
|
+
(0, import_react.createElement)("textarea", {
|
|
438
|
+
key: "input",
|
|
439
|
+
className: "dsh-rt-textarea",
|
|
440
|
+
"aria-label": t("action.editAria"),
|
|
441
|
+
value: draft,
|
|
442
|
+
rows: 3,
|
|
443
|
+
onChange: (event) => setDraft(event.target.value)
|
|
444
|
+
}),
|
|
445
|
+
(0, import_react.createElement)("div", { key: "buttons", className: "dsh-rt-editor-buttons" }, [
|
|
446
|
+
(0, import_react.createElement)("button", {
|
|
447
|
+
key: "send",
|
|
448
|
+
type: "button",
|
|
449
|
+
className: "dsh-rt-editor-send",
|
|
450
|
+
disabled: busy || draft.trim().length === 0,
|
|
451
|
+
onClick: () => run("editAndResend", {
|
|
452
|
+
text: draft.trim(),
|
|
453
|
+
fromScratch: getConfig().editFromScratch
|
|
454
|
+
})
|
|
455
|
+
}, t("action.send")),
|
|
456
|
+
(0, import_react.createElement)("button", {
|
|
457
|
+
key: "cancel",
|
|
458
|
+
type: "button",
|
|
459
|
+
className: "dsh-rt-editor-cancel",
|
|
460
|
+
disabled: busy,
|
|
461
|
+
onClick: closeEditor
|
|
462
|
+
}, t("action.cancel"))
|
|
463
|
+
])
|
|
464
|
+
]) : (0, import_react.createElement)("span", { key: "row", className: "dsh-rt-user-actions" }, [
|
|
465
|
+
(0, import_react.createElement)("button", {
|
|
466
|
+
key: "edit",
|
|
467
|
+
type: "button",
|
|
468
|
+
className: "dsh-rt-chip",
|
|
469
|
+
title: t("action.edit"),
|
|
470
|
+
disabled: busy,
|
|
471
|
+
onClick: openEditor
|
|
472
|
+
}, t("action.edit")),
|
|
473
|
+
(0, import_react.createElement)("button", {
|
|
474
|
+
key: "recall",
|
|
475
|
+
type: "button",
|
|
476
|
+
className: "dsh-rt-chip",
|
|
477
|
+
title: t("action.recallUser"),
|
|
478
|
+
disabled: busy,
|
|
479
|
+
onClick: () => run("recall")
|
|
480
|
+
}, t("action.recall"))
|
|
481
|
+
]),
|
|
482
|
+
failure !== null && (0, import_react.createElement)("div", { key: "error", className: "dsh-rt-error", role: "status" }, failure)
|
|
483
|
+
]);
|
|
484
|
+
}
|
|
485
|
+
function RecallMarkerRow({ node, useSession, t }) {
|
|
486
|
+
const { seq, op, shadowedSeqs } = node.data;
|
|
487
|
+
const dismissed = useMarkerDismissed(useSession, seq, op);
|
|
488
|
+
const hiddenKeys = useHiddenKeys(useSession, shadowedSeqs);
|
|
489
|
+
const css = hiddenKeys === null ? null : hiddenKeys.map((key) => `[data-chat-anchor-key=${JSON.stringify(key)}]{display:none!important}`).join("");
|
|
490
|
+
const count = Array.isArray(shadowedSeqs) ? shadowedSeqs.length : 0;
|
|
491
|
+
const label = op === "recall" ? count > 1 ? t("marker.recallMany", { count }) : t("marker.recallOne") : op === "regenerate" ? t("marker.regenerate") : t("marker.edit");
|
|
492
|
+
return (0, import_react.createElement)("div", { className: "dsh-rt-marker-block", "data-dismissed": dismissed || void 0 }, [
|
|
493
|
+
css !== null && (0, import_react.createElement)("style", { key: "hide", dangerouslySetInnerHTML: { __html: css } }),
|
|
494
|
+
!dismissed && (0, import_react.createElement)("div", { key: "label", className: "dsh-rt-marker", role: "status" }, label)
|
|
495
|
+
]);
|
|
496
|
+
}
|
|
497
|
+
function OptionsRow({ t }) {
|
|
498
|
+
const config = useConfig();
|
|
499
|
+
const toggle = (key) => (event) => setConfig({ [key]: event.target.checked });
|
|
500
|
+
const optionRow = (key, labelKey, descKey) => (0, import_react.createElement)("label", { key, className: "dsh-rt-option" }, [
|
|
501
|
+
(0, import_react.createElement)("input", { type: "checkbox", checked: config[key], onChange: toggle(key) }),
|
|
502
|
+
(0, import_react.createElement)("span", { className: "dsh-rt-option-text" }, [
|
|
503
|
+
(0, import_react.createElement)("span", { className: "dsh-rt-option-label" }, t(labelKey)),
|
|
504
|
+
(0, import_react.createElement)("span", { className: "dsh-rt-option-desc" }, t(descKey))
|
|
505
|
+
])
|
|
506
|
+
]);
|
|
507
|
+
return (0, import_react.createElement)("div", { className: "dsh-rt-options" }, [
|
|
508
|
+
(0, import_react.createElement)("div", { key: "title", className: "dsh-rt-options-title" }, t("options.title")),
|
|
509
|
+
(0, import_react.createElement)("label", { key: "original", className: "dsh-rt-option" }, [
|
|
510
|
+
(0, import_react.createElement)("input", {
|
|
511
|
+
type: "checkbox",
|
|
512
|
+
checked: config.showOriginalInput,
|
|
513
|
+
onChange: toggle("showOriginalInput")
|
|
514
|
+
}),
|
|
515
|
+
(0, import_react.createElement)("span", null, t("options.showOriginalInput"))
|
|
516
|
+
]),
|
|
517
|
+
(0, import_react.createElement)("label", { key: "fresh", className: "dsh-rt-option" }, [
|
|
518
|
+
(0, import_react.createElement)("input", {
|
|
519
|
+
type: "checkbox",
|
|
520
|
+
checked: config.editFromScratch,
|
|
521
|
+
onChange: toggle("editFromScratch")
|
|
522
|
+
}),
|
|
523
|
+
(0, import_react.createElement)("span", null, t("options.editFromScratch"))
|
|
524
|
+
]),
|
|
525
|
+
optionRow("versioning", "options.versioning", "options.versioningDesc"),
|
|
526
|
+
optionRow("git", "options.git", "options.gitDesc"),
|
|
527
|
+
(0, import_react.createElement)("div", { key: "retention", className: "dsh-rt-option dsh-rt-option-number" }, [
|
|
528
|
+
(0, import_react.createElement)("span", { className: "dsh-rt-option-text" }, [
|
|
529
|
+
(0, import_react.createElement)("span", { className: "dsh-rt-option-label" }, t("options.retention")),
|
|
530
|
+
(0, import_react.createElement)("span", { className: "dsh-rt-option-desc" }, t("options.retentionDesc"))
|
|
531
|
+
]),
|
|
532
|
+
(0, import_react.createElement)("input", {
|
|
533
|
+
type: "number",
|
|
534
|
+
min: 5,
|
|
535
|
+
max: 500,
|
|
536
|
+
step: 5,
|
|
537
|
+
className: "dsh-rt-retention-input",
|
|
538
|
+
value: config.retentionLimit,
|
|
539
|
+
onChange: (event) => {
|
|
540
|
+
const value = Math.max(1, Math.min(1e3, Number(event.target.value) || 50));
|
|
541
|
+
setConfig({ retentionLimit: value });
|
|
542
|
+
}
|
|
543
|
+
})
|
|
544
|
+
])
|
|
545
|
+
]);
|
|
546
|
+
}
|
|
547
|
+
var STYLE_ID = "dsh-retrace-css";
|
|
548
|
+
var CSS = `
|
|
549
|
+
.dsh-rt-strip{display:inline-flex;align-items:center;gap:2px}
|
|
550
|
+
.dsh-rt-icon{width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:transparent;border:none;border-radius:28px;display:inline-flex;justify-content:center;align-items:center;padding:0;font-size:14px;line-height:1}
|
|
551
|
+
.dsh-rt-icon:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
|
|
552
|
+
.dsh-rt-icon:disabled{opacity:.4;cursor:default}
|
|
553
|
+
.dsh-rt-user-row{display:flex;flex-direction:column;align-items:flex-end;gap:4px;margin-top:2px}
|
|
554
|
+
.dsh-rt-user-actions{display:inline-flex;gap:6px}
|
|
555
|
+
.dsh-rt-chip{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:var(--dsw-alias-interactive-bg-hover);border:none;border-radius:12px;padding:2px 10px;font-size:12px;line-height:20px}
|
|
556
|
+
.dsh-rt-chip:hover:not(:disabled){color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover-solid)}
|
|
557
|
+
.dsh-rt-chip:disabled{opacity:.5;cursor:default}
|
|
558
|
+
.dsh-rt-editor{display:flex;flex-direction:column;gap:6px;width:min(525px,82%);border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);border-radius:12px;padding:8px}
|
|
559
|
+
.dsh-rt-textarea{resize:vertical;width:100%;box-sizing:border-box;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-elevated);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;padding:8px 10px;font:inherit;font-size:14px;line-height:20px}
|
|
560
|
+
.dsh-rt-textarea:focus{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary)}
|
|
561
|
+
.dsh-rt-editor-buttons{display:flex;justify-content:flex-end;gap:8px}
|
|
562
|
+
.dsh-rt-editor-send{color:#fff;cursor:pointer;background:var(--dsw-alias-button-info-fill);border:none;border-radius:999px;padding:4px 16px;font-size:13px;line-height:20px}
|
|
563
|
+
.dsh-rt-editor-send:hover:not(:disabled){background:var(--dsw-alias-button-info-hover)}
|
|
564
|
+
.dsh-rt-editor-send:disabled{opacity:.4;cursor:default}
|
|
565
|
+
.dsh-rt-editor-cancel{color:var(--dsw-alias-label-secondary);cursor:pointer;background:transparent;border:none;border-radius:999px;padding:4px 12px;font-size:13px;line-height:20px}
|
|
566
|
+
.dsh-rt-editor-cancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
567
|
+
.dsh-rt-error{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;max-width:min(525px,82%)}
|
|
568
|
+
.dsh-rt-marker-block{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;max-width:var(--dsh-chat-content-width);box-sizing:border-box;margin:0 auto;padding:2px 0}
|
|
569
|
+
.dsh-rt-marker{text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px}
|
|
570
|
+
.dsh-rt-reference{width:min(525px,82%);box-sizing:border-box;border:1px dashed var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-elevated);border-radius:10px;padding:2px 12px}
|
|
571
|
+
.dsh-rt-reference summary{color:var(--dsw-alias-label-caption);cursor:pointer;user-select:none;font-size:12px;line-height:22px;list-style:none;display:inline-flex;align-items:center;gap:6px;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
572
|
+
.dsh-rt-reference summary::-webkit-details-marker{display:none}
|
|
573
|
+
.dsh-rt-reference summary:before{content:"\u25B8";transition:transform .12s;font-size:10px}
|
|
574
|
+
.dsh-rt-reference[open] summary:before{transform:rotate(90deg)}
|
|
575
|
+
.dsh-rt-reference summary:hover{color:var(--dsw-alias-label-secondary)}
|
|
576
|
+
.dsh-rt-reference-text{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;white-space:pre-wrap;overflow-wrap:anywhere;padding:2px 0 6px}
|
|
577
|
+
.dsh-rt-options{display:flex;flex-direction:column;gap:8px;padding:2px 0}
|
|
578
|
+
.dsh-rt-options-title{color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:600;line-height:20px}
|
|
579
|
+
.dsh-rt-option{display:flex;align-items:center;gap:8px;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;cursor:pointer}
|
|
580
|
+
.dsh-rt-option-text{display:flex;flex-direction:column;gap:1px;min-width:0}
|
|
581
|
+
.dsh-rt-option-label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}
|
|
582
|
+
.dsh-rt-option-desc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:17px}
|
|
583
|
+
.dsh-rt-option-number{align-items:flex-start}
|
|
584
|
+
.dsh-rt-retention-input{width:64px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-elevated);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:2px 6px;font:inherit;font-size:13px;outline:none;margin-top:1px}
|
|
585
|
+
.dsh-rt-retention-input:focus{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary)}
|
|
586
|
+
.dsh-rt-option input{accent-color:var(--dsw-alias-state-business-primary)}
|
|
587
|
+
`;
|
|
588
|
+
function ensureStyle() {
|
|
589
|
+
if (typeof document === "undefined") return () => {
|
|
590
|
+
};
|
|
591
|
+
if (document.querySelector(`style[data-plugin-css="${STYLE_ID}"]`)) return () => {
|
|
592
|
+
};
|
|
593
|
+
const tag = document.createElement("style");
|
|
594
|
+
tag.dataset.plugin = "dsh-retrace";
|
|
595
|
+
tag.dataset.pluginCss = STYLE_ID;
|
|
596
|
+
tag.textContent = CSS;
|
|
597
|
+
document.head.appendChild(tag);
|
|
598
|
+
return () => {
|
|
599
|
+
tag.remove();
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function apply(ctx) {
|
|
603
|
+
const disposeStyle = ensureStyle();
|
|
604
|
+
ctx.effect(() => () => disposeStyle(), "dsh-retrace: styles");
|
|
605
|
+
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-retrace: dictionaries");
|
|
606
|
+
const conversationEvents = ctx.get("conversationEvents");
|
|
607
|
+
if (conversationEvents) {
|
|
608
|
+
conversationEvents.register(userActionsDefinition);
|
|
609
|
+
conversationEvents.register(userReferenceDefinition);
|
|
610
|
+
conversationEvents.register(recallMarkerDefinition);
|
|
611
|
+
}
|
|
612
|
+
ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register({
|
|
613
|
+
name: "conversation.chat.assistant-actions",
|
|
614
|
+
id: "retrace",
|
|
615
|
+
order: 20,
|
|
616
|
+
locale: NS
|
|
617
|
+
}, AssistantActions));
|
|
618
|
+
ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
|
|
619
|
+
name: "conversation.chat.node",
|
|
620
|
+
key: "user-actions",
|
|
621
|
+
locale: NS
|
|
622
|
+
}, UserActionsRow));
|
|
623
|
+
ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
|
|
624
|
+
name: "conversation.chat.node",
|
|
625
|
+
key: "retrace-reference",
|
|
626
|
+
locale: NS
|
|
627
|
+
}, ReferenceRow));
|
|
628
|
+
ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
|
|
629
|
+
name: "conversation.chat.node",
|
|
630
|
+
key: "recall-marker",
|
|
631
|
+
locale: NS
|
|
632
|
+
}, RecallMarkerRow));
|
|
633
|
+
ctx.slots.inject("settings.general.item", () => ctx.slots.register({
|
|
634
|
+
name: "settings.general.item",
|
|
635
|
+
id: "retrace",
|
|
636
|
+
order: 30,
|
|
637
|
+
locale: NS
|
|
638
|
+
}, OptionsRow));
|
|
639
|
+
}
|
|
640
|
+
return module.exports
|
|
641
|
+
})()
|
|
642
|
+
if (typeof mod.__setMessageEditorWire === 'function') {
|
|
643
|
+
mod.__setMessageEditorWire((op, payload) => host.call(`retrace.${op}`, payload))
|
|
644
|
+
}
|
|
645
|
+
return mod.apply(ctx)
|
|
646
|
+
},
|
|
647
|
+
}
|