@deepseek-ai/dsh-session-reference 0.1.6-alpha.1 → 0.1.7-alpha.1

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.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/context/session-reference/README.md
5
- README.md: 533a1282da920b6daa47ef5be98e49c691a8cb84
6
- README.zh.md: e7b43c9a56cd5b4062770144ccea0bf125153b40
5
+ README.md: e1afeef9b0e78826705ad2b3e426373b2bf0833d
6
+ README.zh.md: e55dab4f6403cb6aee947f80873ffe99919acc5f
package/README.md CHANGED
@@ -39,7 +39,7 @@ For a truncated reference, an optional spill backend saves the full captured tex
39
39
 
40
40
  ### Finding sessions to reference
41
41
 
42
- `listCandidates(agent, query?, limit?)` lists sessions other than the agent's own, filters case-insensitively by id, working directory, or the projected title, and ranks same-directory sessions first. Each candidate carries its latest title as the mention label, falling back to the session id when the title is absent or unreadable, and reports whether its working directory is the requesting agent's so a host can surface a location only when it distinguishes the row. Browser consumers call the same discovery as `ctx.remote.sessionReferenceResolver.candidates`, which attaches each candidate's canonical mention.
42
+ `listCandidates(agent, query?, limit?)` lists sessions other than the agent's own, filters case-insensitively by id, working directory, projected title, or display title, and ranks same-directory sessions first. Each candidate carries its latest title as `label`, falling back to the session id when the title is absent or unreadable. Its display title prefers a subagent's durable creation label over that title. The candidate also reports whether its working directory is the requesting agent's so a host can surface a location only when it distinguishes the row. Browser consumers call the same discovery as `ctx.remote.sessionReferenceResolver.candidates`, which labels the canonical mention with `displayTitle` when present.
43
43
 
44
44
  ### Configuration
45
45
 
package/README.zh.md CHANGED
@@ -39,7 +39,7 @@ kind: "package-reference"
39
39
 
40
40
  ### 查找可引用的会话
41
41
 
42
- `listCandidates(agent, query?, limit?)` 列出除 agent 自身外的会话,按 id、工作目录或投影标题做不区分大小写的过滤,并把同目录会话排在前面。每个候选以其最新标题作为 mention 标签;标题缺失或不可读时回退到会话 id,并报告其工作目录是否就是发起方 agent 的工作目录,宿主因此可以只在位置能区分该行时才显示它。浏览器消费方通过 `ctx.remote.sessionReferenceResolver.candidates` 调用同一发现能力,该方法会为每个候选附上规范 mention。
42
+ `listCandidates(agent, query?, limit?)` 列出除 agent 自身外的会话,按 id、工作目录、投影标题或显示标题做不区分大小写的过滤,并把同目录会话排在前面。每个候选的 `label` 使用最新标题;标题缺失或不可读时回退到会话 id。显示标题优先使用 subagent 的持久创建 label,再回退到该标题。候选还会报告其工作目录是否就是发起方 agent 的工作目录,宿主因此可以只在位置能区分该行时才显示它。浏览器消费方通过 `ctx.remote.sessionReferenceResolver.candidates` 调用同一发现能力;该方法在 `displayTitle` 存在时用它标记规范 mention。
43
43
 
44
44
  ### 配置
45
45
 
package/lib/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
2
  import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
3
3
  import { LlmError, createUserMessage, freezeMessage } from "@deepseek-ai/dsh-llm";
4
- import { SessionLogOffset, SessionSeq } from "@deepseek-ai/dsh-session";
5
4
  import { isCompactCheckpointSource } from "@deepseek-ai/dsh-compaction";
6
5
  import { TextRetainer } from "@deepseek-ai/dsh-output-retention";
7
6
  import { assertNever } from "@deepseek-ai/dsh-util-values";
7
+ import { SessionSeq } from "@deepseek-ai/dsh-session";
8
8
  import { brandString } from "@deepseek-ai/dsh-brand";
9
9
  //#region lib/types/spill.js
10
10
  /** Full projected transcripts and model-visible spill outcomes for bounded reference previews. */
@@ -150,6 +150,7 @@ function projectSessionConversation(snapshot) {
150
150
  });
151
151
  break;
152
152
  }
153
+ case "developer/message":
153
154
  case "system/message":
154
155
  case "tool/result": break;
155
156
  /* v8 ignore next 2 -- SurfaceEventType is closed and every variant is handled above. */
@@ -508,14 +509,13 @@ let SessionReferenceResolver = (() => {
508
509
  /**
509
510
  * List reference candidates, ranked by working-directory affinity.
510
511
  *
511
- * Discovery runs at keystroke rate, so a title only ever comes from a
512
- * projection read: see {@link SessionReferenceResolver.projectedTitle} for
513
- * which sessions can answer one and which fall back to their id.
512
+ * Discovery runs at keystroke rate, so titles and subagent labels only ever
513
+ * come from projection reads; sessions without either fall back to their id.
514
514
  * @param agent - target agent; self is excluded and its cwd drives ranking.
515
- * @param query - optional case-insensitive session-id/cwd/title substring.
515
+ * @param query - optional case-insensitive session-id/cwd/title/display-title substring.
516
516
  * @param limit - optional positive result cap.
517
517
  * @param signal - optional cancellation boundary for host autocomplete teardown.
518
- * @returns candidates labeled by latest title or, when absent, session id.
518
+ * @returns candidates with canonical mention labels and presentation titles.
519
519
  */
520
520
  async listCandidates(agent, query = "", limit = this.config.candidateLimit, signal) {
521
521
  if (!Number.isSafeInteger(limit) || limit <= 0) throw new SessionReferenceError("candidate limit must be a positive safe integer", "SESSION_REFERENCE_INVALID_REFERENCE");
@@ -528,20 +528,21 @@ let SessionReferenceResolver = (() => {
528
528
  })).map(({ record, index }) => ({
529
529
  record,
530
530
  index,
531
- label: this.projectedTitle(record) ?? record.header.id
532
- })).filter(({ record, label }) => {
531
+ ...this.projectedLabels(record)
532
+ })).filter(({ record, label, displayTitle }) => {
533
533
  if (needle === "") return true;
534
- return record.header.id.toLocaleLowerCase().includes(needle) || record.header.cwd?.toLocaleLowerCase().includes(needle) === true || label.toLocaleLowerCase().includes(needle);
535
- }).sort((a, b) => candidateRank(a.record.header.cwd, targetCwd) - candidateRank(b.record.header.cwd, targetCwd) || a.index - b.index).slice(0, limit).map(({ record, label }) => ({
534
+ return record.header.id.toLocaleLowerCase().includes(needle) || record.header.cwd?.toLocaleLowerCase().includes(needle) === true || label.toLocaleLowerCase().includes(needle) || displayTitle.toLocaleLowerCase().includes(needle);
535
+ }).sort((a, b) => candidateRank(a.record.header.cwd, targetCwd) - candidateRank(b.record.header.cwd, targetCwd) || a.index - b.index).slice(0, limit).map(({ record, label, displayTitle }) => ({
536
536
  sessionId: record.header.id,
537
537
  label,
538
+ displayTitle,
538
539
  ...record.header.cwd === void 0 ? {} : { cwd: record.header.cwd },
539
540
  sameWorkspace: record.header.cwd !== void 0 && record.header.cwd === targetCwd,
540
541
  createdAt: record.header.createdAt
541
542
  }));
542
543
  }
543
544
  /**
544
- * The title a session's projections can answer without reading its log.
545
+ * The mention label and display title a Session's projections can answer without reading its log.
545
546
  *
546
547
  * Attachment is decided by the store at read time, not by the listing:
547
548
  * a session that attached in between would otherwise be answered from a
@@ -556,18 +557,21 @@ let SessionReferenceResolver = (() => {
556
557
  * Nothing else is attempted. Folding a title from a log costs the whole
557
558
  * log, and this call sits under every keystroke of `@` completion. A
558
559
  * session that no projection can answer for — one persisted before the
559
- * cache was composed, or seeded straight to disk — is labeled by its id
560
- * and cannot be found by its title until it is opened once, which
561
- * checkpoints it.
560
+ * cache was composed — is labeled by its id and cannot be found by its
561
+ * title until it is opened once, which checkpoints it.
562
562
  * @param record - the listed session, live or cold.
563
- * @returns the projected title, or undefined when no projection holds one.
563
+ * @returns the title-backed mention label and the subagent-label-first display title.
564
564
  */
565
- projectedTitle(record) {
565
+ projectedLabels(record) {
566
566
  const attached = this.ctx.get("sessions")?.get(record.header.id);
567
567
  const projections = this.ctx.get("sessionProjections");
568
- if (attached !== void 0 && projections !== void 0) return titleOf(projections.snapshot(attached, ["title"]));
569
- if (record.header.isSeeded) return void 0;
570
- return titleOf(this.ctx.get("sessionProjectionCache")?.cachedSnapshot(record.header, SessionLogOffset(0), ["title"]));
568
+ const snapshot = attached !== void 0 && projections !== void 0 ? projections.snapshot(attached, ["title", "subagent"]) : this.ctx.get("sessionProjectionCache")?.cachedSnapshot(record.header, ["title", "subagent"]);
569
+ const label = titleOf(snapshot) ?? record.header.id;
570
+ const subagent = snapshot?.values.subagent;
571
+ return {
572
+ label,
573
+ displayTitle: subagent === void 0 || subagent === null ? label : subagent.label ?? label
574
+ };
571
575
  }
572
576
  /**
573
577
  * Remote face of {@link listCandidates}: the configured candidate limit
@@ -583,7 +587,7 @@ let SessionReferenceResolver = (() => {
583
587
  ...candidate,
584
588
  mention: formatSessionReferenceMention({
585
589
  sessionId: candidate.sessionId,
586
- label: candidate.label
590
+ label: candidate.displayTitle ?? candidate.label
587
591
  })
588
592
  }));
589
593
  }
@@ -1,16 +1,20 @@
1
1
  /* Generated by @deepseek-ai/dsh-typert-generator from FaceModel — do not edit. */
2
2
  import { z } from 'zod'
3
3
 
4
- const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema = z.intersection(z.string(), z.unknown())
5
- const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema = z.string()
6
- const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema = z.array(z.object({
4
+ let _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema$value
5
+ const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema = () => (_deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema$value ??= z.intersection(z.string(), z.unknown()))
6
+ let _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema$value
7
+ const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema = () => (_deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema$value ??= z.string())
8
+ let _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema$value
9
+ const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema = () => (_deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema$value ??= z.array(z.object({
7
10
  'mention': z.string(),
8
11
  'sessionId': z.intersection(z.string(), z.unknown()),
9
12
  'label': z.string(),
13
+ 'displayTitle': z.string().optional(),
10
14
  'cwd': z.string().optional(),
11
15
  'sameWorkspace': z.boolean(),
12
16
  'createdAt': z.number(),
13
- }))
17
+ })))
14
18
 
15
19
  export const TYPERT = {
16
20
  package: '@deepseek-ai/dsh-session-reference',
@@ -38,7 +42,7 @@ export const TYPERT = {
38
42
  codec: {
39
43
  mode: 'strict',
40
44
  typeSymbol: '@deepseek-ai/dsh-session/types#SessionId',
41
- schema: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema,
45
+ create: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema,
42
46
  },
43
47
  },
44
48
  {
@@ -48,7 +52,7 @@ export const TYPERT = {
48
52
  codec: {
49
53
  mode: 'strict',
50
54
  typeSymbol: '@deepseek-ai/dsh-session-reference#sessionReferenceResolver/candidates:query',
51
- schema: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema,
55
+ create: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema,
52
56
  },
53
57
  },
54
58
  ],
@@ -56,9 +60,9 @@ export const TYPERT = {
56
60
  result: {
57
61
  mode: 'strict',
58
62
  typeSymbol: '@deepseek-ai/dsh-session-reference#sessionReferenceResolver/candidates:result',
59
- schema: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema,
63
+ create: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema,
60
64
  },
61
- sourceLocation: {"file":"packages/context/session-reference/src/index.ts","line":274,"column":9},
65
+ sourceLocation: {"file":"packages/context/session-reference/src/index.ts","line":272,"column":9},
62
66
  },
63
67
  ],
64
68
  model: {
@@ -76,7 +80,7 @@ export const TYPERT = {
76
80
  "name": "listCandidates",
77
81
  "signature": "async listCandidates( agent: Agent, query: string = '', limit: number = this.config.candidateLimit, signal?: AbortSignal, ): Promise<SessionReferenceCandidate[]>",
78
82
  "summary": "List reference candidates, ranked by working-directory affinity.",
79
- "jsDoc": "/**\n * List reference candidates, ranked by working-directory affinity.\n *\n * Discovery runs at keystroke rate, so a title only ever comes from a\n * projection read: see {@link SessionReferenceResolver.projectedTitle} for\n * which sessions can answer one and which fall back to their id.\n * @param agent - target agent; self is excluded and its cwd drives ranking.\n * @param query - optional case-insensitive session-id/cwd/title substring.\n * @param limit - optional positive result cap.\n * @param signal - optional cancellation boundary for host autocomplete teardown.\n * @returns candidates labeled by latest title or, when absent, session id.\n */"
83
+ "jsDoc": "/**\n * List reference candidates, ranked by working-directory affinity.\n *\n * Discovery runs at keystroke rate, so titles and subagent labels only ever\n * come from projection reads; sessions without either fall back to their id.\n * @param agent - target agent; self is excluded and its cwd drives ranking.\n * @param query - optional case-insensitive session-id/cwd/title/display-title substring.\n * @param limit - optional positive result cap.\n * @param signal - optional cancellation boundary for host autocomplete teardown.\n * @returns candidates with canonical mention labels and presentation titles.\n */"
80
84
  },
81
85
  {
82
86
  "kind": "method",
@@ -128,7 +132,7 @@ export const TYPERT = {
128
132
  },
129
133
  {
130
134
  "name": "AssistantMessage",
131
- "declaration": "export interface AssistantMessage extends Message {\n readonly role: 'assistant';\n readonly source: ModelMessageSource;\n}"
135
+ "declaration": "export interface AssistantMessage extends MessageBase {\n readonly role: 'assistant';\n readonly source: ModelMessageSource;\n}"
132
136
  },
133
137
  {
134
138
  "name": "AssistantProviderMetadata",
@@ -166,6 +170,10 @@ export const TYPERT = {
166
170
  "name": "CommandSourceMap",
167
171
  "declaration": "export interface CommandSourceMap {\n user: { kind: 'user'; };\n}"
168
172
  },
173
+ {
174
+ "name": "CompactionCheckpointSource",
175
+ "declaration": "export type CompactionCheckpointSource = typeof COMPACT_CHECKPOINT_MARKER & { readonly compactionId: CompactionId; readonly sourceCommandId?: CommandId; };"
176
+ },
169
177
  {
170
178
  "name": "CompactionId",
171
179
  "declaration": "export type CompactionId = Branded<'CompactionId'>;"
@@ -176,7 +184,7 @@ export const TYPERT = {
176
184
  },
177
185
  {
178
186
  "name": "ContentBlockMap",
179
- "declaration": "export interface ContentBlockMap {\n text: TextBlock;\n reasoning: ReasoningBlock;\n image: ImageBlock;\n file: FileBlock;\n 'tool-call': ToolCallBlock;\n 'tool-result': ToolResultBlock;\n}"
187
+ "declaration": "export interface ContentBlockMap {\n text: TextBlock;\n reasoning: ReasoningBlock;\n image: ImageBlock;\n file: FileBlock;\n 'tool-call': ToolCallBlock;\n 'tool-addition': ToolAdditionBlock;\n 'tool-removal': ToolRemovalBlock;\n}"
180
188
  },
181
189
  {
182
190
  "name": "ContentBlockType",
@@ -194,9 +202,13 @@ export const TYPERT = {
194
202
  "name": "ContinuableSubagentDescriptorData",
195
203
  "declaration": "export interface ContinuableSubagentDescriptorData extends SubagentDescriptorBase {\n readonly mode: 'continuable';\n readonly label: string;\n readonly agentProvider?: string;\n readonly agentModel?: string;\n readonly agentReasoningEffort?: ReasoningEffortId;\n readonly persona?: string;\n readonly toolFilter?: ToolRestriction;\n}"
196
204
  },
205
+ {
206
+ "name": "DeveloperMessage",
207
+ "declaration": "export interface DeveloperMessage extends MessageBase {\n readonly role: 'developer';\n}"
208
+ },
197
209
  {
198
210
  "name": "EpochHeader",
199
- "declaration": "export interface EpochHeader {\n config: LlmCallConfig;\n adapterDefaults?: LlmCallConfigAdapterDefaults;\n tools?: ToolSchema[];\n}"
211
+ "declaration": "export interface EpochHeader {\n config: LlmCallConfig;\n adapterDefaults?: LlmCallConfigAdapterDefaults;\n tools?: ToolSchema[];\n system?: never;\n}"
200
212
  },
201
213
  {
202
214
  "name": "FeedbackCategory",
@@ -290,6 +302,10 @@ export const TYPERT = {
290
302
  "name": "JsonValue",
291
303
  "declaration": "export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue; };"
292
304
  },
305
+ {
306
+ "name": "KnownCatalogMode",
307
+ "declaration": "export type KnownCatalogMode = { readonly mode: 'one-shot'; readonly label?: string; } | { readonly mode: 'continuable'; readonly label: string; };"
308
+ },
293
309
  {
294
310
  "name": "LlmCallConfig",
295
311
  "declaration": "export interface LlmCallConfig {\n provider: string;\n model: string;\n reasoningEffort?: ReasoningEffortId;\n temperature?: number;\n maxTokens?: number;\n stop?: string[];\n}"
@@ -304,7 +320,11 @@ export const TYPERT = {
304
320
  },
305
321
  {
306
322
  "name": "Message",
307
- "declaration": "export interface Message {\n readonly id: MessageId;\n readonly role: 'system' | 'user' | 'assistant';\n readonly content: ContentBlock[];\n readonly source: MessageSource;\n}"
323
+ "declaration": "export type Message = MessageRoleMap[keyof MessageRoleMap];"
324
+ },
325
+ {
326
+ "name": "MessageBase",
327
+ "declaration": "export interface MessageBase {\n readonly id: MessageId;\n readonly content: readonly ContentBlock[];\n readonly source: MessageSource;\n}"
308
328
  },
309
329
  {
310
330
  "name": "MessageFeedbackDelete",
@@ -330,13 +350,17 @@ export const TYPERT = {
330
350
  "name": "MessageId",
331
351
  "declaration": "export type MessageId = Branded<'MessageId'>;"
332
352
  },
353
+ {
354
+ "name": "MessageRoleMap",
355
+ "declaration": "export interface MessageRoleMap {\n system: SystemMessage;\n developer: DeveloperMessage;\n user: UserMessage;\n assistant: AssistantMessage;\n tool: ToolResultMessage;\n}"
356
+ },
333
357
  {
334
358
  "name": "MessageSource",
335
359
  "declaration": "export type MessageSource = MessageSourceMap[keyof MessageSourceMap];"
336
360
  },
337
361
  {
338
362
  "name": "MessageSourceMap",
339
- "declaration": "export interface MessageSourceMap {\n user: { kind: 'user'; };\n plugin: { kind: 'plugin'; plugin: string; } & ContextFormed;\n model: ModelMessageSource;\n tool: ToolMessageSource;\n 'user-rpc': { kind: 'user'; rpcId: SessionRequestId; clientTimeZone?: string; };\n 'agent-message': AgentMessageSource;\n 'subagent-settled': SubagentSettledMessageSource;\n 'skill-invocation': SkillInvocationSource;\n 'team-message': TeamMessageSource;\n goal: GoalMessageSource;\n 'session-reference': SessionReferenceSource;\n}"
363
+ "declaration": "export interface MessageSourceMap {\n user: { kind: 'user'; };\n model: ModelMessageSource;\n tool: ToolMessageSource;\n 'system-prompt': SystemPromptMessageSource;\n 'model-selection': { kind: 'model-selection'; } & ContextFormed;\n 'user-approval': { kind: 'user-approval'; } & ContextFormed;\n 'ptc-mode': { kind: 'ptc-mode'; };\n 'tool-registry': { kind: 'tool-registry'; };\n 'user-rpc': { kind: 'user'; rpcId: SessionRequestId; clientTimeZone?: string; };\n 'agent-message': AgentMessageSource;\n 'subagent-settled': SubagentSettledMessageSource;\n 'skill-invocation': SkillInvocationSource;\n 'cordis-host-runner': { kind: 'cordis-host-runner'; };\n 'team-message': TeamMessageSource;\n goal: GoalMessageSource;\n 'compact-checkpoint': CompactionCheckpointSource;\n 'session-reference': SessionReferenceSource;\n}"
340
364
  },
341
365
  {
342
366
  "name": "ModelMessageSource",
@@ -396,7 +420,7 @@ export const TYPERT = {
396
420
  },
397
421
  {
398
422
  "name": "Session",
399
- "declaration": "export class Session {\n get surface(): SessionSurface;\n readonly header: SessionHeader;\n readonly inheritedEventCount: SessionLogOffset;\n get id(): SessionId;\n readonly firstLiveSeq: SessionLogOffset;\n eventAt(seq: SessionSeq): SessionEvent | undefined;\n snapshotEvents(fromSeq: SessionLogOffset = SessionLogOffset(0), toSeqExclusive: SessionLogOffset = this.seq): readonly SessionEvent[];\n ownEvents(): readonly SessionEvent[];\n isOwnSeq(seq: SessionSeq): boolean;\n get seq(): SessionLogOffset;\n append<T extends SessionEventType>(type: T, data: SessionEventMap[T], ...opts: T extends SurfaceEventType ? [opts: SurfaceIntent<T>] : []): SessionEvent<T>;\n requestHeader(): EpochHeader | undefined;\n requestContext(): RequestContext | undefined;\n deriveMessages(): Message[];\n deriveEventMessage(event: SessionEvent): Message | null;\n}"
423
+ "declaration": "export class Session {\n get surface(): SessionSurface;\n readonly header: SessionHeader;\n readonly inheritedEventCount: SessionLogOffset;\n get id(): SessionId;\n readonly firstLiveSeq: SessionLogOffset;\n readonly firstLifecycleSeq: SessionLogOffset;\n eventAt(seq: SessionSeq): SessionEvent | undefined;\n snapshotEvents(fromSeq: SessionLogOffset = SessionLogOffset(0), toSeqExclusive: SessionLogOffset = this.seq): readonly SessionEvent[];\n ownEvents(): readonly SessionEvent[];\n isOwnSeq(seq: SessionSeq): boolean;\n get seq(): SessionLogOffset;\n append<T extends SessionEventType>(type: T, data: SessionEventMap[T], ...opts: T extends SurfaceEventType ? [opts: SurfaceIntent<T>] : []): SessionEvent<T>;\n requestHeader(): EpochHeader | undefined;\n requestContext(): RequestContext | undefined;\n deriveMessages(): Message[];\n deriveEventMessage(event: SessionEvent): Message | null;\n}"
400
424
  },
401
425
  {
402
426
  "name": "SessionEvent",
@@ -404,7 +428,7 @@ export const TYPERT = {
404
428
  },
405
429
  {
406
430
  "name": "SessionEventMap",
407
- "declaration": "export interface SessionEventMap {\n 'turn/start': { turn: number; };\n 'turn/end': { turn: number; reason: TurnEndReason; };\n 'step/start': { turn: number; step: number; };\n 'step/end': { turn: number; step: number; };\n 'user/message': UserMessage;\n 'system/message': { turn: number; step: number; message: SystemMessage; };\n 'assistant/message': { turn: number; step: number; message: AssistantMessage; stream: AssistantStreamRecord[]; usage?: TokenUsage; interrupted?: true; };\n 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[]; };\n 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string; };\n 'tool/result': { turn: number; step: number; message: ToolResultMessage; error?: { name: string; code: string; reason?: string; }; meta?: JsonValue; };\n 'request/header': { header: EpochHeader; reason: RequestHeaderReason; startsSeries?: true; };\n 'request/context': RequestContext;\n 'session/end-seed': { inherited?: true; };\n 'agent/inbox/spliced': { target: InboxTarget; start: number; removedCount?: number; inserted: UserMessage[]; outcome?: 'canceled'; };\n 'sandbox/mode': { mode: SandboxMode; source?: 'delegation'; };\n 'approval/asked': { id: ApprovalRequestId; toolName: string; callId?: ToolCallId; reason?: string; };\n 'approval/decided': { id: ApprovalRequestId; outcome: ApprovalOutcome; };\n 'approval/policy': { policy: ApprovalPolicy; source?: 'delegation'; };\n 'tool/ptc-dispatch-start': PtcDispatchStartEventData;\n 'tool/ptc-dispatch': PtcDispatchEventData;\n 'agent-preset/selected': { agentPreset: string; };\n 'command/run': { commandId: CommandId; name: string; args?: string; source: CommandSource; };\n 'command/done': { commandId: CommandId; kind: 'success' | 'error'; text?: string; sourceEventSeq?: import('@deepseek-ai/dsh-session/types').SessionSeq; };\n 'image/offload': { targets: ImageOffloadTarget[]; };\n 'session/title': SessionTitleEventData;\n 'todo/write': { todos: TodoItem[]; };\n 'model/selection': ModelSelection;\n 'subagent/descriptor': SubagentDescriptorData;\n 'permission/preset': { preset: string; };\n 'subagent/catalog': SubagentCatalogEvent;\n 'feedback/record': FeedbackRecord;\n 'team/member': { version: 2; teamId: TeamId; member: TeamMemberSnapshot; };\n 'team/task': { version: 2; teamId: TeamId; task: TeamTaskSnapshot; };\n 'team/message/queued': { version: 2; teamId: TeamId; message: TeamMessageSnapshot; };\n 'team/message/delivered': { version: 2; teamId: TeamId; messageId: TeamMessageId; targetId: SessionId; };\n 'goal/change': GoalChangeMeta;\n 'feedback/message-put': MessageFeedbackPut;\n 'feedback/message-delete': MessageFeedbackDelete;\n 'compaction/start': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; };\n 'compaction/summary': { compactionId: CompactionId; sourceCommandId?: CommandId; summary: ContentBlock[]; shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; provider: string; model: string; maxTokens?: number; usage?: TokenUsage; } & ({ rawOutput: ContentBlock[]; llmStreamCall: true; } | { rawOutput?: ContentBlock[]; llmStreamCall?: never; });\n 'compaction/end': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; error?: string; };\n 'compaction/prune': { shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; };\n}"
431
+ "declaration": "export interface SessionEventMap {\n 'turn/start': { turn: number; };\n 'turn/end': { turn: number; reason: TurnEndReason; };\n 'step/start': { turn: number; step: number; };\n 'step/end': { turn: number; step: number; };\n 'user/message': UserMessage;\n 'developer/message': { turn: number; step: number; message: DeveloperMessage; headerSeq?: SessionSeq; };\n 'system/message': { turn: number; step: number; message: SystemMessage; };\n 'assistant/message': { turn: number; step: number; message: AssistantMessage; stream: AssistantStreamRecord[]; usage?: TokenUsage; interrupted?: true; };\n 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[]; };\n 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string; };\n 'tool/result': { turn: number; step: number; message: ToolResultMessage; error?: { name: string; code: string; reason?: string; }; meta?: JsonValue; };\n 'request/header': { header: EpochHeader; reason: RequestHeaderReason; startsSeries?: true; };\n 'request/context': RequestContext;\n 'session/end-seed': { inherited?: true; };\n 'agent/inbox/spliced': { target: InboxTarget; start: number; removedCount?: number; inserted: UserMessage[]; outcome?: 'canceled'; };\n 'sandbox/mode': { mode: SandboxMode; source?: 'delegation'; };\n 'approval/asked': { id: ApprovalRequestId; toolName: string; callId?: ToolCallId; reason?: string; };\n 'approval/decided': { id: ApprovalRequestId; outcome: ApprovalOutcome; };\n 'approval/policy': { policy: ApprovalPolicy; source?: 'delegation'; };\n 'tool/ptc-dispatch-start': PtcDispatchStartEventData;\n 'tool/ptc-dispatch': PtcDispatchEventData;\n 'agent-preset/selected': { agentPreset: string; };\n 'command/run': { commandId: CommandId; name: string; args?: string; source: CommandSource; };\n 'command/done': { commandId: CommandId; kind: 'success' | 'error'; text?: string; sourceEventSeq?: import('@deepseek-ai/dsh-session/types').SessionSeq; };\n 'image/offload': { targets: ImageOffloadTarget[]; };\n 'session/title': SessionTitleEventData;\n 'todo/write': { todos: TodoItem[]; };\n 'model/selection': ModelSelection;\n 'subagent/descriptor': SubagentDescriptorData;\n 'permission/preset': { preset: string; };\n 'subagent/catalog': SubagentCatalogEvent;\n 'feedback/record': FeedbackRecord;\n 'team/member': { version: 2; teamId: TeamId; member: TeamMemberSnapshot; };\n 'team/task': { version: 2; teamId: TeamId; task: TeamTaskSnapshot; };\n 'team/message/queued': { version: 2; teamId: TeamId; message: TeamMessageSnapshot; };\n 'team/message/delivered': { version: 2; teamId: TeamId; messageId: TeamMessageId; targetId: SessionId; };\n 'goal/change': GoalChangeMeta;\n 'feedback/message-put': MessageFeedbackPut;\n 'feedback/message-delete': MessageFeedbackDelete;\n 'compaction/start': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; };\n 'compaction/summary': { compactionId: CompactionId; sourceCommandId?: CommandId; summary: ContentBlock[]; shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; provider: string; model: string; maxTokens?: number; usage?: TokenUsage; } & ({ rawOutput: ContentBlock[]; llmStreamCall: true; } | { rawOutput?: ContentBlock[]; llmStreamCall?: never; });\n 'compaction/end': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; error?: string; };\n 'compaction/prune': { shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; };\n}"
408
432
  },
409
433
  {
410
434
  "name": "SessionEventType",
@@ -424,7 +448,7 @@ export const TYPERT = {
424
448
  },
425
449
  {
426
450
  "name": "SessionReferenceCandidate",
427
- "declaration": "export interface SessionReferenceCandidate {\n sessionId: SessionId;\n label: string;\n cwd?: string;\n sameWorkspace: boolean;\n createdAt: number;\n}"
451
+ "declaration": "export interface SessionReferenceCandidate {\n sessionId: SessionId;\n label: string;\n displayTitle?: string;\n cwd?: string;\n sameWorkspace: boolean;\n createdAt: number;\n}"
428
452
  },
429
453
  {
430
454
  "name": "SessionReferenceInput",
@@ -476,7 +500,7 @@ export const TYPERT = {
476
500
  },
477
501
  {
478
502
  "name": "SubagentCatalogEvent",
479
- "declaration": "export type SubagentCatalogEvent = { readonly version: 0; readonly childId: SessionId; readonly childCreatedAt: number; } & ({ readonly mode: 'one-shot'; readonly label?: string; } | { readonly mode: 'continuable'; readonly label: string; });"
503
+ "declaration": "export type SubagentCatalogEvent = { readonly childId: SessionId; readonly childCreatedAt: number; } & (({ readonly version: 0; } & KnownCatalogMode) | ({ readonly version: 1; } & (KnownCatalogMode | { readonly mode: 'unknown'; readonly label?: string; })));"
480
504
  },
481
505
  {
482
506
  "name": "SubagentDescriptorBase",
@@ -492,7 +516,7 @@ export const TYPERT = {
492
516
  },
493
517
  {
494
518
  "name": "SurfaceEventType",
495
- "declaration": "export type SurfaceEventType = 'system/message' | 'user/message' | 'assistant/message' | 'tool/result';"
519
+ "declaration": "export type SurfaceEventType = 'system/message' | 'developer/message' | 'user/message' | 'assistant/message' | 'tool/result';"
496
520
  },
497
521
  {
498
522
  "name": "SurfaceIntent",
@@ -504,7 +528,11 @@ export const TYPERT = {
504
528
  },
505
529
  {
506
530
  "name": "SystemMessage",
507
- "declaration": "export interface SystemMessage extends Message {\n readonly role: 'system';\n readonly source: MessageSourceMap['plugin'];\n}"
531
+ "declaration": "export interface SystemMessage extends MessageBase {\n readonly role: 'system';\n readonly source: MessageSourceMap['system-prompt'];\n}"
532
+ },
533
+ {
534
+ "name": "SystemPromptMessageSource",
535
+ "declaration": "export interface SystemPromptMessageSource {\n kind: 'system-prompt';\n}"
508
536
  },
509
537
  {
510
538
  "name": "SystemPromptUpdate",
@@ -558,6 +586,10 @@ export const TYPERT = {
558
586
  "name": "TokenUsage",
559
587
  "declaration": "export interface TokenUsage {\n inputTokens: number;\n outputTokens: number;\n totalTokens?: number;\n cacheReadTokens?: number;\n cacheWriteTokens?: number;\n reasoningTokens?: number;\n}"
560
588
  },
589
+ {
590
+ "name": "ToolAdditionBlock",
591
+ "declaration": "export interface ToolAdditionBlock {\n type: 'tool-addition';\n toolName: string;\n tool?: never;\n}"
592
+ },
561
593
  {
562
594
  "name": "ToolCallBlock",
563
595
  "declaration": "export interface ToolCallBlock {\n type: 'tool-call';\n id: ToolCallId;\n name: string;\n arguments: string;\n}"
@@ -571,20 +603,20 @@ export const TYPERT = {
571
603
  "declaration": "export interface ToolMessageSource {\n kind: 'tool';\n callId: ToolCallId;\n}"
572
604
  },
573
605
  {
574
- "name": "ToolRestriction",
575
- "declaration": "export interface ToolRestriction {\n readonly allow?: readonly string[];\n readonly deny?: readonly string[];\n}"
606
+ "name": "ToolRemovalBlock",
607
+ "declaration": "export interface ToolRemovalBlock {\n type: 'tool-removal';\n toolName: string;\n}"
576
608
  },
577
609
  {
578
- "name": "ToolResultBlock",
579
- "declaration": "export interface ToolResultBlock {\n type: 'tool-result';\n toolCallId: ToolCallId;\n content: ContentBlock[];\n isError?: boolean;\n}"
610
+ "name": "ToolRestriction",
611
+ "declaration": "export interface ToolRestriction {\n readonly allow?: readonly string[];\n readonly deny?: readonly string[];\n}"
580
612
  },
581
613
  {
582
614
  "name": "ToolResultMessage",
583
- "declaration": "export interface ToolResultMessage extends Message {\n readonly role: 'user';\n readonly content: [ToolResultBlock];\n readonly source: ToolMessageSource;\n}"
615
+ "declaration": "export interface ToolResultMessage extends MessageBase {\n readonly role: 'tool';\n readonly source: ToolMessageSource;\n readonly toolCallId: ToolCallId;\n readonly isError?: boolean;\n}"
584
616
  },
585
617
  {
586
618
  "name": "ToolSchema",
587
- "declaration": "export interface ToolSchema {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n}"
619
+ "declaration": "export interface ToolSchema {\n deferLoading?: true;\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n}"
588
620
  },
589
621
  {
590
622
  "name": "TurnEndCancelCause",
@@ -596,11 +628,11 @@ export const TYPERT = {
596
628
  },
597
629
  {
598
630
  "name": "TurnEndReasonMap",
599
- "declaration": "export interface TurnEndReasonMap {\n completed: { kind: 'completed'; };\n aborted: { kind: 'aborted'; reason: TurnEndCancelCause; };\n blocked: { kind: 'blocked'; };\n error: { kind: 'error'; error: LlmFailure; };\n 'max-tokens': { kind: 'max-tokens'; };\n interrupted: { kind: 'interrupted'; };\n}"
631
+ "declaration": "export interface TurnEndReasonMap {\n completed: { kind: 'completed'; };\n aborted: { kind: 'aborted'; reason: TurnEndCancelCause; };\n blocked: { kind: 'blocked'; };\n error: { kind: 'error'; error: LlmFailure; };\n 'max-tokens': { kind: 'max-tokens'; };\n interrupted: { kind: 'interrupted'; };\n forked: { kind: 'forked'; };\n}"
600
632
  },
601
633
  {
602
634
  "name": "UserMessage",
603
- "declaration": "export interface UserMessage extends Message {\n readonly role: 'user';\n}"
635
+ "declaration": "export interface UserMessage extends MessageBase {\n readonly role: 'user';\n}"
604
636
  }
605
637
  ]
606
638
  }
@@ -1,16 +1,20 @@
1
1
  /* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
2
2
  import { z } from 'zod'
3
3
 
4
- const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema = z.intersection(z.string(), z.unknown())
5
- const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema = z.string()
6
- const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema = z.array(z.object({
4
+ let _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema$value
5
+ const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema = () => (_deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema$value ??= z.intersection(z.string(), z.unknown()))
6
+ let _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema$value
7
+ const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema = () => (_deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema$value ??= z.string())
8
+ let _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema$value
9
+ const _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema = () => (_deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema$value ??= z.array(z.object({
7
10
  'mention': z.string(),
8
11
  'sessionId': z.intersection(z.string(), z.unknown()),
9
12
  'label': z.string(),
13
+ 'displayTitle': z.string().optional(),
10
14
  'cwd': z.string().optional(),
11
15
  'sameWorkspace': z.boolean(),
12
16
  'createdAt': z.number(),
13
- }))
17
+ })))
14
18
 
15
19
  export const TYPERT_REMOTE = {
16
20
  package: '@deepseek-ai/dsh-session-reference',
@@ -35,7 +39,7 @@ export const TYPERT_REMOTE = {
35
39
  codec: {
36
40
  mode: 'strict',
37
41
  typeSymbol: '@deepseek-ai/dsh-session/types#SessionId',
38
- schema: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema,
42
+ create: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_0$schema,
39
43
  },
40
44
  },
41
45
  {
@@ -45,7 +49,7 @@ export const TYPERT_REMOTE = {
45
49
  codec: {
46
50
  mode: 'strict',
47
51
  typeSymbol: '@deepseek-ai/dsh-session-reference#sessionReferenceResolver/candidates:query',
48
- schema: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema,
52
+ create: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_parameter_1$schema,
49
53
  },
50
54
  },
51
55
  ],
@@ -53,9 +57,9 @@ export const TYPERT_REMOTE = {
53
57
  result: {
54
58
  mode: 'strict',
55
59
  typeSymbol: '@deepseek-ai/dsh-session-reference#sessionReferenceResolver/candidates:result',
56
- schema: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema,
60
+ create: _deepseek_ai_dsh_session_reference_sessionReferenceResolver_candidates_result$schema,
57
61
  },
58
- sourceLocation: {"file":"packages/context/session-reference/src/index.ts","line":274,"column":9},
62
+ sourceLocation: {"file":"packages/context/session-reference/src/index.ts","line":272,"column":9},
59
63
  },
60
64
  ],
61
65
  }
@@ -39,18 +39,17 @@ export declare class SessionReferenceResolver extends TypertRemoteService {
39
39
  /**
40
40
  * List reference candidates, ranked by working-directory affinity.
41
41
  *
42
- * Discovery runs at keystroke rate, so a title only ever comes from a
43
- * projection read: see {@link SessionReferenceResolver.projectedTitle} for
44
- * which sessions can answer one and which fall back to their id.
42
+ * Discovery runs at keystroke rate, so titles and subagent labels only ever
43
+ * come from projection reads; sessions without either fall back to their id.
45
44
  * @param agent - target agent; self is excluded and its cwd drives ranking.
46
- * @param query - optional case-insensitive session-id/cwd/title substring.
45
+ * @param query - optional case-insensitive session-id/cwd/title/display-title substring.
47
46
  * @param limit - optional positive result cap.
48
47
  * @param signal - optional cancellation boundary for host autocomplete teardown.
49
- * @returns candidates labeled by latest title or, when absent, session id.
48
+ * @returns candidates with canonical mention labels and presentation titles.
50
49
  */
51
50
  listCandidates(agent: Agent, query?: string, limit?: number, signal?: AbortSignal): Promise<SessionReferenceCandidate[]>;
52
51
  /**
53
- * The title a session's projections can answer without reading its log.
52
+ * The mention label and display title a Session's projections can answer without reading its log.
54
53
  *
55
54
  * Attachment is decided by the store at read time, not by the listing:
56
55
  * a session that attached in between would otherwise be answered from a
@@ -65,13 +64,12 @@ export declare class SessionReferenceResolver extends TypertRemoteService {
65
64
  * Nothing else is attempted. Folding a title from a log costs the whole
66
65
  * log, and this call sits under every keystroke of `@` completion. A
67
66
  * session that no projection can answer for — one persisted before the
68
- * cache was composed, or seeded straight to disk — is labeled by its id
69
- * and cannot be found by its title until it is opened once, which
70
- * checkpoints it.
67
+ * cache was composed — is labeled by its id and cannot be found by its
68
+ * title until it is opened once, which checkpoints it.
71
69
  * @param record - the listed session, live or cold.
72
- * @returns the projected title, or undefined when no projection holds one.
70
+ * @returns the title-backed mention label and the subagent-label-first display title.
73
71
  */
74
- private projectedTitle;
72
+ private projectedLabels;
75
73
  /**
76
74
  * Remote face of {@link listCandidates}: the configured candidate limit
77
75
  * applies, and every candidate carries the canonical mention a host inserts
@@ -41,7 +41,6 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
41
41
  import z from '@deepseek-ai/schemastery';
42
42
  import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
43
43
  import { createUserMessage, freezeMessage, LlmError } from '@deepseek-ai/dsh-llm';
44
- import { SessionLogOffset } from '@deepseek-ai/dsh-session';
45
44
  import { prepareReferenceOmission, REFERENCE_WARNING } from "./spill.js";
46
45
  import { DEFAULT_CANDIDATE_LIMIT, DEFAULT_MAX_REFERENCE_BYTES, MAX_REFERENCES, SessionReferenceError, } from "./config.js";
47
46
  import { retainReferencedSession } from "./projection.js";
@@ -153,14 +152,13 @@ let SessionReferenceResolver = (() => {
153
152
  /**
154
153
  * List reference candidates, ranked by working-directory affinity.
155
154
  *
156
- * Discovery runs at keystroke rate, so a title only ever comes from a
157
- * projection read: see {@link SessionReferenceResolver.projectedTitle} for
158
- * which sessions can answer one and which fall back to their id.
155
+ * Discovery runs at keystroke rate, so titles and subagent labels only ever
156
+ * come from projection reads; sessions without either fall back to their id.
159
157
  * @param agent - target agent; self is excluded and its cwd drives ranking.
160
- * @param query - optional case-insensitive session-id/cwd/title substring.
158
+ * @param query - optional case-insensitive session-id/cwd/title/display-title substring.
161
159
  * @param limit - optional positive result cap.
162
160
  * @param signal - optional cancellation boundary for host autocomplete teardown.
163
- * @returns candidates labeled by latest title or, when absent, session id.
161
+ * @returns candidates with canonical mention labels and presentation titles.
164
162
  */
165
163
  async listCandidates(agent, query = '', limit = this.config.candidateLimit, signal) {
166
164
  if (!Number.isSafeInteger(limit) || limit <= 0) {
@@ -172,30 +170,28 @@ let SessionReferenceResolver = (() => {
172
170
  const records = (await settleWithCancellation(this.ctx.sessionQuery.listSessions(signal), signal))
173
171
  .filter(record => record.header.id !== agent.id)
174
172
  .map((record, index) => ({ record, index }));
175
- const labelled = records.map(({ record, index }) => ({
176
- record,
177
- index,
178
- label: this.projectedTitle(record) ?? record.header.id,
179
- }));
180
- return labelled.filter(({ record, label }) => {
173
+ const labelled = records.map(({ record, index }) => ({ record, index, ...this.projectedLabels(record) }));
174
+ return labelled.filter(({ record, label, displayTitle }) => {
181
175
  if (needle === '')
182
176
  return true;
183
177
  return record.header.id.toLocaleLowerCase().includes(needle)
184
178
  || record.header.cwd?.toLocaleLowerCase().includes(needle) === true
185
- || label.toLocaleLowerCase().includes(needle);
179
+ || label.toLocaleLowerCase().includes(needle)
180
+ || displayTitle.toLocaleLowerCase().includes(needle);
186
181
  }).sort((a, b) => candidateRank(a.record.header.cwd, targetCwd) - candidateRank(b.record.header.cwd, targetCwd)
187
182
  || a.index - b.index)
188
183
  .slice(0, limit)
189
- .map(({ record, label }) => ({
184
+ .map(({ record, label, displayTitle }) => ({
190
185
  sessionId: record.header.id,
191
186
  label,
187
+ displayTitle,
192
188
  ...record.header.cwd === undefined ? {} : { cwd: record.header.cwd },
193
189
  sameWorkspace: record.header.cwd !== undefined && record.header.cwd === targetCwd,
194
190
  createdAt: record.header.createdAt,
195
191
  }));
196
192
  }
197
193
  /**
198
- * The title a session's projections can answer without reading its log.
194
+ * The mention label and display title a Session's projections can answer without reading its log.
199
195
  *
200
196
  * Attachment is decided by the store at read time, not by the listing:
201
197
  * a session that attached in between would otherwise be answered from a
@@ -210,21 +206,25 @@ let SessionReferenceResolver = (() => {
210
206
  * Nothing else is attempted. Folding a title from a log costs the whole
211
207
  * log, and this call sits under every keystroke of `@` completion. A
212
208
  * session that no projection can answer for — one persisted before the
213
- * cache was composed, or seeded straight to disk — is labeled by its id
214
- * and cannot be found by its title until it is opened once, which
215
- * checkpoints it.
209
+ * cache was composed — is labeled by its id and cannot be found by its
210
+ * title until it is opened once, which checkpoints it.
216
211
  * @param record - the listed session, live or cold.
217
- * @returns the projected title, or undefined when no projection holds one.
212
+ * @returns the title-backed mention label and the subagent-label-first display title.
218
213
  */
219
- projectedTitle(record) {
214
+ projectedLabels(record) {
220
215
  const attached = this.ctx.get('sessions')?.get(record.header.id);
221
216
  const projections = this.ctx.get('sessionProjections');
222
- if (attached !== undefined && projections !== undefined) {
223
- return titleOf(projections.snapshot(attached, ['title']));
224
- }
225
- if (record.header.isSeeded)
226
- return undefined;
227
- return titleOf(this.ctx.get('sessionProjectionCache')?.cachedSnapshot(record.header, SessionLogOffset(0), ['title']));
217
+ const snapshot = attached !== undefined && projections !== undefined
218
+ ? projections.snapshot(attached, ['title', 'subagent'])
219
+ : this.ctx.get('sessionProjectionCache')?.cachedSnapshot(record.header, ['title', 'subagent']);
220
+ const label = titleOf(snapshot) ?? record.header.id;
221
+ const subagent = snapshot?.values.subagent;
222
+ return {
223
+ label,
224
+ displayTitle: subagent === undefined || subagent === null
225
+ ? label
226
+ : subagent.label ?? label,
227
+ };
228
228
  }
229
229
  /**
230
230
  * Remote face of {@link listCandidates}: the configured candidate limit
@@ -239,7 +239,10 @@ let SessionReferenceResolver = (() => {
239
239
  const candidates = await this.listCandidates(agent, query, this.config.candidateLimit, signal);
240
240
  return candidates.map(candidate => ({
241
241
  ...candidate,
242
- mention: formatSessionReferenceMention({ sessionId: candidate.sessionId, label: candidate.label }),
242
+ mention: formatSessionReferenceMention({
243
+ sessionId: candidate.sessionId,
244
+ label: candidate.displayTitle ?? candidate.label,
245
+ }),
243
246
  }));
244
247
  }
245
248
  /**
@@ -24,6 +24,7 @@ function projectSessionConversation(snapshot) {
24
24
  conversation.push({ role: 'assistant', text, checkpoint: false, originalText: text, omittedBytes: 0 });
25
25
  break;
26
26
  }
27
+ case 'developer/message':
27
28
  case 'system/message':
28
29
  case 'tool/result':
29
30
  break;
@@ -46,6 +46,8 @@ export interface SessionReferenceCandidate {
46
46
  sessionId: SessionId;
47
47
  /** Latest log-backed title, falling back to the opaque session id. */
48
48
  label: string;
49
+ /** Display and canonical-mention text, preferring a subagent's durable creation label over {@link label}. */
50
+ displayTitle?: string;
49
51
  /** Source session working directory, when recorded. */
50
52
  cwd?: string;
51
53
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-session-reference",
3
3
  "description": "Cross-session snapshot references and durable untrusted model context (ctx.sessionReferenceResolver)",
4
- "version": "0.1.6-alpha.1",
4
+ "version": "0.1.7-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -45,24 +45,25 @@
45
45
  "license": "MIT",
46
46
  "dependencies": {
47
47
  "zod": "^4.4.3",
48
- "@deepseek-ai/dsh-util-values": "^0.1.6-alpha.1",
49
- "@deepseek-ai/schemastery": "^3.18.2",
50
- "@deepseek-ai/dsh-brand": "^0.1.6-alpha.1"
48
+ "@deepseek-ai/dsh-brand": "^0.1.7-alpha.1",
49
+ "@deepseek-ai/dsh-util-values": "^0.1.7-alpha.1",
50
+ "@deepseek-ai/schemastery": "^3.18.3"
51
51
  },
52
52
  "peerDependencies": {
53
- "@deepseek-ai/cordis": "^4.0.2",
54
- "@deepseek-ai/dsh-agent": "^0.1.6-alpha.1",
55
- "@deepseek-ai/dsh-llm": "^0.1.6-alpha.1",
56
- "@deepseek-ai/dsh-output-retention": "^0.1.6-alpha.1",
57
- "@deepseek-ai/dsh-session": "^0.1.6-alpha.1",
58
- "@deepseek-ai/dsh-session-projection": "^0.1.6-alpha.1",
59
- "@deepseek-ai/dsh-compaction": "^0.1.6-alpha.1",
60
- "@deepseek-ai/dsh-session-projection-cache": "^0.1.6-alpha.1",
61
- "@deepseek-ai/dsh-session-query": "^0.1.6-alpha.1",
62
- "@deepseek-ai/dsh-session-title": "^0.1.6-alpha.1",
63
- "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.1",
64
- "@deepseek-ai/dsh-spill": "^0.1.6-alpha.1",
65
- "@deepseek-ai/dsh-system-prompt": "^0.1.6-alpha.1"
53
+ "@deepseek-ai/dsh-agent": "^0.1.7-alpha.1",
54
+ "@deepseek-ai/dsh-compaction": "^0.1.7-alpha.1",
55
+ "@deepseek-ai/cordis": "^4.0.3",
56
+ "@deepseek-ai/dsh-llm": "^0.1.7-alpha.1",
57
+ "@deepseek-ai/dsh-output-retention": "^0.1.7-alpha.1",
58
+ "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
59
+ "@deepseek-ai/dsh-session-query": "^0.1.7-alpha.1",
60
+ "@deepseek-ai/dsh-session-title": "^0.1.7-alpha.1",
61
+ "@deepseek-ai/dsh-system-prompt": "^0.1.7-alpha.1",
62
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.7-alpha.1",
63
+ "@deepseek-ai/dsh-session-projection-cache": "^0.1.7-alpha.1",
64
+ "@deepseek-ai/dsh-spill": "^0.1.7-alpha.1",
65
+ "@deepseek-ai/dsh-session-projection": "^0.1.7-alpha.1",
66
+ "@deepseek-ai/dsh-subagent": "^0.1.7-alpha.1"
66
67
  },
67
68
  "peerDependenciesMeta": {
68
69
  "@deepseek-ai/dsh-spill": {
@@ -73,25 +74,26 @@
73
74
  }
74
75
  },
75
76
  "devDependencies": {
76
- "@deepseek-ai/cordis": "^4.0.2",
77
- "@deepseek-ai/cordis-plugin-include": "^1.0.7",
78
- "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
79
- "@deepseek-ai/dsh-compaction": "^0.1.6-alpha.1",
80
- "@deepseek-ai/dsh-agent": "^0.1.6-alpha.1",
81
- "@deepseek-ai/dsh-llm": "^0.1.6-alpha.1",
82
- "@deepseek-ai/dsh-output-retention": "^0.1.6-alpha.1",
83
- "@deepseek-ai/dsh-session": "^0.1.6-alpha.1",
84
- "@deepseek-ai/dsh-fs-local": "^0.1.6-alpha.1",
85
- "@deepseek-ai/dsh-session-projection": "^0.1.6-alpha.1",
86
- "@deepseek-ai/dsh-session-projection-cache": "^0.1.6-alpha.1",
87
- "@deepseek-ai/dsh-session-query": "^0.1.6-alpha.1",
88
- "@deepseek-ai/dsh-session-query-sqlite": "^0.1.6-alpha.1",
89
- "@deepseek-ai/dsh-session-title": "^0.1.6-alpha.1",
90
- "@deepseek-ai/dsh-spill": "^0.1.6-alpha.1",
91
- "@deepseek-ai/dsh-spill-local": "^0.1.6-alpha.1",
92
- "@deepseek-ai/dsh-system-prompt": "^0.1.6-alpha.1",
93
- "@deepseek-ai/dsh-tool-fs": "^0.1.6-alpha.1",
94
- "@deepseek-ai/dsh-tools": "^0.1.6-alpha.1",
95
- "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.1"
77
+ "@deepseek-ai/cordis": "^4.0.3",
78
+ "@deepseek-ai/cordis-plugin-include": "^1.0.8",
79
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.4",
80
+ "@deepseek-ai/dsh-compaction": "^0.1.7-alpha.1",
81
+ "@deepseek-ai/dsh-fs-local": "^0.1.7-alpha.1",
82
+ "@deepseek-ai/dsh-llm": "^0.1.7-alpha.1",
83
+ "@deepseek-ai/dsh-agent": "^0.1.7-alpha.1",
84
+ "@deepseek-ai/dsh-output-retention": "^0.1.7-alpha.1",
85
+ "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
86
+ "@deepseek-ai/dsh-session-projection-cache": "^0.1.7-alpha.1",
87
+ "@deepseek-ai/dsh-session-projection": "^0.1.7-alpha.1",
88
+ "@deepseek-ai/dsh-session-query": "^0.1.7-alpha.1",
89
+ "@deepseek-ai/dsh-session-query-sqlite": "^0.1.7-alpha.1",
90
+ "@deepseek-ai/dsh-session-title": "^0.1.7-alpha.1",
91
+ "@deepseek-ai/dsh-spill": "^0.1.7-alpha.1",
92
+ "@deepseek-ai/dsh-subagent": "^0.1.7-alpha.1",
93
+ "@deepseek-ai/dsh-spill-local": "^0.1.7-alpha.1",
94
+ "@deepseek-ai/dsh-system-prompt": "^0.1.7-alpha.1",
95
+ "@deepseek-ai/dsh-tool-fs": "^0.1.7-alpha.1",
96
+ "@deepseek-ai/dsh-tools": "^0.1.7-alpha.1",
97
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.7-alpha.1"
96
98
  }
97
99
  }