Skip to content

Commit e93458f

Browse files
committed
fix: keep transcript hit helpers lightweight
1 parent 5300011 commit e93458f

4 files changed

Lines changed: 154 additions & 121 deletions

File tree

src/plugin-sdk/session-transcript-hit.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Session transcript hit tests cover transcript match formatting and path resolution.
2+
import fs from "node:fs";
23
import { describe, expect, it } from "vitest";
34
import type { SessionEntry } from "../config/sessions/types.js";
45
import {
@@ -278,6 +279,12 @@ describe("resolveTranscriptStemToSessionKeys", () => {
278279
});
279280

280281
describe("session transcript memory hit key compatibility exports", () => {
282+
it("keeps hit-subpath memory helpers off the runtime writer import path", () => {
283+
const source = fs.readFileSync(new URL("./session-transcript-hit.ts", import.meta.url), "utf8");
284+
285+
expect(source).not.toContain("session-transcript-runtime.js");
286+
});
287+
281288
it("exports storage-neutral memory hit key helpers from the legacy hit subpath", () => {
282289
const key = formatSessionTranscriptMemoryHitKey({
283290
agentId: "main",

src/plugin-sdk/session-transcript-hit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ export {
99
formatSessionTranscriptMemoryHitKey,
1010
parseSessionTranscriptMemoryHitKey,
1111
resolveSessionTranscriptMemoryHitKeyToSessionKeys,
12-
} from "./session-transcript-runtime.js";
12+
} from "./session-transcript-memory-hit.js";
1313
export type {
1414
ResolveSessionTranscriptMemoryHitKeyParams,
1515
SessionTranscriptIdentity,
1616
SessionTranscriptMemoryHitIdentity,
1717
SessionTranscriptMemoryHitKey,
1818
SessionTranscriptMemoryHitKeyParams,
1919
SessionTranscriptReadParams,
20-
} from "./session-transcript-runtime.js";
20+
} from "./session-transcript-memory-hit.js";
2121

2222
export { loadCombinedSessionStoreForGateway } from "../config/sessions/combined-store-gateway.js";
2323

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { normalizeOptionalString } from "../../packages/normalization-core/src/string-coerce.js";
2+
import { uniqueStrings } from "../../packages/normalization-core/src/string-normalization.js";
3+
import type { SessionEntry } from "../config/sessions/types.js";
4+
import { normalizeAgentId, resolveAgentIdFromSessionKey } from "../routing/session-key.js";
5+
6+
const SESSION_TRANSCRIPT_MEMORY_HIT_PREFIX = "transcript";
7+
8+
export type SessionTranscriptIdentity = {
9+
agentId: string;
10+
memoryKey: SessionTranscriptMemoryHitKey;
11+
sessionId: string;
12+
sessionKey: string;
13+
};
14+
15+
export type SessionTranscriptMemoryHitIdentity = {
16+
agentId: string;
17+
key: SessionTranscriptMemoryHitKey;
18+
sessionId: string;
19+
};
20+
21+
export type SessionTranscriptMemoryHitKey = `transcript:${string}:${string}`;
22+
23+
export type SessionTranscriptReadParams = {
24+
agentId?: string;
25+
env?: NodeJS.ProcessEnv;
26+
hydrateSkillPromptRefs?: boolean;
27+
sessionId: string;
28+
sessionKey: string;
29+
storePath?: string;
30+
threadId?: string | number;
31+
};
32+
33+
export type SessionTranscriptMemoryHitKeyParams = {
34+
agentId: string;
35+
sessionId: string;
36+
};
37+
38+
export type ResolveSessionTranscriptMemoryHitKeyParams = {
39+
includeSyntheticFallback?: boolean;
40+
key: string;
41+
store: Record<string, SessionEntry>;
42+
};
43+
44+
function requireMemoryKeySegment(value: string, label: string): string {
45+
const normalized = normalizeOptionalString(value);
46+
if (!normalized) {
47+
throw new Error(`Cannot build session transcript memory hit key without ${label}.`);
48+
}
49+
return encodeURIComponent(normalized);
50+
}
51+
52+
function decodeMemoryKeySegment(value: string): string | null {
53+
try {
54+
return normalizeOptionalString(decodeURIComponent(value)) ?? null;
55+
} catch {
56+
return null;
57+
}
58+
}
59+
60+
function syntheticSessionKey(identity: SessionTranscriptMemoryHitIdentity): string {
61+
return `agent:${identity.agentId}:${identity.sessionId}`;
62+
}
63+
64+
/**
65+
* Builds the memory hit key for one session transcript.
66+
*/
67+
export function formatSessionTranscriptMemoryHitKey(
68+
params: SessionTranscriptMemoryHitKeyParams,
69+
): SessionTranscriptMemoryHitKey {
70+
const agentId = requireMemoryKeySegment(normalizeAgentId(params.agentId), "agentId");
71+
const sessionId = requireMemoryKeySegment(params.sessionId, "sessionId");
72+
return `${SESSION_TRANSCRIPT_MEMORY_HIT_PREFIX}:${agentId}:${sessionId}`;
73+
}
74+
75+
/**
76+
* Parses a session transcript memory hit key.
77+
*/
78+
export function parseSessionTranscriptMemoryHitKey(
79+
key: string,
80+
): SessionTranscriptMemoryHitIdentity | null {
81+
const parts = key.split(":");
82+
if (parts.length !== 3 || parts[0] !== SESSION_TRANSCRIPT_MEMORY_HIT_PREFIX) {
83+
return null;
84+
}
85+
const agentId = decodeMemoryKeySegment(parts[1] ?? "");
86+
const sessionId = decodeMemoryKeySegment(parts[2] ?? "");
87+
if (!agentId || !sessionId) {
88+
return null;
89+
}
90+
return {
91+
agentId: normalizeAgentId(agentId),
92+
key: formatSessionTranscriptMemoryHitKey({ agentId, sessionId }),
93+
sessionId,
94+
};
95+
}
96+
97+
/**
98+
* Maps a session transcript memory hit key back to visible session store keys.
99+
*/
100+
export function resolveSessionTranscriptMemoryHitKeyToSessionKeys(
101+
params: ResolveSessionTranscriptMemoryHitKeyParams,
102+
): string[] {
103+
const identity = parseSessionTranscriptMemoryHitKey(params.key);
104+
if (!identity) {
105+
return [];
106+
}
107+
const matches = Object.entries(params.store)
108+
.filter(([sessionKey, entry]) => {
109+
return (
110+
entry.sessionId === identity.sessionId &&
111+
normalizeAgentId(resolveAgentIdFromSessionKey(sessionKey)) === identity.agentId
112+
);
113+
})
114+
.map(([sessionKey]) => sessionKey);
115+
const deduped = uniqueStrings(matches);
116+
if (deduped.length > 0) {
117+
return deduped;
118+
}
119+
return params.includeSyntheticFallback === false ? [] : [syntheticSessionKey(identity)];
120+
}

src/plugin-sdk/session-transcript-runtime.ts

Lines changed: 25 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { normalizeOptionalString } from "../../packages/normalization-core/src/string-coerce.js";
2-
import { uniqueStrings } from "../../packages/normalization-core/src/string-normalization.js";
31
import {
42
appendTranscriptMessage,
53
publishTranscriptUpdate,
@@ -11,37 +9,34 @@ import {
119
} from "../config/sessions/session-accessor.js";
1210
import { runSessionTranscriptAppendTransaction } from "../config/sessions/transcript-append.js";
1311
import { streamSessionTranscriptLines } from "../config/sessions/transcript-stream.js";
14-
import type { SessionEntry } from "../config/sessions/types.js";
15-
import { normalizeAgentId, resolveAgentIdFromSessionKey } from "../routing/session-key.js";
16-
17-
const SESSION_TRANSCRIPT_MEMORY_HIT_PREFIX = "transcript";
18-
19-
export type SessionTranscriptEvent = unknown;
20-
21-
export type SessionTranscriptIdentity = {
22-
agentId: string;
23-
memoryKey: SessionTranscriptMemoryHitKey;
24-
sessionId: string;
25-
sessionKey: string;
12+
import { normalizeAgentId } from "../routing/session-key.js";
13+
import {
14+
formatSessionTranscriptMemoryHitKey,
15+
parseSessionTranscriptMemoryHitKey,
16+
resolveSessionTranscriptMemoryHitKeyToSessionKeys,
17+
type ResolveSessionTranscriptMemoryHitKeyParams,
18+
type SessionTranscriptIdentity,
19+
type SessionTranscriptMemoryHitIdentity,
20+
type SessionTranscriptMemoryHitKey,
21+
type SessionTranscriptMemoryHitKeyParams,
22+
type SessionTranscriptReadParams,
23+
} from "./session-transcript-memory-hit.js";
24+
25+
export {
26+
formatSessionTranscriptMemoryHitKey,
27+
parseSessionTranscriptMemoryHitKey,
28+
resolveSessionTranscriptMemoryHitKeyToSessionKeys,
2629
};
27-
28-
export type SessionTranscriptMemoryHitIdentity = {
29-
agentId: string;
30-
key: SessionTranscriptMemoryHitKey;
31-
sessionId: string;
30+
export type {
31+
ResolveSessionTranscriptMemoryHitKeyParams,
32+
SessionTranscriptIdentity,
33+
SessionTranscriptMemoryHitIdentity,
34+
SessionTranscriptMemoryHitKey,
35+
SessionTranscriptMemoryHitKeyParams,
36+
SessionTranscriptReadParams,
3237
};
3338

34-
export type SessionTranscriptMemoryHitKey = `transcript:${string}:${string}`;
35-
36-
export type SessionTranscriptReadParams = {
37-
agentId?: string;
38-
env?: NodeJS.ProcessEnv;
39-
hydrateSkillPromptRefs?: boolean;
40-
sessionId: string;
41-
sessionKey: string;
42-
storePath?: string;
43-
threadId?: string | number;
44-
};
39+
export type SessionTranscriptEvent = unknown;
4540

4641
export type SessionTranscriptTargetParams = SessionTranscriptReadParams & {
4742
/**
@@ -72,70 +67,6 @@ export type SessionTranscriptWriteLockContext = {
7267
target: SessionTranscriptTarget;
7368
};
7469

75-
export type SessionTranscriptMemoryHitKeyParams = {
76-
agentId: string;
77-
sessionId: string;
78-
};
79-
80-
export type ResolveSessionTranscriptMemoryHitKeyParams = {
81-
includeSyntheticFallback?: boolean;
82-
key: string;
83-
store: Record<string, SessionEntry>;
84-
};
85-
86-
function requireMemoryKeySegment(value: string, label: string): string {
87-
const normalized = normalizeOptionalString(value);
88-
if (!normalized) {
89-
throw new Error(`Cannot build session transcript memory hit key without ${label}.`);
90-
}
91-
return encodeURIComponent(normalized);
92-
}
93-
94-
function decodeMemoryKeySegment(value: string): string | null {
95-
try {
96-
return normalizeOptionalString(decodeURIComponent(value)) ?? null;
97-
} catch {
98-
return null;
99-
}
100-
}
101-
102-
function syntheticSessionKey(identity: SessionTranscriptMemoryHitIdentity): string {
103-
return `agent:${identity.agentId}:${identity.sessionId}`;
104-
}
105-
106-
/**
107-
* Builds the memory hit key for one session transcript.
108-
*/
109-
export function formatSessionTranscriptMemoryHitKey(
110-
params: SessionTranscriptMemoryHitKeyParams,
111-
): SessionTranscriptMemoryHitKey {
112-
const agentId = requireMemoryKeySegment(normalizeAgentId(params.agentId), "agentId");
113-
const sessionId = requireMemoryKeySegment(params.sessionId, "sessionId");
114-
return `${SESSION_TRANSCRIPT_MEMORY_HIT_PREFIX}:${agentId}:${sessionId}`;
115-
}
116-
117-
/**
118-
* Parses a session transcript memory hit key.
119-
*/
120-
export function parseSessionTranscriptMemoryHitKey(
121-
key: string,
122-
): SessionTranscriptMemoryHitIdentity | null {
123-
const parts = key.split(":");
124-
if (parts.length !== 3 || parts[0] !== SESSION_TRANSCRIPT_MEMORY_HIT_PREFIX) {
125-
return null;
126-
}
127-
const agentId = decodeMemoryKeySegment(parts[1] ?? "");
128-
const sessionId = decodeMemoryKeySegment(parts[2] ?? "");
129-
if (!agentId || !sessionId) {
130-
return null;
131-
}
132-
return {
133-
agentId: normalizeAgentId(agentId),
134-
key: formatSessionTranscriptMemoryHitKey({ agentId, sessionId }),
135-
sessionId,
136-
};
137-
}
138-
13970
/**
14071
* Resolves the public identity for a transcript without returning its file path.
14172
*/
@@ -260,31 +191,6 @@ export async function withSessionTranscriptWriteLock<T>(
260191
return result;
261192
}
262193

263-
/**
264-
* Maps a session transcript memory hit key back to visible session store keys.
265-
*/
266-
export function resolveSessionTranscriptMemoryHitKeyToSessionKeys(
267-
params: ResolveSessionTranscriptMemoryHitKeyParams,
268-
): string[] {
269-
const identity = parseSessionTranscriptMemoryHitKey(params.key);
270-
if (!identity) {
271-
return [];
272-
}
273-
const matches = Object.entries(params.store)
274-
.filter(([sessionKey, entry]) => {
275-
return (
276-
entry.sessionId === identity.sessionId &&
277-
normalizeAgentId(resolveAgentIdFromSessionKey(sessionKey)) === identity.agentId
278-
);
279-
})
280-
.map(([sessionKey]) => sessionKey);
281-
const deduped = uniqueStrings(matches);
282-
if (deduped.length > 0) {
283-
return deduped;
284-
}
285-
return params.includeSyntheticFallback === false ? [] : [syntheticSessionKey(identity)];
286-
}
287-
288194
function projectPublicTarget(target: {
289195
agentId: string;
290196
sessionId: string;

0 commit comments

Comments
 (0)