dsh-context-fold 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +39 -13
- package/lib/index.js +9 -11
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -12,16 +12,31 @@ const zh = { "tab.contextFold": "上下文折叠" };
|
|
|
12
12
|
/** Dictionary: keep keys aligned with `locale/*.json`. */
|
|
13
13
|
const en = { "tab.contextFold": "Context fold" };
|
|
14
14
|
const react = require("react");
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Fetch the report for one session.
|
|
17
|
+
*
|
|
18
|
+
* Failures are reported rather than swallowed: before the routes existed the
|
|
19
|
+
* tab rendered "no report available", which reads like "this session folded
|
|
20
|
+
* nothing" instead of "the request never landed".
|
|
21
|
+
*
|
|
22
|
+
* @param sessionId - session to report on.
|
|
23
|
+
* @returns the payload, or a message describing why there is none.
|
|
24
|
+
*/
|
|
16
25
|
async function fetchReport(sessionId) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
let response;
|
|
27
|
+
try {
|
|
28
|
+
response = await fetch("/api/context-fold/report", {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: { "content-type": "application/json" },
|
|
31
|
+
body: JSON.stringify({ sessionId })
|
|
32
|
+
});
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return { error: `request failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
35
|
+
}
|
|
36
|
+
const body = await response.json().catch(() => void 0);
|
|
37
|
+
if (!response.ok) return { error: body?.error ?? `HTTP ${String(response.status)}` };
|
|
38
|
+
if (body?.ok !== true || body.value === void 0) return { error: body?.error ?? "the server returned no value" };
|
|
39
|
+
return { report: body.value };
|
|
25
40
|
}
|
|
26
41
|
/** Fetch the full text behind one fold. */
|
|
27
42
|
async function fetchOriginal(sessionId, locator) {
|
|
@@ -83,6 +98,10 @@ const styles = {
|
|
|
83
98
|
marginTop: "6px",
|
|
84
99
|
color: "#facc15"
|
|
85
100
|
},
|
|
101
|
+
error: {
|
|
102
|
+
marginTop: "6px",
|
|
103
|
+
color: "#f87171"
|
|
104
|
+
},
|
|
86
105
|
reread: {
|
|
87
106
|
marginTop: "6px",
|
|
88
107
|
color: "#f87171"
|
|
@@ -231,11 +250,14 @@ function ContextFoldTab(props) {
|
|
|
231
250
|
}
|
|
232
251
|
let cancelled = false;
|
|
233
252
|
setState({ status: "loading" });
|
|
234
|
-
fetchReport(sessionId).then((
|
|
253
|
+
fetchReport(sessionId).then((result) => {
|
|
235
254
|
if (cancelled) return;
|
|
236
|
-
setState(report
|
|
255
|
+
setState("report" in result ? {
|
|
237
256
|
status: "ready",
|
|
238
|
-
report
|
|
257
|
+
report: result.report
|
|
258
|
+
} : {
|
|
259
|
+
status: "error",
|
|
260
|
+
error: result.error
|
|
239
261
|
});
|
|
240
262
|
});
|
|
241
263
|
return () => {
|
|
@@ -254,10 +276,14 @@ function ContextFoldTab(props) {
|
|
|
254
276
|
style: styles.muted,
|
|
255
277
|
key: "m"
|
|
256
278
|
}, "Loading…"));
|
|
279
|
+
else if (state.status === "error") children.push(react.createElement("div", {
|
|
280
|
+
style: styles.error,
|
|
281
|
+
key: "e"
|
|
282
|
+
}, `Could not load the report: ${state.error ?? "unknown"}`));
|
|
257
283
|
else if (state.status !== "ready" || state.report === void 0) children.push(react.createElement("div", {
|
|
258
284
|
style: styles.muted,
|
|
259
285
|
key: "m"
|
|
260
|
-
}, "
|
|
286
|
+
}, "This session has no folds to report."));
|
|
261
287
|
else {
|
|
262
288
|
children.push(react.createElement(Summary, {
|
|
263
289
|
key: "summary",
|
package/lib/index.js
CHANGED
|
@@ -1407,7 +1407,7 @@ function buildReport(session, deps) {
|
|
|
1407
1407
|
const event = session.eventAt(fold.seq);
|
|
1408
1408
|
const message = event === void 0 ? null : session.deriveEventMessage(event);
|
|
1409
1409
|
const content = message !== null && message.role === "tool" ? message.content : void 0;
|
|
1410
|
-
const tokensAfter =
|
|
1410
|
+
const tokensAfter = message === null ? 0 : deps.estimateMessage(message);
|
|
1411
1411
|
const inline = inlineTextOf(content) ?? "";
|
|
1412
1412
|
const locator = locatorOf(inline);
|
|
1413
1413
|
folds.push({
|
|
@@ -1444,10 +1444,7 @@ function buildReport(session, deps) {
|
|
|
1444
1444
|
charsBefore: target.chars?.[position] ?? 0,
|
|
1445
1445
|
charsAfter: Array.from(block.text).length,
|
|
1446
1446
|
tokensBefore: estimateFromChars(target.chars?.[position] ?? 0),
|
|
1447
|
-
tokensAfter:
|
|
1448
|
-
type: "text",
|
|
1449
|
-
text: block.text
|
|
1450
|
-
}]),
|
|
1447
|
+
tokensAfter: estimateFromChars(Array.from(block.text).length),
|
|
1451
1448
|
inline: block.text,
|
|
1452
1449
|
recoverable: locator !== void 0,
|
|
1453
1450
|
...locator === void 0 ? {} : { locator }
|
|
@@ -1525,11 +1522,12 @@ function json(value, status = 200) {
|
|
|
1525
1522
|
/**
|
|
1526
1523
|
* Register the two read-only routes the tab uses.
|
|
1527
1524
|
*
|
|
1528
|
-
* @param
|
|
1525
|
+
* @param connection - the host `connection` service (its `fetch.register` is the route registrar).
|
|
1529
1526
|
* @param deps - session and spill access.
|
|
1530
1527
|
* @returns a disposer for both routes.
|
|
1531
1528
|
*/
|
|
1532
|
-
function registerRoutes(
|
|
1529
|
+
function registerRoutes(connection, deps) {
|
|
1530
|
+
const register = connection.fetch.register.bind(connection.fetch);
|
|
1533
1531
|
const report = register({
|
|
1534
1532
|
path: "/api/context-fold/report",
|
|
1535
1533
|
methods: ["POST"],
|
|
@@ -1720,18 +1718,18 @@ function apply(ctx, rawConfig = {}) {
|
|
|
1720
1718
|
});
|
|
1721
1719
|
ctx.inject(["connection"], (scope) => {
|
|
1722
1720
|
const connection = scope.connection;
|
|
1723
|
-
if (connection === void 0) {
|
|
1724
|
-
ctx.logger.warn("context-fold: no ctx.connection
|
|
1721
|
+
if (connection === void 0 || typeof connection.fetch?.register !== "function") {
|
|
1722
|
+
ctx.logger.warn("context-fold: no ctx.connection.fetch.register; the conversation tab has no data source");
|
|
1725
1723
|
return;
|
|
1726
1724
|
}
|
|
1727
1725
|
const tokenMeter = ctx.get("tokenMeter");
|
|
1728
|
-
registerRoutes(connection, {
|
|
1726
|
+
ctx.effect(() => registerRoutes(connection, {
|
|
1729
1727
|
sessionOf: (id) => {
|
|
1730
1728
|
return ctx.get("sessions")?.get(id);
|
|
1731
1729
|
},
|
|
1732
1730
|
estimateMessage: (message) => tokenMeter === void 0 ? 0 : tokenMeter.estimateMessage(message),
|
|
1733
1731
|
...ctx.get("spillStore") === void 0 ? {} : { spillStore: ctx.get("spillStore") }
|
|
1734
|
-
});
|
|
1732
|
+
}), "context-fold: conversation tab routes");
|
|
1735
1733
|
});
|
|
1736
1734
|
ctx.on("session/disposed", (session) => {
|
|
1737
1735
|
states.delete(session.header.id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context-fold",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "dsh-context-fold: stage-boundary folding of completed tool results into a conclusion plus a spill path, deterministic and replay-safe.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|