@principal-ai/principal-view-core 0.28.11 → 0.28.13

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/src/index.ts CHANGED
@@ -55,11 +55,33 @@ export type {
55
55
  JsonObject,
56
56
  ProjectArea,
57
57
  AuxiliaryManifest,
58
+ SubsystemConstruct,
59
+ SubsystemComponentRole,
60
+ SubsystemFramework,
61
+ SubsystemStereotype,
62
+ SubsystemEdgeMechanism,
63
+ SubsystemCapture,
64
+ SubsystemDeclarationProvenance,
65
+ SubsystemDeclTokenKind,
66
+ SubsystemDeclToken,
67
+ SubsystemDeclarationRef,
68
+ SubsystemConstructDeclaration,
69
+ SubsystemComponent,
70
+ SubsystemComponentEdge,
71
+ SubsystemThroughlineStep,
72
+ SubsystemThroughline,
73
+ SubsystemRepoRef,
74
+ SubsystemGraphDocument,
75
+ SubsystemGraphHostBinding,
76
+ SubsystemGraphHydrated,
58
77
  } from './types';
59
78
 
60
79
  // Auxiliary manifest type guard (browser-safe runtime helper)
61
80
  export { isAuxiliaryManifest } from './types/auxiliary';
62
81
 
82
+ // Subsystem graph type guard (browser-safe runtime helper)
83
+ export { isSubsystemGraphDocument } from './types/subsystem-graph';
84
+
63
85
  // Export path-based configuration types (browser-safe)
64
86
  export type { PathBasedGraphConfiguration } from './types/path-based-config';
65
87
 
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  ClineSessionReader,
3
+ CodexSessionReader,
4
+ CursorSessionReader,
3
5
  GrokSessionReader,
4
6
  PiSessionReader,
5
7
  type UniversalAgentSessionEvent,
@@ -24,7 +26,7 @@ export {
24
26
 
25
27
  /**
26
28
  * Shared, agent-agnostic session access: list + fetch across Cline, opencode,
27
- * pi, and Grok, normalizing raw events into repo-aware universal events.
29
+ * pi, Grok, and Codex, normalizing raw events into repo-aware universal events.
28
30
  *
29
31
  * This is the single source of truth for CLI and trail-viewer session pulling.
30
32
  * opencode listing delegates to `OpenCodeEventStore.listSessionsWithSummaries()`,
@@ -32,7 +34,14 @@ export {
32
34
  * from the standalone list.
33
35
  */
34
36
 
35
- export type SupportedSessionAgent = "cline" | "opencode" | "pi" | "grok" | "unknown";
37
+ export type SupportedSessionAgent =
38
+ | "cline"
39
+ | "codex"
40
+ | "cursor"
41
+ | "opencode"
42
+ | "pi"
43
+ | "grok"
44
+ | "unknown";
36
45
 
37
46
  export interface AgentSessionSummary {
38
47
  agent: SupportedSessionAgent;
@@ -54,6 +63,8 @@ export interface AgentSessionMeta {
54
63
  const clineReader = new ClineSessionReader();
55
64
  const piReader = new PiSessionReader();
56
65
  const grokReader = new GrokSessionReader();
66
+ const codexReader = new CodexSessionReader();
67
+ const cursorReader = new CursorSessionReader();
57
68
 
58
69
  function isClineSession(sessionId: string): boolean {
59
70
  return clineReader.readSession(sessionId) !== null;
@@ -67,6 +78,14 @@ function isGrokSession(sessionId: string): boolean {
67
78
  return grokReader.readSession(sessionId) !== null;
68
79
  }
69
80
 
81
+ function isCodexSession(sessionId: string): boolean {
82
+ return codexReader.readSession(sessionId) !== null;
83
+ }
84
+
85
+ function isCursorSession(sessionId: string): boolean {
86
+ return cursorReader.readSession(sessionId) !== null;
87
+ }
88
+
70
89
  /** List Cline CLI sessions from the durable on-disk transcript. */
71
90
  function listClineSessions(): AgentSessionSummary[] {
72
91
  return clineReader.listSessions().map((record) => {
@@ -127,6 +146,33 @@ function listGrokSessions(): AgentSessionSummary[] {
127
146
  });
128
147
  }
129
148
 
149
+ /** List Codex sessions from durable rollout JSONL. */
150
+ function listCodexSessions(): AgentSessionSummary[] {
151
+ return codexReader.listSessions().map((record) => ({
152
+ agent: "codex" as const,
153
+ sessionId: record.sessionId,
154
+ title: record.firstPrompt || "Codex session",
155
+ createdAt:
156
+ typeof record.meta.timestamp === "string"
157
+ ? record.meta.timestamp
158
+ : new Date(record.lastActivity).toISOString(),
159
+ eventCount: record.messageCount,
160
+ isFinished: false,
161
+ }));
162
+ }
163
+
164
+ /** List Cursor IDE agent chats from ~/.cursor/chats. */
165
+ function listCursorSessions(): AgentSessionSummary[] {
166
+ return cursorReader.listSessions().map((record) => ({
167
+ agent: "cursor" as const,
168
+ sessionId: record.sessionId,
169
+ title: record.title || "Cursor session",
170
+ createdAt: record.createdAt || new Date(record.lastActivity).toISOString(),
171
+ eventCount: record.messageCount,
172
+ isFinished: false,
173
+ }));
174
+ }
175
+
130
176
  /**
131
177
  * List opencode sessions, top-level only by default. Delegates to the store's
132
178
  * grouped `listSessionsWithSummaries()` which excludes subagent (child) sessions.
@@ -188,6 +234,16 @@ export function listAgentSessions(
188
234
  } catch (err) {
189
235
  if (!firstError) firstError = err as Error;
190
236
  }
237
+ try {
238
+ all.push(...listCodexSessions());
239
+ } catch (err) {
240
+ if (!firstError) firstError = err as Error;
241
+ }
242
+ try {
243
+ all.push(...listCursorSessions());
244
+ } catch (err) {
245
+ if (!firstError) firstError = err as Error;
246
+ }
191
247
  if (all.length === 0 && firstError) {
192
248
  throw firstError;
193
249
  }
@@ -197,10 +253,12 @@ export function listAgentSessions(
197
253
  /** Detect which agent a session id belongs to. */
198
254
  export function detectAgent(
199
255
  sessionId: string,
200
- ): "cline" | "opencode" | "pi" | "grok" {
256
+ ): "cline" | "opencode" | "pi" | "grok" | "codex" | "cursor" {
201
257
  if (isClineSession(sessionId)) return "cline";
202
258
  if (isPiSession(sessionId)) return "pi";
203
259
  if (isGrokSession(sessionId)) return "grok";
260
+ if (isCodexSession(sessionId)) return "codex";
261
+ if (isCursorSession(sessionId)) return "cursor";
204
262
  return "opencode";
205
263
  }
206
264
 
@@ -210,11 +268,11 @@ export function detectAgent(
210
268
  export function fetchRawEvents(
211
269
  sessionId: string,
212
270
  options: {
213
- agent?: "cline" | "opencode" | "pi" | "grok";
271
+ agent?: "cline" | "codex" | "cursor" | "opencode" | "pi" | "grok";
214
272
  dbPath?: string;
215
273
  } = {},
216
274
  ): {
217
- agent: "cline" | "opencode" | "pi" | "grok";
275
+ agent: "cline" | "codex" | "cursor" | "opencode" | "pi" | "grok";
218
276
  events: UniversalAgentSessionEvent[];
219
277
  sessionMeta: AgentSessionMeta;
220
278
  } {
@@ -261,6 +319,42 @@ export function fetchRawEvents(
261
319
  },
262
320
  };
263
321
  }
322
+ if (agent === "codex") {
323
+ const record = codexReader.readSession(sessionId);
324
+ const promptText = (record?.firstPrompt ?? "").trim();
325
+ return {
326
+ agent,
327
+ events: codexReader.toUniversalEvents(sessionId),
328
+ sessionMeta: {
329
+ sessionName: "codex",
330
+ sessionTask:
331
+ (promptText.length > 80 ? `${promptText.slice(0, 80)}…` : promptText) ||
332
+ "Codex session",
333
+ workingDirectory: record?.cwd ?? "",
334
+ createdAt:
335
+ typeof record?.meta.timestamp === "string"
336
+ ? record.meta.timestamp
337
+ : undefined,
338
+ },
339
+ };
340
+ }
341
+ if (agent === "cursor") {
342
+ const record = cursorReader.readSession(sessionId);
343
+ const promptText = (record?.firstPrompt ?? "").trim();
344
+ return {
345
+ agent,
346
+ events: cursorReader.toUniversalEvents(sessionId),
347
+ sessionMeta: {
348
+ sessionName: "cursor",
349
+ sessionTask:
350
+ record?.title ||
351
+ (promptText.length > 80 ? `${promptText.slice(0, 80)}…` : promptText) ||
352
+ "Cursor session",
353
+ workingDirectory: record?.cwd ?? "",
354
+ createdAt: record?.createdAt,
355
+ },
356
+ };
357
+ }
264
358
  const store = new OpenCodeEventStore({ dbPath: options.dbPath });
265
359
  try {
266
360
  const { events } = store.readAggregate(sessionId, { limit: 10000 });
@@ -457,3 +457,4 @@ export * from './canvas-scope';
457
457
  export * from './audit';
458
458
  export * from './dashboard';
459
459
  export * from './auxiliary';
460
+ export * from './subsystem-graph';
@@ -0,0 +1,346 @@
1
+ /**
2
+ * Subsystem Graph Types
3
+ *
4
+ * Two layers — do not conflate them:
5
+ *
6
+ * 1. **Portable document** (`SubsystemGraphDocument`) — the shareable standard.
7
+ * Schema: `schemas/subsystem-graph.schema.json`
8
+ * (`https://principal-ai.dev/schemas/subsystem-graph.schema.json`).
9
+ *
10
+ * 2. **Hydrated envelope** (`SubsystemGraphHydrated`) — portable document plus
11
+ * host/machine binding (local roots, provenance, store ids, verification).
12
+ * Used by viewers and stores; not part of the portable standard.
13
+ *
14
+ * Ontology: construct = what a node is, framework + stereotype = which
15
+ * framework pattern it plays, role = where it sits, process = where it runs.
16
+ * `symbol` is the code identity; `name` is the display label.
17
+ */
18
+
19
+ /** What the node IS as a declaration. `module` is not an authored construct —
20
+ * a module is its own subsystem. */
21
+ export type SubsystemConstruct =
22
+ | 'class'
23
+ | 'function'
24
+ | 'method'
25
+ | 'interface'
26
+ | 'type_alias'
27
+ | 'enum'
28
+ | 'store'
29
+ | 'external';
30
+
31
+ /** Where the node sits in the topology, orthogonal to construct. */
32
+ export type SubsystemComponentRole = 'entry' | 'service';
33
+
34
+ /**
35
+ * Framework that owns a stereotype vocabulary (open string).
36
+ * Examples: `react`, `vue`, `nestjs`, `django`, `spring`.
37
+ * Empty when the node is language-only / framework-agnostic.
38
+ */
39
+ export type SubsystemFramework = string;
40
+
41
+ /**
42
+ * Framework-level pattern stamped on a language construct (open string).
43
+ * Examples: `component`, `hook`, `middleware`, `controller`, `guard`.
44
+ * Empty when no framework pattern applies. Pair with `framework` when set.
45
+ */
46
+ export type SubsystemStereotype = string;
47
+
48
+ /** How `from` relates to `to` on an edge. */
49
+ export type SubsystemEdgeMechanism =
50
+ | 'imports'
51
+ | 'imports_from'
52
+ | 're_exports'
53
+ | 'defines'
54
+ | 'calls'
55
+ | 'extends'
56
+ | 'inherits'
57
+ | 'implements'
58
+ | 'mixes_in'
59
+ | 'uses'
60
+ | 'method'
61
+ | 'references'
62
+ | 'contains'
63
+ | 'feeds'
64
+ | 'produces'
65
+ | 'writes'
66
+ | 'reads'
67
+ | 'watches'
68
+ | 'registers-into';
69
+
70
+ export type SubsystemCapture = 'edited' | 'analyzed' | 'referenced';
71
+
72
+ export type SubsystemDeclarationProvenance = 'verified' | 'authored';
73
+
74
+ export type SubsystemDeclTokenKind =
75
+ | 'keyword'
76
+ | 'name'
77
+ | 'member'
78
+ | 'type'
79
+ | 'punctuation'
80
+ | 'string'
81
+ | 'newline';
82
+
83
+ export interface SubsystemDeclToken {
84
+ text: string;
85
+ kind: SubsystemDeclTokenKind;
86
+ /** Optional foreground when pre-tokenized for a theme. */
87
+ color?: string;
88
+ }
89
+
90
+ /** Anchored declaration location (usually from verify tooling). */
91
+ export interface SubsystemDeclarationRef {
92
+ file: string;
93
+ startLine: number;
94
+ lineHash: string;
95
+ graphifyNodeId?: string;
96
+ capturedAt: string;
97
+ revision?: {
98
+ headSha: string;
99
+ dirtyHash?: string | null;
100
+ };
101
+ }
102
+
103
+ export interface SubsystemParamInfo {
104
+ name?: string;
105
+ type: string;
106
+ ref?: SubsystemReferenceInfo;
107
+ }
108
+
109
+ export interface SubsystemPropertyInfo {
110
+ name: string;
111
+ type?: string;
112
+ typeRef?: SubsystemReferenceInfo;
113
+ nodeId?: string;
114
+ }
115
+
116
+ export interface SubsystemMethodInfo {
117
+ nodeId: string;
118
+ name: string;
119
+ parameters?: SubsystemParamInfo[];
120
+ returnType?: string;
121
+ returnTypeRef?: SubsystemReferenceInfo;
122
+ }
123
+
124
+ export interface SubsystemCallInfo {
125
+ nodeId: string;
126
+ name: string;
127
+ source_location?: string;
128
+ }
129
+
130
+ export interface SubsystemReferenceInfo {
131
+ nodeId: string;
132
+ name: string;
133
+ context?: string;
134
+ source_location?: string;
135
+ }
136
+
137
+ export interface SubsystemImportInfo {
138
+ nodeId: string;
139
+ name: string;
140
+ relation?: string;
141
+ source_location?: string;
142
+ }
143
+
144
+ export interface SubsystemClassDeclaration {
145
+ kind: 'class';
146
+ methods: SubsystemMethodInfo[];
147
+ properties: SubsystemPropertyInfo[];
148
+ extends: string[];
149
+ implements: string[];
150
+ instantiations: SubsystemCallInfo[];
151
+ references: SubsystemReferenceInfo[];
152
+ }
153
+
154
+ export interface SubsystemFunctionDeclaration {
155
+ kind: 'function';
156
+ parameters: SubsystemParamInfo[];
157
+ returnType?: string;
158
+ returnTypeRef?: SubsystemReferenceInfo;
159
+ callers: SubsystemCallInfo[];
160
+ callees: SubsystemCallInfo[];
161
+ }
162
+
163
+ export interface SubsystemMethodDeclaration {
164
+ kind: 'method';
165
+ hostClass: string;
166
+ parameters?: SubsystemParamInfo[];
167
+ returnType?: string;
168
+ }
169
+
170
+ export interface SubsystemTypeDeclaration {
171
+ kind: 'type';
172
+ properties: SubsystemPropertyInfo[];
173
+ usedBy: SubsystemReferenceInfo[];
174
+ implementors: string[];
175
+ }
176
+
177
+ export interface SubsystemModuleDeclaration {
178
+ kind: 'module';
179
+ exports: string[];
180
+ imports: SubsystemImportInfo[];
181
+ symbols: string[];
182
+ }
183
+
184
+ export interface SubsystemExternalDeclaration {
185
+ kind: 'external';
186
+ label: string;
187
+ }
188
+
189
+ export interface SubsystemStoreDeclaration {
190
+ kind: 'store';
191
+ properties: SubsystemPropertyInfo[];
192
+ }
193
+
194
+ /** Structured declaration shape of a construct. */
195
+ export type SubsystemConstructDeclaration =
196
+ | SubsystemClassDeclaration
197
+ | SubsystemFunctionDeclaration
198
+ | SubsystemMethodDeclaration
199
+ | SubsystemTypeDeclaration
200
+ | SubsystemModuleDeclaration
201
+ | SubsystemExternalDeclaration
202
+ | SubsystemStoreDeclaration;
203
+
204
+ /** A component node — the named unit, construct-tagged. */
205
+ export interface SubsystemComponent {
206
+ id: string;
207
+ /** Display label. Prefer aligning with `symbol` when present. */
208
+ name: string;
209
+ construct: SubsystemConstruct;
210
+ /** Repo-root-relative source path. */
211
+ file: string;
212
+ /** PURL for repo/package grouping. */
213
+ purl: string;
214
+ purpose?: string;
215
+ role?: SubsystemComponentRole;
216
+ /**
217
+ * Framework that owns the stereotype (e.g. `react`, `nestjs`).
218
+ * Orthogonal to `construct` — a React component is still `construct: function`.
219
+ */
220
+ framework?: SubsystemFramework;
221
+ /**
222
+ * Framework pattern this declaration plays (e.g. `component`, `hook`).
223
+ * Prefer this over inventing framework-specific constructs.
224
+ */
225
+ stereotype?: SubsystemStereotype;
226
+ process?: string;
227
+ /** Code identity — real declaration in `file` when set. */
228
+ symbol?: string;
229
+ layer?: number;
230
+ capture?: SubsystemCapture;
231
+ /** Structured declaration shape of the construct (params, members, …). */
232
+ declaration?: SubsystemConstructDeclaration;
233
+ declarationProvenance?: SubsystemDeclarationProvenance;
234
+ tokens?: SubsystemDeclToken[];
235
+ /** Location anchor (file/line/hash) — distinct from `declaration` (shape). */
236
+ declarationRef?: SubsystemDeclarationRef;
237
+ }
238
+
239
+ /** A cross-component edge. */
240
+ export interface SubsystemComponentEdge {
241
+ id: string;
242
+ /** Source component id. */
243
+ from: string;
244
+ /** Target component id (or external label). */
245
+ to: string;
246
+ mechanism: SubsystemEdgeMechanism;
247
+ /** Concrete file/symbol evidence (often purls). */
248
+ refs?: string[];
249
+ }
250
+
251
+ export interface SubsystemThroughlineStep {
252
+ edgeId: string;
253
+ file: string;
254
+ /** 1-based line within `file`. */
255
+ line: number;
256
+ /** Frame name for this hop (function/method/symbol). Optional. */
257
+ symbol?: string;
258
+ }
259
+
260
+ /** Ordered execution story over existing edges (one per flow). */
261
+ export interface SubsystemThroughline {
262
+ id: string;
263
+ title: string;
264
+ steps: SubsystemThroughlineStep[];
265
+ }
266
+
267
+ export interface SubsystemRepoRef {
268
+ owner: string;
269
+ name: string;
270
+ }
271
+
272
+ /**
273
+ * Portable subsystem graph — the shareable standard.
274
+ * No host paths, provenance, document-level repo, store ids, or verification.
275
+ * Repo identity lives on each component's `purl`.
276
+ */
277
+ export interface SubsystemGraphDocument {
278
+ /**
279
+ * Optional pointer to the JSON Schema that describes this file. Lets editors
280
+ * give autocomplete and validation when the field is set.
281
+ */
282
+ $schema?: string;
283
+
284
+ title: string;
285
+ description?: string;
286
+ components: SubsystemComponent[];
287
+ edges: SubsystemComponentEdge[];
288
+ throughlines?: SubsystemThroughline[];
289
+ }
290
+
291
+ /**
292
+ * Host/machine fields layered onto a portable document for local use.
293
+ * Not part of the portable standard — viewers and stores own this envelope.
294
+ */
295
+ export interface SubsystemGraphHostBinding {
296
+ /** Provenance of the graph (e.g. `agent:<name>`, `manual`). */
297
+ source?: string;
298
+ /**
299
+ * Optional document-level repo summary for host indexes/listings.
300
+ * Portable identity lives on each component's `purl`.
301
+ */
302
+ repo?: SubsystemRepoRef;
303
+ /**
304
+ * Local filesystem root component `file` paths resolve against.
305
+ * Opt-in; sandboxed reads for single-repo graphs.
306
+ */
307
+ repoRoot?: string;
308
+ /**
309
+ * Per-repo local roots for multi-repo graphs, keyed by purl repo key
310
+ * (`pkg:github/owner/name`, fragment stripped).
311
+ */
312
+ repoRoots?: Record<string, string>;
313
+ }
314
+
315
+ /**
316
+ * Hydrated graph: portable document + host binding + optional store metadata.
317
+ * Shape used after a host accepts/persists a graph for viewing on a machine.
318
+ */
319
+ export interface SubsystemGraphHydrated
320
+ extends SubsystemGraphDocument, SubsystemGraphHostBinding {
321
+ /** Store-assigned id when persisted. */
322
+ id?: string;
323
+ createdAt?: string;
324
+ updatedAt?: string;
325
+ /** Host verification result; shape is host-defined. */
326
+ verification?: unknown;
327
+ }
328
+
329
+ /**
330
+ * Type guard: true when the value plausibly conforms to a portable
331
+ * subsystem graph. Shallow check — full validation belongs to the schema /
332
+ * host validator.
333
+ */
334
+ export function isSubsystemGraphDocument(value: unknown): value is SubsystemGraphDocument {
335
+ if (!value || typeof value !== 'object') return false;
336
+ const v = value as {
337
+ title?: unknown;
338
+ components?: unknown;
339
+ edges?: unknown;
340
+ };
341
+ return (
342
+ typeof v.title === 'string' &&
343
+ Array.isArray(v.components) &&
344
+ Array.isArray(v.edges)
345
+ );
346
+ }