dsh-session-recall 0.5.0 → 0.6.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/lib/index.d.ts +10 -1
- package/lib/index.js +35 -2
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ToolDefinition, ToolRunContext } from "@deepseek-ai/dsh-tools";
|
|
2
|
-
import { SessionEventResultFilter, SessionEventSearchDocument, SessionEventSearchPage, SessionEventSearchRequest, SessionRecord, SessionSearchExecContext, SessionSearchHit, SessionSearchPage, SessionSearchRequest, SessionTitleObservationResult } from "@deepseek-ai/dsh-session-query";
|
|
2
|
+
import { SessionEventResultFilter, SessionEventSearchDocument, SessionEventSearchPage, SessionEventSearchRequest, SessionLineageTrace, SessionRecord, SessionSearchExecContext, SessionSearchHit, SessionSearchPage, SessionSearchRequest, SessionTitleObservationResult } from "@deepseek-ai/dsh-session-query";
|
|
3
3
|
import { JsonValue, SessionId } from "@deepseek-ai/dsh-session";
|
|
4
4
|
import { Context } from "@deepseek-ai/cordis";
|
|
5
5
|
import { ContentBlock } from "@deepseek-ai/dsh-llm";
|
|
@@ -50,6 +50,12 @@ interface RecallConfig {
|
|
|
50
50
|
recencyHalfLifeDays?: number;
|
|
51
51
|
/** Sessions started in these project directories rank first. Default: none. */
|
|
52
52
|
pinnedCwds?: readonly string[];
|
|
53
|
+
/**
|
|
54
|
+
* Restrict recall results to sessions in the calling agent's own lineage
|
|
55
|
+
* (ancestors + self + descendants). Default `true` — other agents in the
|
|
56
|
+
* same project are invisible unless this is set to `false`.
|
|
57
|
+
*/
|
|
58
|
+
callerTreeOnly?: boolean;
|
|
53
59
|
}
|
|
54
60
|
/** Validated, fully defaulted configuration. */
|
|
55
61
|
interface NormalizedRecallConfig {
|
|
@@ -65,6 +71,7 @@ interface NormalizedRecallConfig {
|
|
|
65
71
|
readonly allProjectsPolicy: AllProjectsPolicy;
|
|
66
72
|
readonly recencyHalfLifeDays: number | undefined;
|
|
67
73
|
readonly pinnedCwds: readonly string[];
|
|
74
|
+
readonly callerTreeOnly: boolean;
|
|
68
75
|
}
|
|
69
76
|
/** Default, clamp, and cross-check every optional field. */
|
|
70
77
|
declare function normalizeRecallConfig(config?: RecallConfig): NormalizedRecallConfig;
|
|
@@ -186,6 +193,8 @@ interface RecallQueryEngine {
|
|
|
186
193
|
readTitleSnapshots(sessionIds: readonly string[], signal?: AbortSignal): Promise<SessionTitleObservationResult[]>;
|
|
187
194
|
listSessions(signal?: AbortSignal): Promise<SessionRecord[]>;
|
|
188
195
|
filterEvents(sessionId: SessionId, filters: readonly SessionEventResultFilter[]): Promise<SessionEventSearchDocument[]>;
|
|
196
|
+
/** Trace the caller's session lineage for caller-authorization gating. */
|
|
197
|
+
traceSession(sessionId: SessionId, signal?: AbortSignal): Promise<SessionLineageTrace>;
|
|
189
198
|
}
|
|
190
199
|
declare const RECALL_TOOL_DESCRIPTION: string;
|
|
191
200
|
/** The approval verdict vocabulary mirrored from `@deepseek-ai/dsh-user-approval`. */
|
package/lib/index.js
CHANGED
|
@@ -97,7 +97,8 @@ function normalizeRecallConfig(config) {
|
|
|
97
97
|
cwdDenylist: stringList(config?.cwdDenylist),
|
|
98
98
|
allProjectsPolicy: normalizePolicy(config?.allProjectsPolicy),
|
|
99
99
|
recencyHalfLifeDays: typeof config?.recencyHalfLifeDays === "number" && Number.isFinite(config.recencyHalfLifeDays) && config.recencyHalfLifeDays > 0 ? Math.min(3650, Math.trunc(config.recencyHalfLifeDays)) : void 0,
|
|
100
|
-
pinnedCwds: stringList(config?.pinnedCwds)
|
|
100
|
+
pinnedCwds: stringList(config?.pinnedCwds),
|
|
101
|
+
callerTreeOnly: config?.callerTreeOnly !== false
|
|
101
102
|
};
|
|
102
103
|
}
|
|
103
104
|
/** Whether a session cwd is searchable under the allowlist/denylist policy. */
|
|
@@ -296,11 +297,26 @@ function cjkZeroHitHint(query, zeroHits, enabled) {
|
|
|
296
297
|
* calling agent's own project cwd unless the model explicitly widens the
|
|
297
298
|
* scope and the deployment allows it.
|
|
298
299
|
*/
|
|
300
|
+
/** Walk a lineage trace and collect every session id in the tree. */
|
|
301
|
+
function collectLineageIds(trace) {
|
|
302
|
+
const ids = /* @__PURE__ */ new Set();
|
|
303
|
+
ids.add(trace.target.header.id);
|
|
304
|
+
for (const ancestor of trace.ancestors) ids.add(ancestor.header.id);
|
|
305
|
+
const walk = (nodes) => {
|
|
306
|
+
for (const node of nodes) {
|
|
307
|
+
ids.add(node.session.header.id);
|
|
308
|
+
walk(node.descendants);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
walk(trace.descendants);
|
|
312
|
+
return ids;
|
|
313
|
+
}
|
|
299
314
|
const RECALL_TOOL_DESCRIPTION = [
|
|
300
315
|
"Search the FULL TEXT of past and current session transcripts on this machine (your own conversation history with this user).",
|
|
301
316
|
"Use it when the user refers to earlier work (\"that bug we fixed last week\", \"the font we chose for my resume\") or when prior context was compacted away.",
|
|
302
317
|
"Matches whole words/phrases for English and code identifiers; a zero-hit Chinese (CJK) query automatically falls back to a substring scan in which every whitespace-separated term must match. Returns the best-matching event snippet per session plus the session id.",
|
|
303
318
|
"Then use the read tool on files, or ask the user, to go deeper — this tool only points at history, it does not resume sessions.",
|
|
319
|
+
"To save a full evidence report of a hit session, call the transcript_export tool (from dsh-session-export) with its sessionId when available.",
|
|
304
320
|
"Scoping: by default only sessions started in the current project directory; pass all_projects=true to search everywhere (the deployment may ignore it or require user approval).",
|
|
305
321
|
"When the deployment enables redaction, secret-looking text in snippets appears as [REDACTED] or a #hash marker — treat it as removed; do not try to reconstruct or echo it.",
|
|
306
322
|
"The first search after startup may be slow while the index builds."
|
|
@@ -597,6 +613,11 @@ function createRecallTool(config, engine, approver) {
|
|
|
597
613
|
if (query === "") return recallError(query, Object.assign(/* @__PURE__ */ new Error("empty query"), { code: "SESSION_QUERY_INVALID_QUERY" }));
|
|
598
614
|
const limit = clamp(Math.trunc(args.limit ?? cfg.defaultLimit), 1, cfg.maxLimit);
|
|
599
615
|
const agentCwd = exec.agent?.session.header?.cwd ?? null;
|
|
616
|
+
const callerSessionId = exec.agent?.session?.id;
|
|
617
|
+
let allowedIds = null;
|
|
618
|
+
if (cfg.callerTreeOnly && callerSessionId != null) try {
|
|
619
|
+
allowedIds = collectLineageIds(await engine.traceSession(SessionId(callerSessionId), exec.signal));
|
|
620
|
+
} catch {}
|
|
600
621
|
const scopeHints = [];
|
|
601
622
|
let wantAll = false;
|
|
602
623
|
if (args.session_id == null && args.all_projects === true) {
|
|
@@ -632,6 +653,17 @@ function createRecallTool(config, engine, approver) {
|
|
|
632
653
|
limit,
|
|
633
654
|
cursor: brand(args.cursor)
|
|
634
655
|
}, { signal: exec.signal });
|
|
656
|
+
if (allowedIds != null && !allowedIds.has(args.session_id)) return {
|
|
657
|
+
query,
|
|
658
|
+
scope,
|
|
659
|
+
count: 0,
|
|
660
|
+
hasMore: false,
|
|
661
|
+
items: [],
|
|
662
|
+
nextCursor: null,
|
|
663
|
+
hint: "that session is outside your conversation lineage. Search without session_id to find sessions in your own tree, or ask the user to disable callerTreeOnly.",
|
|
664
|
+
redacted: 0,
|
|
665
|
+
diagnostics: null
|
|
666
|
+
};
|
|
635
667
|
if (!cwdAllowed(page.session.cwd ?? null, cfg)) return {
|
|
636
668
|
query,
|
|
637
669
|
scope,
|
|
@@ -705,6 +737,7 @@ function createRecallTool(config, engine, approver) {
|
|
|
705
737
|
};
|
|
706
738
|
const ranked = rankingActive(rankingOptions);
|
|
707
739
|
let items = rankItems(toItems(page.items, titles).filter((item) => cwdAllowed(item.cwd, cfg)), rankingOptions);
|
|
740
|
+
if (allowedIds != null) items = items.filter((item) => allowedIds.has(item.sessionId));
|
|
708
741
|
let hint = null;
|
|
709
742
|
let fallbackRan = false;
|
|
710
743
|
let diagnostics = {
|
|
@@ -715,7 +748,7 @@ function createRecallTool(config, engine, approver) {
|
|
|
715
748
|
fallbackRan = true;
|
|
716
749
|
const scan = await cjkScanSessions(engine, query, agentCwd, wantAll, cfg, cfg.cjkFallbackScanMax, limit, exec.signal);
|
|
717
750
|
const scanTitles = await titlesFor(engine, scan.items.map((item) => item.sessionId), exec.signal);
|
|
718
|
-
items = rankItems(scan.items.map((item) => ({
|
|
751
|
+
items = rankItems(scan.items.filter((item) => allowedIds == null || allowedIds.has(item.sessionId)).map((item) => ({
|
|
719
752
|
...item,
|
|
720
753
|
title: scanTitles.get(item.sessionId) ?? null
|
|
721
754
|
})), rankingOptions);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-session-recall",
|
|
3
3
|
"description": "Deterministic cross-session transcript retrieval for DeepSeek Harness: the model-facing `recall` tool searches past session logs with explicit scope control",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|