dsh-plugin-sessiongraph-service 0.1.21 → 0.3.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 CHANGED
@@ -19,10 +19,11 @@ import type { SessiongraphIndexer, SessiongraphIndexOptions, SessiongraphIndexRe
19
19
  export { assertNever } from './assert.ts';
20
20
  export { SessiongraphIndexerId, SessiongraphSessionId, SessiongraphStoreId } from './brand.ts';
21
21
  export { ENTRY_FTS_TABLE, ENTRY_TABLE, INDEX_RELATIVE_PATH, META_DISCOVERED, META_FAILED, META_INDEXED_AT, META_SCHEMA_VERSION, SCHEMA_STATEMENTS, SCHEMA_VERSION, SESSION_TABLE, } from './schema.ts';
22
+ export { PANEL_CODEX_SYNC_KEY, PANEL_FILE_NAME } from './panel.ts';
22
23
  export { highlightTerms, isCjk, matchExpression, tokenize } from './tokenize.ts';
23
24
  export { SessiongraphError } from './types.ts';
24
- export type { SessiongraphEntry, SessiongraphEntryKind, SessiongraphErrorCode, SessiongraphHit, SessiongraphIndexer, SessiongraphIndexOptions, SessiongraphIndexReport, SessiongraphOperation, SessiongraphRequest, SessiongraphRequestBase, SessiongraphResult, SessiongraphResultFor, SessiongraphSearchRequest, SessiongraphSearchResult, SessiongraphService, SessiongraphSessionRequest, SessiongraphSessionResult, SessiongraphSessionSummary, SessiongraphStatusRequest, SessiongraphStatusResult, SessiongraphStoreProvider, SessiongraphTimelineRequest, SessiongraphTimelineResult, SessiongraphWorkspaceStat, SessiongraphWorkspacesRequest, SessiongraphWorkspacesResult, } from './types.ts';
25
- export { SESSIONGRAPH_ENTRY_KINDS, SESSIONGRAPH_OPERATIONS } from './types.ts';
25
+ export type { SessiongraphEntry, SessiongraphEntryKind, SessiongraphErrorCode, SessiongraphEventRequest, SessiongraphEventResult, SessiongraphEventTraceResult, SessiongraphHit, SessiongraphIndexer, SessiongraphIndexOptions, SessiongraphIndexReport, SessiongraphOperation, SessiongraphRawEvent, SessiongraphRawEventSummary, SessiongraphRequest, SessiongraphRequestBase, SessiongraphResult, SessiongraphResultFor, SessiongraphSearchRequest, SessiongraphSearchResult, SessiongraphService, SessiongraphSessionRequest, SessiongraphSessionResult, SessiongraphSessionSummary, SessiongraphSessionTraceResult, SessiongraphSource, SessiongraphStatusRequest, SessiongraphStatusResult, SessiongraphStoreProvider, SessiongraphTimelineRequest, SessiongraphTimelineResult, SessiongraphTraceNode, SessiongraphTraceRequest, SessiongraphTraceResult, SessiongraphWorkspaceStat, SessiongraphWorkspacesRequest, SessiongraphWorkspacesResult, } from './types.ts';
26
+ export { SESSIONGRAPH_ENTRY_KINDS, SESSIONGRAPH_OPERATIONS, SESSIONGRAPH_SOURCES } from './types.ts';
26
27
  /**
27
28
  * `ctx.sessiongraph`. Holds the store and indexer reservations; selection reads them per call so a
28
29
  * provider that unloads mid-session stops serving without leaving a stale route behind.
package/lib/index.js CHANGED
@@ -21,9 +21,10 @@ import { SessiongraphError } from "./types.js";
21
21
  export { assertNever } from "./assert.js";
22
22
  export { SessiongraphIndexerId, SessiongraphSessionId, SessiongraphStoreId } from "./brand.js";
23
23
  export { ENTRY_FTS_TABLE, ENTRY_TABLE, INDEX_RELATIVE_PATH, META_DISCOVERED, META_FAILED, META_INDEXED_AT, META_SCHEMA_VERSION, SCHEMA_STATEMENTS, SCHEMA_VERSION, SESSION_TABLE, } from "./schema.js";
24
+ export { PANEL_CODEX_SYNC_KEY, PANEL_FILE_NAME } from "./panel.js";
24
25
  export { highlightTerms, isCjk, matchExpression, tokenize } from "./tokenize.js";
25
26
  export { SessiongraphError } from "./types.js";
26
- export { SESSIONGRAPH_ENTRY_KINDS, SESSIONGRAPH_OPERATIONS } from "./types.js";
27
+ export { SESSIONGRAPH_ENTRY_KINDS, SESSIONGRAPH_OPERATIONS, SESSIONGRAPH_SOURCES } from "./types.js";
27
28
  /**
28
29
  * `ctx.sessiongraph`. Holds the store and indexer reservations; selection reads them per call so a
29
30
  * provider that unloads mid-session stops serving without leaving a stale route behind.
package/lib/panel.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The one file the settings panel and the indexer both know about.
3
+ *
4
+ * `panel.json` lives inside an index root and holds choices a human made in the settings page. It is
5
+ * **not** an index artifact: nothing in it describes indexed data, it is rewritten whole on every
6
+ * save, and a missing or unreadable file means "use the configured value" rather than an error — a
7
+ * broken panel must never stop an index run.
8
+ *
9
+ * Two packages read it for different members, which is why the name lives here rather than in either
10
+ * of them: `tool` owns the HTTP surface and writes the document, while `sessions` reads
11
+ * `codexSync` on every run because that choice decides whether Codex history is part of the corpus.
12
+ * A second spelling of the filename would let the panel save a choice the indexer never sees.
13
+ * @module dsh-plugin-sessiongraph-service/panel
14
+ */
15
+ /** Name of the settings document inside an index root. */
16
+ export declare const PANEL_FILE_NAME = "panel.json";
17
+ /**
18
+ * The member of `panel.json` that decides whether Codex history is indexed.
19
+ *
20
+ * Absent means "whatever the plugin configuration says": a user who never opened the panel keeps the
21
+ * configured default, and only an explicit save stores a value. Deliberately a bare optional boolean
22
+ * rather than a tri-state — the configuration already owns "default", so the file only ever records
23
+ * a human's deviation from it.
24
+ */
25
+ export declare const PANEL_CODEX_SYNC_KEY = "codexSync";
26
+ //# sourceMappingURL=panel.d.ts.map
package/lib/panel.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The one file the settings panel and the indexer both know about.
3
+ *
4
+ * `panel.json` lives inside an index root and holds choices a human made in the settings page. It is
5
+ * **not** an index artifact: nothing in it describes indexed data, it is rewritten whole on every
6
+ * save, and a missing or unreadable file means "use the configured value" rather than an error — a
7
+ * broken panel must never stop an index run.
8
+ *
9
+ * Two packages read it for different members, which is why the name lives here rather than in either
10
+ * of them: `tool` owns the HTTP surface and writes the document, while `sessions` reads
11
+ * `codexSync` on every run because that choice decides whether Codex history is part of the corpus.
12
+ * A second spelling of the filename would let the panel save a choice the indexer never sees.
13
+ * @module dsh-plugin-sessiongraph-service/panel
14
+ */
15
+ /** Name of the settings document inside an index root. */
16
+ export const PANEL_FILE_NAME = 'panel.json';
17
+ /**
18
+ * The member of `panel.json` that decides whether Codex history is indexed.
19
+ *
20
+ * Absent means "whatever the plugin configuration says": a user who never opened the panel keeps the
21
+ * configured default, and only an explicit save stores a value. Deliberately a bare optional boolean
22
+ * rather than a tri-state — the configuration already owns "default", so the file only ever records
23
+ * a human's deviation from it.
24
+ */
25
+ export const PANEL_CODEX_SYNC_KEY = 'codexSync';
26
+ //# sourceMappingURL=panel.js.map
package/lib/schema.d.ts CHANGED
@@ -14,8 +14,12 @@
14
14
  * v2 stores the FTS index contentless (`content=''`): FTS5's default build keeps a copy of every
15
15
  * indexed column in a `_content` shadow table, which on a real corpus was 67MB of a 174MB file
16
16
  * holding a token stream that can always be recomputed from `entry.text`.
17
+ *
18
+ * v3 records which harness wrote a session (`session.source`). The first version indexed only DSH's
19
+ * own logs, so the column is absent rather than defaulted; a v3 row carries `'dsh'` or `'codex'` and
20
+ * a store that cannot name the origin refuses the file instead of labelling every hit "dsh".
17
21
  */
18
- export declare const SCHEMA_VERSION = 2;
22
+ export declare const SCHEMA_VERSION = 3;
19
23
  /** Relative path of the index file inside an index root. */
20
24
  export declare const INDEX_RELATIVE_PATH = "index.db";
21
25
  /** One session, keyed by its harness session id. */
package/lib/schema.js CHANGED
@@ -14,8 +14,12 @@
14
14
  * v2 stores the FTS index contentless (`content=''`): FTS5's default build keeps a copy of every
15
15
  * indexed column in a `_content` shadow table, which on a real corpus was 67MB of a 174MB file
16
16
  * holding a token stream that can always be recomputed from `entry.text`.
17
+ *
18
+ * v3 records which harness wrote a session (`session.source`). The first version indexed only DSH's
19
+ * own logs, so the column is absent rather than defaulted; a v3 row carries `'dsh'` or `'codex'` and
20
+ * a store that cannot name the origin refuses the file instead of labelling every hit "dsh".
17
21
  */
18
- export const SCHEMA_VERSION = 2;
22
+ export const SCHEMA_VERSION = 3;
19
23
  /** Relative path of the index file inside an index root. */
20
24
  export const INDEX_RELATIVE_PATH = 'index.db';
21
25
  /** One session, keyed by its harness session id. */
@@ -43,6 +47,7 @@ export const SCHEMA_STATEMENTS = [
43
47
  )`,
44
48
  `CREATE TABLE IF NOT EXISTS ${SESSION_TABLE} (
45
49
  id TEXT PRIMARY KEY,
50
+ source TEXT NOT NULL,
46
51
  workspace TEXT NOT NULL,
47
52
  title TEXT,
48
53
  created_at INTEGER NOT NULL,
package/lib/types.d.ts CHANGED
@@ -9,8 +9,16 @@
9
9
  * @module dsh-plugin-sessiongraph-service/types
10
10
  */
11
11
  import type { SessiongraphIndexerId, SessiongraphSessionId, SessiongraphStoreId } from './brand.ts';
12
- /** Every query operation the seam answers. */
13
- export declare const SESSIONGRAPH_OPERATIONS: readonly ["search", "session", "timeline", "workspaces", "status"];
12
+ /**
13
+ * Every query operation the seam answers.
14
+ *
15
+ * `event` and `trace` were added when the model-facing surface collapsed from two tools into one:
16
+ * the built-in session tools could read a single unabridged event and follow replacement/citation
17
+ * relationships, and a replacement that dropped those answers would be a downgrade rather than a
18
+ * swap. An index run is deliberately **not** here — it writes, so the tool routes it through
19
+ * `Sessiongraph.index()` instead of pretending a query provider can build an index.
20
+ */
21
+ export declare const SESSIONGRAPH_OPERATIONS: readonly ["search", "session", "event", "trace", "timeline", "workspaces", "status"];
14
22
  /** One query operation. */
15
23
  export type SessiongraphOperation = (typeof SESSIONGRAPH_OPERATIONS)[number];
16
24
  /**
@@ -23,6 +31,18 @@ export type SessiongraphOperation = (typeof SESSIONGRAPH_OPERATIONS)[number];
23
31
  export declare const SESSIONGRAPH_ENTRY_KINDS: readonly ["user", "assistant", "tool", "title"];
24
32
  /** One entry kind. */
25
33
  export type SessiongraphEntryKind = (typeof SESSIONGRAPH_ENTRY_KINDS)[number];
34
+ /**
35
+ * Which agent harness wrote a session log.
36
+ *
37
+ * `dsh` is this host's own store under `~/.dsh/sessions`; `codex` is OpenAI Codex's rollout log
38
+ * under `~/.codex`. The value is stored per session, not per index, because one index deliberately
39
+ * holds both and a hit that cannot say where it came from makes a caller read the wrong file. It is
40
+ * a closed set rather than a free-form string: an unknown origin is a bug in the indexer, and
41
+ * widening this list is how a new harness becomes queryable.
42
+ */
43
+ export declare const SESSIONGRAPH_SOURCES: readonly ["dsh", "codex"];
44
+ /** One session source. */
45
+ export type SessiongraphSource = (typeof SESSIONGRAPH_SOURCES)[number];
26
46
  /** Error codes the seam raises, so a consumer can distinguish "no index" from "bad request". */
27
47
  export type SessiongraphErrorCode = 'SESSIONGRAPH_UNAVAILABLE' | 'SESSIONGRAPH_CONFLICT' | 'SESSIONGRAPH_INVALID_PROVIDER' | 'SESSIONGRAPH_INVALID_REQUEST' | 'SESSIONGRAPH_NOT_FOUND' | 'SESSIONGRAPH_CORRUPT' | 'SESSIONGRAPH_IO';
28
48
  /** The single error type crossing the seam. */
@@ -46,12 +66,36 @@ export interface SessiongraphSearchRequest extends SessiongraphRequestBase {
46
66
  query: string;
47
67
  /** Restrict to one workspace (exact `cwd`), or a path prefix when it ends without a trailing slash. */
48
68
  workspace?: string;
69
+ /**
70
+ * Restrict to sessions one harness wrote.
71
+ *
72
+ * An index holds DSH's own logs and Codex's rollout logs side by side, and the two answer different
73
+ * questions: "what did I ask DSH about this repo" and "what did I ask Codex about it". Absent means
74
+ * every source, which is the right default for a cross-harness sweep.
75
+ */
76
+ source?: SessiongraphSource;
49
77
  /** Only entries at or after this epoch-millisecond timestamp. */
50
78
  after?: number;
51
79
  /** Only entries at or before this epoch-millisecond timestamp. */
52
80
  before?: number;
53
81
  /** Restrict to these entry kinds. Absent means every kind. */
54
82
  kinds?: readonly SessiongraphEntryKind[];
83
+ /**
84
+ * Restrict the search to one session: a full id, or a prefix long enough to identify exactly one.
85
+ *
86
+ * A prefix that matches several sessions raises `SESSIONGRAPH_INVALID_REQUEST` rather than
87
+ * guessing — a search silently answered from the wrong conversation is worse than a refusal. This
88
+ * is what replaces the built-in `session_event_search`, which needed its own tool for it.
89
+ */
90
+ sessionId?: string;
91
+ /**
92
+ * Return at most one hit per session (the strongest), default `true`.
93
+ *
94
+ * The built-in cross-session search answered one hit per session, and a caller paging through a
95
+ * verbatim log usually wants breadth across conversations, not ten matches from one. Callers that
96
+ * want every entry (a single-session sweep) turn it off.
97
+ */
98
+ dedupeBySession?: boolean;
55
99
  /** Maximum hits to return. */
56
100
  limit: number;
57
101
  /** Hits to skip, for paging. */
@@ -60,6 +104,8 @@ export interface SessiongraphSearchRequest extends SessiongraphRequestBase {
60
104
  /** One matched entry, with enough context to recognize it without a second query. */
61
105
  export interface SessiongraphHit {
62
106
  sessionId: SessiongraphSessionId;
107
+ /** Which harness wrote the session this hit belongs to. */
108
+ source: SessiongraphSource;
63
109
  /** Session title, when the session ever produced one. */
64
110
  title: string | undefined;
65
111
  /** The session's working directory. */
@@ -81,7 +127,13 @@ export interface SessiongraphHit {
81
127
  export interface SessiongraphSearchResult {
82
128
  operation: 'search';
83
129
  hits: SessiongraphHit[];
84
- /** Total entries matching the query across the whole store, before `limit`/`offset`. */
130
+ /**
131
+ * How many results matched before `limit`/`offset` — counted in the same unit the hits use.
132
+ *
133
+ * With `dedupeBySession` on (the default) that is **sessions**, because the hits are one per
134
+ * session; counting entries there would make `truncated` permanently true and paging incoherent.
135
+ * With it off, it is entries.
136
+ */
85
137
  total: number;
86
138
  /** True when `total` exceeded `limit + offset`, or when a snippet was clipped. */
87
139
  truncated: boolean;
@@ -89,6 +141,8 @@ export interface SessiongraphSearchResult {
89
141
  /** A session's metadata, as the timeline and session operations report it. */
90
142
  export interface SessiongraphSessionSummary {
91
143
  sessionId: SessiongraphSessionId;
144
+ /** Which harness wrote this session's log. */
145
+ source: SessiongraphSource;
92
146
  title: string | undefined;
93
147
  workspace: string;
94
148
  /** Epoch milliseconds of the session's first event. */
@@ -142,12 +196,16 @@ export interface SessiongraphTimelineRequest extends SessiongraphRequestBase {
142
196
  operation: 'timeline';
143
197
  /** Restrict to one workspace, or a path prefix when it ends without a trailing slash. */
144
198
  workspace?: string;
199
+ /** Restrict to one harness's sessions. Absent means every source. */
200
+ source?: SessiongraphSource;
145
201
  /** Only sessions whose last event is at or after this epoch-millisecond timestamp. */
146
202
  after?: number;
147
203
  /** Only sessions whose first event is at or before this epoch-millisecond timestamp. */
148
204
  before?: number;
149
205
  /** Maximum sessions to return, newest first. */
150
206
  limit: number;
207
+ /** Sessions to skip, for paging; follows the timeline's newest-first order. */
208
+ offset?: number;
151
209
  }
152
210
  /** Answer to a timeline request. */
153
211
  export interface SessiongraphTimelineResult {
@@ -159,8 +217,12 @@ export interface SessiongraphTimelineResult {
159
217
  /** Enumerate the workspaces the index covers. */
160
218
  export interface SessiongraphWorkspacesRequest extends SessiongraphRequestBase {
161
219
  operation: 'workspaces';
220
+ /** Restrict the census to one harness's sessions. Absent means every source. */
221
+ source?: SessiongraphSource;
162
222
  /** Maximum workspaces to return, most recently active first. */
163
223
  limit: number;
224
+ /** Workspaces to skip, for paging; follows the most-recently-active-first order. */
225
+ offset?: number;
164
226
  }
165
227
  /** One workspace's coverage. */
166
228
  export interface SessiongraphWorkspaceStat {
@@ -208,12 +270,120 @@ export interface SessiongraphStatusResult {
208
270
  /** Bytes the index file occupies on disk. */
209
271
  bytes: number;
210
272
  }
273
+ /**
274
+ * One raw event, reduced to the fields that let a reader orient around a target.
275
+ *
276
+ * The window around an event is answered as summaries rather than full payloads: a caller asking
277
+ * "what came before this" wants the shape of the conversation, not megabytes of tool output.
278
+ */
279
+ export interface SessiongraphRawEventSummary {
280
+ /** Event sequence number inside its session. */
281
+ seq: number;
282
+ /** Event timestamp, epoch milliseconds. */
283
+ time: number;
284
+ /** Event type as the log records it (`tool/call`, `assistant/message`, …). */
285
+ type: string;
286
+ }
287
+ /** One event exactly as the session log holds it, with no clipping applied. */
288
+ export interface SessiongraphRawEvent {
289
+ /** Event sequence number inside its session. */
290
+ seq: number;
291
+ /** Event timestamp, epoch milliseconds. */
292
+ time: number;
293
+ /** Event type as the log records it. */
294
+ type: string;
295
+ /**
296
+ * The event's own `data` payload, verbatim. Opaque to this seam on purpose: the log's event
297
+ * vocabulary belongs to `dsh-session`, and re-declaring it here would drift.
298
+ */
299
+ data: unknown;
300
+ }
301
+ /**
302
+ * Read one unabridged event, plus an optional window of neighbouring events.
303
+ *
304
+ * This is what replaces the built-in `session_event_read`. It reads the source log directly rather
305
+ * than the index, because the index stores semantic text clipped to a per-entry cap and this answer
306
+ * must be the original bytes.
307
+ */
308
+ export interface SessiongraphEventRequest extends SessiongraphRequestBase {
309
+ operation: 'event';
310
+ /** Full session id, or a prefix long enough to identify exactly one session. */
311
+ sessionId: string;
312
+ /** Sequence number of the target event. */
313
+ seq: number;
314
+ /** Raw events to include immediately before the target. Omitted means none. */
315
+ windowBefore?: number;
316
+ /** Raw events to include immediately after the target. Omitted means none. */
317
+ windowAfter?: number;
318
+ }
319
+ /** Answer to an event request. */
320
+ export interface SessiongraphEventResult {
321
+ operation: 'event';
322
+ session: SessiongraphSessionSummary;
323
+ /** The target event, unabridged. */
324
+ event: SessiongraphRawEvent;
325
+ /** Neighbours before the target, oldest first. */
326
+ before: SessiongraphRawEventSummary[];
327
+ /** Neighbours after the target, oldest first. */
328
+ after: SessiongraphRawEventSummary[];
329
+ }
330
+ /** One node of a recursive descendant tree. */
331
+ export interface SessiongraphTraceNode {
332
+ session: SessiongraphSessionSummary;
333
+ descendants: SessiongraphTraceNode[];
334
+ }
335
+ /**
336
+ * Follow session lineage, or one event's replacement and citation relationships.
337
+ *
338
+ * `seq` selects the shape: present means "trace this event", absent means "trace this session".
339
+ * Both live in one operation because they share the only other input (`sessionId`) and because a
340
+ * second tool would cost permanent schema tokens for a question asked rarely.
341
+ */
342
+ export interface SessiongraphTraceRequest extends SessiongraphRequestBase {
343
+ operation: 'trace';
344
+ /** Full session id, or a prefix long enough to identify exactly one session. */
345
+ sessionId: string;
346
+ /** Given → trace one event inside the session; omitted → trace the session's lineage. */
347
+ seq?: number;
348
+ /** Maximum descendant depth to walk. Omitted means the whole tree. */
349
+ depth?: number;
350
+ }
351
+ /** A session's ancestor chain and descendant tree. */
352
+ export interface SessiongraphSessionTraceResult {
353
+ operation: 'trace';
354
+ kind: 'session';
355
+ target: SessiongraphSessionSummary;
356
+ /** Ancestors, nearest first. */
357
+ ancestors: SessiongraphSessionSummary[];
358
+ descendants: SessiongraphTraceNode[];
359
+ /**
360
+ * False when the walk stopped early — a depth limit was hit, or a parent the index does not hold.
361
+ * A caller must be able to tell "this is the root" from "I gave up looking".
362
+ */
363
+ complete: boolean;
364
+ }
365
+ /** One event's positional replacements and the source events it cites. */
366
+ export interface SessiongraphEventTraceResult {
367
+ operation: 'trace';
368
+ kind: 'event';
369
+ session: SessiongraphSessionSummary;
370
+ /** The target event, reduced to its identity. */
371
+ event: SessiongraphRawEventSummary;
372
+ /** Sequence numbers that replaced this event, following the chain outward. */
373
+ replacedBy: number[];
374
+ /** Sequence numbers this event shadowed. */
375
+ shadowedSeqs: number[];
376
+ /** Sequence numbers this event cites as sources (a tool result citing its call, for instance). */
377
+ sourceEventSeqs: number[];
378
+ }
379
+ /** Either trace shape, discriminated on `kind`. */
380
+ export type SessiongraphTraceResult = SessiongraphSessionTraceResult | SessiongraphEventTraceResult;
211
381
  /** Every request the seam accepts, discriminated on `operation`. */
212
- export type SessiongraphRequest = SessiongraphSearchRequest | SessiongraphSessionRequest | SessiongraphTimelineRequest | SessiongraphWorkspacesRequest | SessiongraphStatusRequest;
382
+ export type SessiongraphRequest = SessiongraphSearchRequest | SessiongraphSessionRequest | SessiongraphEventRequest | SessiongraphTraceRequest | SessiongraphTimelineRequest | SessiongraphWorkspacesRequest | SessiongraphStatusRequest;
213
383
  /** The result type a given request produces. */
214
- export type SessiongraphResultFor<R extends SessiongraphRequest> = R extends SessiongraphSearchRequest ? SessiongraphSearchResult : R extends SessiongraphSessionRequest ? SessiongraphSessionResult : R extends SessiongraphTimelineRequest ? SessiongraphTimelineResult : R extends SessiongraphWorkspacesRequest ? SessiongraphWorkspacesResult : R extends SessiongraphStatusRequest ? SessiongraphStatusResult : never;
384
+ export type SessiongraphResultFor<R extends SessiongraphRequest> = R extends SessiongraphSearchRequest ? SessiongraphSearchResult : R extends SessiongraphSessionRequest ? SessiongraphSessionResult : R extends SessiongraphEventRequest ? SessiongraphEventResult : R extends SessiongraphTraceRequest ? SessiongraphTraceResult : R extends SessiongraphTimelineRequest ? SessiongraphTimelineResult : R extends SessiongraphWorkspacesRequest ? SessiongraphWorkspacesResult : R extends SessiongraphStatusRequest ? SessiongraphStatusResult : never;
215
385
  /** Every result the seam produces. */
216
- export type SessiongraphResult = SessiongraphSearchResult | SessiongraphSessionResult | SessiongraphTimelineResult | SessiongraphWorkspacesResult | SessiongraphStatusResult;
386
+ export type SessiongraphResult = SessiongraphSearchResult | SessiongraphSessionResult | SessiongraphEventResult | SessiongraphTraceResult | SessiongraphTimelineResult | SessiongraphWorkspacesResult | SessiongraphStatusResult;
217
387
  /**
218
388
  * A read provider: answers queries from an index it claims.
219
389
  *
package/lib/types.js CHANGED
@@ -8,8 +8,16 @@
8
8
  * never exposes a path to a table — only sessions, entries, and search hits.
9
9
  * @module dsh-plugin-sessiongraph-service/types
10
10
  */
11
- /** Every query operation the seam answers. */
12
- export const SESSIONGRAPH_OPERATIONS = ['search', 'session', 'timeline', 'workspaces', 'status'];
11
+ /**
12
+ * Every query operation the seam answers.
13
+ *
14
+ * `event` and `trace` were added when the model-facing surface collapsed from two tools into one:
15
+ * the built-in session tools could read a single unabridged event and follow replacement/citation
16
+ * relationships, and a replacement that dropped those answers would be a downgrade rather than a
17
+ * swap. An index run is deliberately **not** here — it writes, so the tool routes it through
18
+ * `Sessiongraph.index()` instead of pretending a query provider can build an index.
19
+ */
20
+ export const SESSIONGRAPH_OPERATIONS = ['search', 'session', 'event', 'trace', 'timeline', 'workspaces', 'status'];
13
21
  /**
14
22
  * What a searchable entry is, from the reader's point of view.
15
23
  *
@@ -18,6 +26,16 @@ export const SESSIONGRAPH_OPERATIONS = ['search', 'session', 'timeline', 'worksp
18
26
  * is a session title, kept as its own kind so a title match is recognizable in the hit list.
19
27
  */
20
28
  export const SESSIONGRAPH_ENTRY_KINDS = ['user', 'assistant', 'tool', 'title'];
29
+ /**
30
+ * Which agent harness wrote a session log.
31
+ *
32
+ * `dsh` is this host's own store under `~/.dsh/sessions`; `codex` is OpenAI Codex's rollout log
33
+ * under `~/.codex`. The value is stored per session, not per index, because one index deliberately
34
+ * holds both and a hit that cannot say where it came from makes a caller read the wrong file. It is
35
+ * a closed set rather than a free-form string: an unknown origin is a bug in the indexer, and
36
+ * widening this list is how a new harness becomes queryable.
37
+ */
38
+ export const SESSIONGRAPH_SOURCES = ['dsh', 'codex'];
21
39
  /** The single error type crossing the seam. */
22
40
  export class SessiongraphError extends Error {
23
41
  code;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-sessiongraph-service",
3
- "version": "0.1.21",
3
+ "version": "0.3.0",
4
4
  "description": "Service definition for the DeepSeek Harness session-graph seam (ctx.sessiongraph) — a store-provider and indexer-provider registry over local conversation history, plus the normalized query vocabulary the model-facing tool speaks",
5
5
  "keywords": [
6
6
  "dsh",