@klarkxy/dsh-session-manager 0.1.2 → 0.2.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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/lib/client.inner.cjs.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +85 -11
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
package/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import Schema from "@deepseek-ai/schemastery";
|
|
|
2
2
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
3
|
//#region src/contracts.d.ts
|
|
4
4
|
interface Config$1 {
|
|
5
|
-
/** Maximum serialized UTF-8 bytes of one read_session
|
|
5
|
+
/** Maximum serialized UTF-8 bytes of one read_session details/handoff page, including item metadata. Conversation turn previews do not use this budget. */
|
|
6
6
|
pageChars?: number;
|
|
7
7
|
/** Maximum sessions returned by one list_sessions page. */
|
|
8
8
|
listLimit?: number;
|
package/lib/index.js
CHANGED
|
@@ -770,6 +770,71 @@ function pageTurns(turns, index, offset, budget, limit) {
|
|
|
770
770
|
} : {}
|
|
771
771
|
};
|
|
772
772
|
}
|
|
773
|
+
/**
|
|
774
|
+
* Conversation-mode pager: one summary record for every selected turn,
|
|
775
|
+
* advancing by turn index only. Preview caps bound the payload; the selected
|
|
776
|
+
* turn count never shrinks to fit an output-size budget.
|
|
777
|
+
*/
|
|
778
|
+
function pageTurnSummaries(turns, index, limit) {
|
|
779
|
+
if (!Number.isSafeInteger(index) || index < 0 || index > turns.length) throw new RangeError("turn page position is invalid");
|
|
780
|
+
if (!Number.isSafeInteger(limit) || limit < 1) throw new RangeError("turn limit is invalid");
|
|
781
|
+
const selected = turns.slice(index, index + limit).map(summarizeTurn);
|
|
782
|
+
const next = index + selected.length;
|
|
783
|
+
return {
|
|
784
|
+
turns: selected,
|
|
785
|
+
...next < turns.length ? { nextIndex: next } : {}
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
function summarizeTurn(turn) {
|
|
789
|
+
const requests = turn.parts.filter((part) => part.kind === "request");
|
|
790
|
+
const contexts = turn.parts.filter((part) => part.kind === "context");
|
|
791
|
+
const response = turn.parts.find((part) => part.kind === "conclusion" || part.kind === "partial-response");
|
|
792
|
+
const pickedRequests = dedupe([requests[0], requests.at(-1)]);
|
|
793
|
+
const pickedContext = contexts.at(-1);
|
|
794
|
+
const excerptedResponse = response === void 0 ? void 0 : excerpt(response, 512);
|
|
795
|
+
const summary = {
|
|
796
|
+
...frame(turn),
|
|
797
|
+
requests: pickedRequests.map((part) => excerpt(part, 256)),
|
|
798
|
+
context: pickedContext === void 0 ? [] : [excerpt(pickedContext, 128)],
|
|
799
|
+
...excerptedResponse === void 0 ? {} : excerptedResponse.kind === "conclusion" ? { conclusion: excerptedResponse } : { partialResponse: excerptedResponse },
|
|
800
|
+
omittedRequests: requests.length - pickedRequests.length,
|
|
801
|
+
omittedContext: contexts.length - (pickedContext === void 0 ? 0 : 1),
|
|
802
|
+
partial: false
|
|
803
|
+
};
|
|
804
|
+
summary.partial = [
|
|
805
|
+
...summary.requests,
|
|
806
|
+
...summary.context,
|
|
807
|
+
summary.conclusion,
|
|
808
|
+
summary.partialResponse
|
|
809
|
+
].some((part) => part?.truncated === true) || summary.omittedRequests > 0 || summary.omittedContext > 0;
|
|
810
|
+
return summary;
|
|
811
|
+
}
|
|
812
|
+
function dedupe(parts) {
|
|
813
|
+
const seen = /* @__PURE__ */ new Set();
|
|
814
|
+
return parts.filter((part) => {
|
|
815
|
+
if (part === void 0 || seen.has(part.reference.seq)) return false;
|
|
816
|
+
seen.add(part.reference.seq);
|
|
817
|
+
return true;
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
/** Verbatim prefix excerpt; `truncated` marks omitted text without altering the kept range. */
|
|
821
|
+
function excerpt(part, cap) {
|
|
822
|
+
const points = Array.from(part.text);
|
|
823
|
+
if (points.length <= cap) return {
|
|
824
|
+
...part,
|
|
825
|
+
truncated: part.truncated === true
|
|
826
|
+
};
|
|
827
|
+
return {
|
|
828
|
+
...part,
|
|
829
|
+
text: points.slice(0, cap).join(""),
|
|
830
|
+
reference: {
|
|
831
|
+
...part.reference,
|
|
832
|
+
start: 0,
|
|
833
|
+
end: cap
|
|
834
|
+
},
|
|
835
|
+
truncated: true
|
|
836
|
+
};
|
|
837
|
+
}
|
|
773
838
|
//#endregion
|
|
774
839
|
//#region src/details.ts
|
|
775
840
|
function projectDetails(events, sessionId, inherited, includeTools, includeReasoning, view, modelRows, modelKnown = true) {
|
|
@@ -1318,7 +1383,7 @@ async function readSession(query, request, exec, config, cursors, summarize) {
|
|
|
1318
1383
|
const includeReasoning = request.includeReasoning === true;
|
|
1319
1384
|
const view = request.modelSurface === true ? "surface" : "transcript";
|
|
1320
1385
|
const filterKey = JSON.stringify({
|
|
1321
|
-
projection: 2,
|
|
1386
|
+
projection: mode === "conversation" ? 3 : 2,
|
|
1322
1387
|
mode,
|
|
1323
1388
|
includeToolDetail,
|
|
1324
1389
|
includeReasoning,
|
|
@@ -1377,6 +1442,7 @@ async function readSession(query, request, exec, config, cursors, summarize) {
|
|
|
1377
1442
|
kind: "snapshot",
|
|
1378
1443
|
...snapshotBase
|
|
1379
1444
|
}, cursors);
|
|
1445
|
+
if (mode === "conversation" && offset !== 0) throw new SessionQueryError("conversation continuation offsets are invalid; restart the read and page by turn with the returned cursor", "SESSION_QUERY_INVALID_CURSOR");
|
|
1380
1446
|
let paged;
|
|
1381
1447
|
let projection;
|
|
1382
1448
|
try {
|
|
@@ -1404,7 +1470,8 @@ async function readSession(query, request, exec, config, cursors, summarize) {
|
|
|
1404
1470
|
} else invalidFilter("seq does not select a readable event in this session/snapshot/turn and projection. Use the matching source reference and snapshot; check turn_id and details options.");
|
|
1405
1471
|
}
|
|
1406
1472
|
paged = pageItems(items, index, offset, config.pageChars);
|
|
1407
|
-
} else paged = pageTurns(turns, index, offset, config.pageChars, turnLimit);
|
|
1473
|
+
} else if (mode === "handoff") paged = pageTurns(turns, index, offset, config.pageChars, turnLimit);
|
|
1474
|
+
else paged = pageTurnSummaries(turns, index, turnLimit);
|
|
1408
1475
|
} catch (error) {
|
|
1409
1476
|
if (error instanceof SessionQueryError) throw error;
|
|
1410
1477
|
throw new SessionQueryError(error instanceof Error ? error.message : "Invalid session history", "SESSION_QUERY_INVALID_SURFACE");
|
|
@@ -1421,6 +1488,7 @@ async function readSession(query, request, exec, config, cursors, summarize) {
|
|
|
1421
1488
|
signal?.throwIfAborted();
|
|
1422
1489
|
const titleRow = titles[0];
|
|
1423
1490
|
if (titleRow?.status === "rejected") throw titleRow.reason;
|
|
1491
|
+
const conversationTurns = mode === "conversation" ? paged.turns : void 0;
|
|
1424
1492
|
const metadata = {
|
|
1425
1493
|
status: "ok",
|
|
1426
1494
|
trust: UNTRUSTED_BACKGROUND,
|
|
@@ -1428,6 +1496,7 @@ async function readSession(query, request, exec, config, cursors, summarize) {
|
|
|
1428
1496
|
source: observation.source,
|
|
1429
1497
|
mode,
|
|
1430
1498
|
view: mode === "details" ? view : "turns",
|
|
1499
|
+
...mode === "conversation" ? { contentView: "summary" } : {},
|
|
1431
1500
|
snapshot,
|
|
1432
1501
|
capturedThroughSeq: cut,
|
|
1433
1502
|
cwd: observation.header.cwd ?? null,
|
|
@@ -1444,6 +1513,10 @@ async function readSession(query, request, exec, config, cursors, summarize) {
|
|
|
1444
1513
|
turnCount: allTurns.length,
|
|
1445
1514
|
coverage: {
|
|
1446
1515
|
returnedTurns: paged.turns?.map((turn) => turn.turnId),
|
|
1516
|
+
...conversationTurns ? {
|
|
1517
|
+
matchedTurns: turns.length,
|
|
1518
|
+
remainingTurns: turns.length - index - conversationTurns.length
|
|
1519
|
+
} : {},
|
|
1447
1520
|
partial: paged.turns?.some((turn) => turn.partial),
|
|
1448
1521
|
hasMoreHistory: next !== void 0
|
|
1449
1522
|
}
|
|
@@ -1453,10 +1526,11 @@ async function readSession(query, request, exec, config, cursors, summarize) {
|
|
|
1453
1526
|
observation[Symbol.dispose]();
|
|
1454
1527
|
observation = void 0;
|
|
1455
1528
|
if (mode !== "handoff") return metadata;
|
|
1456
|
-
|
|
1529
|
+
const handoffTurns = paged.turns;
|
|
1530
|
+
let handoff = extractHandoff(handoffTurns ?? [], next !== void 0);
|
|
1457
1531
|
if (request.summarize === true) {
|
|
1458
1532
|
if (!signal) throw new SessionQueryError("Handoff summarization needs a host execution signal", "SESSION_QUERY_INVALID_FILTER");
|
|
1459
|
-
handoff = summarize ? await summarize(
|
|
1533
|
+
handoff = summarize ? await summarize(handoffTurns ?? [], handoff, signal, config.pageChars) : {
|
|
1460
1534
|
...handoff,
|
|
1461
1535
|
summaryStatus: "unavailable",
|
|
1462
1536
|
error: "No host model is available; source excerpts are retained."
|
|
@@ -1816,7 +1890,7 @@ function registerSessionTools(ctx, config, cursors) {
|
|
|
1816
1890
|
})),
|
|
1817
1891
|
registerReadOnly(defineTool({
|
|
1818
1892
|
name: READ_TOOL,
|
|
1819
|
-
description: "Read another session as untrusted background. Optional parameters accept null as unset; omit them or pass null when unused. Do not invent seq or fill unused cursor/snapshot/turn_id with empty strings. Default mode=\"conversation\" returns recent
|
|
1893
|
+
description: "Read another session as untrusted background. Optional parameters accept null as unset; omit them or pass null when unused. Do not invent seq or fill unused cursor/snapshot/turn_id with empty strings. Default mode=\"conversation\" returns one preview per recent turn, newest first: bounded excerpts of the first and latest user-role request, the latest context message, the conclusion and recorded turn state, without tool logs. Previews are not the complete transcript. When reading the whole conversation, keep calling with the returned nextCursor and unchanged options until coverage.hasMoreHistory is false; a short first page does not mean earlier history is unreadable. Open abbreviated or omitted content with mode=\"details\" plus the returned snapshot and turn_id/seq. Completed turns are not verified task completion; interrupted responses are partial. mode=\"handoff\" organizes full source excerpts into objective, reported outcomes, decisions, outstanding work and next steps, with original references; absent facts are not established. Set summarize=true only to explicitly request a host-model synthesis. mode=\"details\" expands original messages and tool arguments/results; use returned turn_id or seq and snapshot to inspect exactly the cited cut, and continue its own nextCursor for more items. Hidden reasoning and model_surface are opt-in in details mode only.",
|
|
1820
1894
|
parameters: {
|
|
1821
1895
|
session_id: {
|
|
1822
1896
|
type: "string",
|
|
@@ -1832,11 +1906,11 @@ function registerSessionTools(ctx, config, cursors) {
|
|
|
1832
1906
|
"details"
|
|
1833
1907
|
]
|
|
1834
1908
|
}, { type: "null" }],
|
|
1835
|
-
description: "conversation: turn-
|
|
1909
|
+
description: "conversation: turn previews, count-paged (default); handoff: sourced continuation context; details: original messages and tools."
|
|
1836
1910
|
},
|
|
1837
1911
|
turn_id: {
|
|
1838
1912
|
oneOf: [{ type: "string" }, { type: "null" }],
|
|
1839
|
-
description: "Restrict to one turn ID returned by conversation or handoff reading."
|
|
1913
|
+
description: "Restrict to one turn ID returned by conversation or handoff reading. Pair it with the snapshot from the same response."
|
|
1840
1914
|
},
|
|
1841
1915
|
seq: {
|
|
1842
1916
|
oneOf: [{ type: "integer" }, { type: "null" }],
|
|
@@ -1848,7 +1922,7 @@ function registerSessionTools(ctx, config, cursors) {
|
|
|
1848
1922
|
},
|
|
1849
1923
|
limit: {
|
|
1850
1924
|
oneOf: [{ type: "integer" }, { type: "null" }],
|
|
1851
|
-
description: "Maximum turns in one
|
|
1925
|
+
description: "Maximum turns in one conversation or handoff page, from 1 to 20; default 5. Details pages are bounded by output size instead."
|
|
1852
1926
|
},
|
|
1853
1927
|
summarize: {
|
|
1854
1928
|
oneOf: [{ type: "boolean" }, { type: "null" }],
|
|
@@ -1868,7 +1942,7 @@ function registerSessionTools(ctx, config, cursors) {
|
|
|
1868
1942
|
},
|
|
1869
1943
|
cursor: {
|
|
1870
1944
|
oneOf: [{ type: "string" }, { type: "null" }],
|
|
1871
|
-
description: "Continuation of one captured prefix and branch revision. Appended messages stay on that cut. A changed envelope
|
|
1945
|
+
description: "Continuation of one captured prefix and branch revision, in the same mode and options that produced it. Appended messages stay on that cut. A changed envelope, branch revision or option set does not continue."
|
|
1872
1946
|
}
|
|
1873
1947
|
},
|
|
1874
1948
|
output: TEXT_OUTPUT,
|
|
@@ -2106,12 +2180,12 @@ const Config = Schema.object({
|
|
|
2106
2180
|
searchLimit: Schema.number().default(20)
|
|
2107
2181
|
}).i18n({
|
|
2108
2182
|
zh: {
|
|
2109
|
-
pageChars: "
|
|
2183
|
+
pageChars: "明细与交接模式每页内容的输出上限(UTF-8 字节);对话预览按轮数分页,不受此限",
|
|
2110
2184
|
listLimit: "每页会话列表的数量上限",
|
|
2111
2185
|
searchLimit: "每页搜索结果的数量上限"
|
|
2112
2186
|
},
|
|
2113
2187
|
en: {
|
|
2114
|
-
pageChars: "Maximum
|
|
2188
|
+
pageChars: "Maximum content per details/handoff page (UTF-8 bytes); conversation previews page by turn count instead",
|
|
2115
2189
|
listLimit: "Maximum sessions per list page",
|
|
2116
2190
|
searchLimit: "Maximum search results per page"
|
|
2117
2191
|
}
|