@roj-ai/shared 0.0.2

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.
Files changed (72) hide show
  1. package/dist/chat-protocol.d.ts +60 -0
  2. package/dist/chat-protocol.d.ts.map +1 -0
  3. package/dist/index.d.ts +13 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/lib/domain-utils.d.ts +16 -0
  6. package/dist/lib/domain-utils.d.ts.map +1 -0
  7. package/dist/lib/ids.d.ts +18 -0
  8. package/dist/lib/ids.d.ts.map +1 -0
  9. package/dist/lib/result.d.ts +26 -0
  10. package/dist/lib/result.d.ts.map +1 -0
  11. package/dist/projections/agent-detail-projection.d.ts +91 -0
  12. package/dist/projections/agent-detail-projection.d.ts.map +1 -0
  13. package/dist/projections/agent-registry.d.ts +16 -0
  14. package/dist/projections/agent-registry.d.ts.map +1 -0
  15. package/dist/projections/agent-tree-projection.d.ts +30 -0
  16. package/dist/projections/agent-tree-projection.d.ts.map +1 -0
  17. package/dist/projections/chat-debug.d.ts +34 -0
  18. package/dist/projections/chat-debug.d.ts.map +1 -0
  19. package/dist/projections/events.d.ts +9 -0
  20. package/dist/projections/events.d.ts.map +1 -0
  21. package/dist/projections/index.d.ts +22 -0
  22. package/dist/projections/index.d.ts.map +1 -0
  23. package/dist/projections/mailbox.d.ts +21 -0
  24. package/dist/projections/mailbox.d.ts.map +1 -0
  25. package/dist/projections/metrics.d.ts +30 -0
  26. package/dist/projections/metrics.d.ts.map +1 -0
  27. package/dist/projections/protocol-status.d.ts +9 -0
  28. package/dist/projections/protocol-status.d.ts.map +1 -0
  29. package/dist/projections/services-projection.d.ts +24 -0
  30. package/dist/projections/services-projection.d.ts.map +1 -0
  31. package/dist/projections/session-info.d.ts +19 -0
  32. package/dist/projections/session-info.d.ts.map +1 -0
  33. package/dist/projections/timeline.d.ts +26 -0
  34. package/dist/projections/timeline.d.ts.map +1 -0
  35. package/dist/projections/types.d.ts +182 -0
  36. package/dist/projections/types.d.ts.map +1 -0
  37. package/dist/rpc/client.d.ts +79 -0
  38. package/dist/rpc/client.d.ts.map +1 -0
  39. package/dist/rpc/index.d.ts +9 -0
  40. package/dist/rpc/index.d.ts.map +1 -0
  41. package/dist/src/api-types.d.ts +8 -0
  42. package/dist/src/index.d.ts +17 -0
  43. package/dist/src/rpc/admin-methods.d.ts +99 -0
  44. package/dist/src/rpc/client.d.ts +26 -0
  45. package/dist/src/rpc/definition.d.ts +39 -0
  46. package/dist/src/rpc/instance-methods.d.ts +94 -0
  47. package/dist/src/rpc/methods.d.ts +260 -0
  48. package/dist/src/rpc/server.d.ts +21 -0
  49. package/dist/src/workspace-config.d.ts +16 -0
  50. package/dist/tsconfig.tsbuildinfo +1 -0
  51. package/package.json +36 -0
  52. package/src/chat-protocol.ts +46 -0
  53. package/src/globals.d.ts +3 -0
  54. package/src/index.ts +82 -0
  55. package/src/lib/domain-utils.ts +26 -0
  56. package/src/lib/ids.ts +19 -0
  57. package/src/lib/result.ts +35 -0
  58. package/src/projections/agent-detail-projection.ts +623 -0
  59. package/src/projections/agent-registry.ts +37 -0
  60. package/src/projections/agent-tree-projection.ts +229 -0
  61. package/src/projections/chat-debug.ts +260 -0
  62. package/src/projections/events.ts +10 -0
  63. package/src/projections/index.ts +59 -0
  64. package/src/projections/mailbox.ts +113 -0
  65. package/src/projections/metrics.ts +111 -0
  66. package/src/projections/protocol-status.ts +23 -0
  67. package/src/projections/services-projection.ts +89 -0
  68. package/src/projections/session-info.ts +47 -0
  69. package/src/projections/timeline.ts +228 -0
  70. package/src/projections/types.ts +237 -0
  71. package/src/rpc/client.ts +188 -0
  72. package/src/rpc/index.ts +14 -0
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Metrics projection - tracks token usage, costs, and call counts.
3
+ */
4
+
5
+ import type { ProjectionEvent } from './events.js'
6
+ import type { GetMetricsResponse } from './types.js'
7
+
8
+ export interface ProviderMetricsState {
9
+ llmCalls: number
10
+ totalTokens: number
11
+ promptTokens: number
12
+ completionTokens: number
13
+ totalCost: number
14
+ }
15
+
16
+ export interface MetricsState {
17
+ totalTokens: number
18
+ promptTokens: number
19
+ completionTokens: number
20
+ llmCalls: number
21
+ toolCalls: number
22
+ totalCost: number
23
+ firstEventTimestamp: number | null
24
+ lastEventTimestamp: number | null
25
+ byProvider: Record<string, ProviderMetricsState>
26
+ }
27
+
28
+ export function createMetricsState(): MetricsState {
29
+ return {
30
+ totalTokens: 0,
31
+ promptTokens: 0,
32
+ completionTokens: 0,
33
+ llmCalls: 0,
34
+ toolCalls: 0,
35
+ totalCost: 0,
36
+ firstEventTimestamp: null,
37
+ lastEventTimestamp: null,
38
+ byProvider: {},
39
+ }
40
+ }
41
+
42
+ export function applyEventToMetrics(state: MetricsState, event: ProjectionEvent): MetricsState {
43
+ // Update timestamps
44
+ const firstEventTimestamp = state.firstEventTimestamp ?? event.timestamp
45
+ const lastEventTimestamp = event.timestamp
46
+
47
+ switch (event.type) {
48
+ case 'inference_completed': {
49
+ const provider = event.metrics.provider
50
+ const byProvider = provider
51
+ ? {
52
+ ...state.byProvider,
53
+ [provider]: {
54
+ llmCalls: (state.byProvider[provider]?.llmCalls ?? 0) + 1,
55
+ totalTokens: (state.byProvider[provider]?.totalTokens ?? 0) + event.metrics.totalTokens,
56
+ promptTokens: (state.byProvider[provider]?.promptTokens ?? 0) + event.metrics.promptTokens,
57
+ completionTokens: (state.byProvider[provider]?.completionTokens ?? 0) + event.metrics.completionTokens,
58
+ totalCost: (state.byProvider[provider]?.totalCost ?? 0) + (event.metrics.cost ?? 0),
59
+ },
60
+ }
61
+ : state.byProvider
62
+ return {
63
+ ...state,
64
+ llmCalls: state.llmCalls + 1,
65
+ promptTokens: state.promptTokens + event.metrics.promptTokens,
66
+ completionTokens: state.completionTokens + event.metrics.completionTokens,
67
+ totalTokens: state.totalTokens + event.metrics.totalTokens,
68
+ totalCost: state.totalCost + (event.metrics.cost ?? 0),
69
+ firstEventTimestamp,
70
+ lastEventTimestamp,
71
+ byProvider,
72
+ }
73
+ }
74
+
75
+ case 'tool_started':
76
+ return {
77
+ ...state,
78
+ toolCalls: state.toolCalls + 1,
79
+ firstEventTimestamp,
80
+ lastEventTimestamp,
81
+ }
82
+
83
+ default:
84
+ // Only update timestamps for other events
85
+ if (state.firstEventTimestamp !== firstEventTimestamp || state.lastEventTimestamp !== lastEventTimestamp) {
86
+ return { ...state, firstEventTimestamp, lastEventTimestamp }
87
+ }
88
+ return state
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Convert MetricsState to GetMetricsResponse format.
94
+ */
95
+ export function metricsStateToResponse(state: MetricsState, agentCount: number): GetMetricsResponse {
96
+ const durationMs = state.firstEventTimestamp && state.lastEventTimestamp
97
+ ? state.lastEventTimestamp - state.firstEventTimestamp
98
+ : 0
99
+
100
+ return {
101
+ totalTokens: state.totalTokens,
102
+ promptTokens: state.promptTokens,
103
+ completionTokens: state.completionTokens,
104
+ totalCost: state.totalCost > 0 ? state.totalCost : undefined,
105
+ llmCalls: state.llmCalls,
106
+ toolCalls: state.toolCalls,
107
+ agentCount,
108
+ durationMs,
109
+ byProvider: state.byProvider,
110
+ }
111
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Protocol status conversion.
3
+ */
4
+
5
+ import type { DomainAgentStatus, ProtocolAgentStatus } from '@roj-ai/sdk'
6
+
7
+ /**
8
+ * Convert domain AgentStatus to protocol AgentStatus.
9
+ */
10
+ export function toProtocolStatus(status: DomainAgentStatus): ProtocolAgentStatus {
11
+ switch (status) {
12
+ case 'pending':
13
+ return 'idle'
14
+ case 'inferring':
15
+ return 'thinking'
16
+ case 'tool_exec':
17
+ return 'responding'
18
+ case 'errored':
19
+ return 'error'
20
+ case 'paused':
21
+ return 'paused'
22
+ }
23
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Services projection - tracks service states from events.
3
+ *
4
+ * Replaces selectPluginState(sessionState, 'services') for client-side use.
5
+ * Handles service_status_changed and session_restarted events.
6
+ */
7
+
8
+ import type { ServiceStatus } from '@roj-ai/sdk'
9
+ export type { ServiceStatus } from '@roj-ai/sdk'
10
+ import type { ProjectionEvent } from './events.js'
11
+
12
+ export interface ServiceEntry {
13
+ serviceType: string
14
+ status: ServiceStatus
15
+ port?: number
16
+ error?: string
17
+ startedAt?: number
18
+ readyAt?: number
19
+ stoppedAt?: number
20
+ }
21
+
22
+ export interface ServicesProjectionState {
23
+ services: Map<string, ServiceEntry>
24
+ }
25
+
26
+ export function createServicesProjectionState(): ServicesProjectionState {
27
+ return { services: new Map() }
28
+ }
29
+
30
+ export function applyEventToServices(state: ServicesProjectionState, event: ProjectionEvent): ServicesProjectionState {
31
+ switch (event.type) {
32
+ case 'service_status_changed': {
33
+ const newServices = new Map(state.services)
34
+ const existing = newServices.get(event.serviceType)
35
+
36
+ if (!existing && event.toStatus === 'starting') {
37
+ newServices.set(event.serviceType, {
38
+ serviceType: event.serviceType,
39
+ status: event.toStatus,
40
+ startedAt: event.timestamp,
41
+ })
42
+ } else if (existing) {
43
+ const updated: ServiceEntry = {
44
+ ...existing,
45
+ status: event.toStatus,
46
+ }
47
+ if (event.toStatus === 'starting') {
48
+ updated.startedAt = event.timestamp
49
+ updated.error = undefined
50
+ }
51
+ if (event.toStatus === 'ready') {
52
+ updated.readyAt = event.timestamp
53
+ if (event.port !== undefined) {
54
+ updated.port = event.port
55
+ }
56
+ }
57
+ if (event.toStatus === 'failed' && event.error) {
58
+ updated.error = event.error
59
+ }
60
+ if (event.toStatus === 'stopped') {
61
+ updated.stoppedAt = event.timestamp
62
+ }
63
+ newServices.set(event.serviceType, updated)
64
+ }
65
+
66
+ return { ...state, services: newServices }
67
+ }
68
+
69
+ case 'session_restarted': {
70
+ let changed = false
71
+ const newServices = new Map(state.services)
72
+ for (const [serviceType, entry] of state.services) {
73
+ if (entry.status === 'starting' || entry.status === 'ready') {
74
+ newServices.set(serviceType, {
75
+ ...entry,
76
+ status: 'stopped',
77
+ port: undefined,
78
+ stoppedAt: event.timestamp,
79
+ })
80
+ changed = true
81
+ }
82
+ }
83
+ return changed ? { ...state, services: newServices } : state
84
+ }
85
+
86
+ default:
87
+ return state
88
+ }
89
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Session info projection - tracks basic session metadata.
3
+ *
4
+ * Extracts session-level information (id, preset, status, timestamps)
5
+ * from lifecycle events without requiring full SessionState.
6
+ */
7
+
8
+ import type { SessionId } from '@roj-ai/sdk'
9
+ import type { ProjectionEvent } from './events.js'
10
+
11
+ export interface SessionInfoState {
12
+ id: SessionId | null
13
+ presetId: string | null
14
+ status: 'active' | 'closed'
15
+ createdAt: number | null
16
+ closedAt?: number
17
+ workspaceDir?: string
18
+ }
19
+
20
+ export function createSessionInfoState(): SessionInfoState {
21
+ return {
22
+ id: null,
23
+ presetId: null,
24
+ status: 'active',
25
+ createdAt: null,
26
+ }
27
+ }
28
+
29
+ export function applyEventToSessionInfo(state: SessionInfoState, event: ProjectionEvent): SessionInfoState {
30
+ switch (event.type) {
31
+ case 'session_created': {
32
+ return {
33
+ ...state,
34
+ id: event.sessionId,
35
+ presetId: event.presetId,
36
+ createdAt: event.timestamp,
37
+ workspaceDir: event.workspaceDir,
38
+ }
39
+ }
40
+ case 'session_closed':
41
+ return { ...state, status: 'closed', closedAt: event.timestamp }
42
+ case 'session_reopened':
43
+ return { ...state, status: 'active', closedAt: undefined }
44
+ default:
45
+ return state
46
+ }
47
+ }
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Timeline projection - tracks LLM calls, tool executions, and compactions.
3
+ */
4
+
5
+ import type { AgentId, InferenceStartedEvent, LLMCallLogEntry, ToolCallId, ToolStartedEvent } from '@roj-ai/sdk'
6
+ import type { AgentRegistryState } from './agent-registry.js'
7
+ import type { ProjectionEvent } from './events.js'
8
+ import type { TimelineItem } from './types.js'
9
+
10
+ export interface TimelineState {
11
+ items: TimelineItem[]
12
+ /** Pending inference starts waiting for completion */
13
+ pendingInferences: Map<string, InferenceStartedEvent>
14
+ /** Pending tool starts waiting for completion */
15
+ pendingTools: Map<string, ToolStartedEvent>
16
+ }
17
+
18
+ export function createTimelineState(): TimelineState {
19
+ return {
20
+ items: [],
21
+ pendingInferences: new Map(),
22
+ pendingTools: new Map(),
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Apply event to timeline state.
28
+ * @param registry Agent registry for name lookups
29
+ * @param llmCallMap Optional map for enriching LLM calls with detailed metrics
30
+ */
31
+ export function applyEventToTimeline(
32
+ state: TimelineState,
33
+ event: ProjectionEvent,
34
+ registry: AgentRegistryState,
35
+ llmCallMap?: Map<string, LLMCallLogEntry>,
36
+ ): TimelineState {
37
+ const getAgentName = (agentId: string): string => {
38
+ return registry.names.get(agentId as AgentId) ?? 'unknown'
39
+ }
40
+
41
+ switch (event.type) {
42
+ case 'inference_started': {
43
+ const newPending = new Map(state.pendingInferences)
44
+ newPending.set(event.agentId, event)
45
+ return { ...state, pendingInferences: newPending }
46
+ }
47
+
48
+ case 'inference_completed': {
49
+ const startEvent = state.pendingInferences.get(event.agentId)
50
+ const newPending = new Map(state.pendingInferences)
51
+ newPending.delete(event.agentId)
52
+
53
+ const llmCall = event.llmCallId && llmCallMap ? llmCallMap.get(event.llmCallId) : undefined
54
+ const metrics = llmCall?.metrics ?? event.metrics
55
+
56
+ const newItem: TimelineItem = {
57
+ id: event.llmCallId ?? `llm-${event.agentId}-${event.timestamp}`,
58
+ type: 'llm',
59
+ agentId: event.agentId,
60
+ agentName: getAgentName(event.agentId),
61
+ startedAt: startEvent?.timestamp ?? event.timestamp,
62
+ completedAt: event.timestamp,
63
+ durationMs: startEvent ? event.timestamp - startEvent.timestamp : undefined,
64
+ status: 'success',
65
+ model: event.metrics.model,
66
+ promptTokens: metrics.promptTokens,
67
+ completionTokens: metrics.completionTokens,
68
+ cachedTokens: metrics.cachedTokens,
69
+ cacheWriteTokens: metrics.cacheWriteTokens,
70
+ cost: metrics.cost,
71
+ llmCallId: event.llmCallId,
72
+ }
73
+
74
+ return {
75
+ ...state,
76
+ items: [...state.items, newItem],
77
+ pendingInferences: newPending,
78
+ }
79
+ }
80
+
81
+ case 'inference_failed': {
82
+ const startEvent = state.pendingInferences.get(event.agentId)
83
+ const newPending = new Map(state.pendingInferences)
84
+ newPending.delete(event.agentId)
85
+
86
+ const newItem: TimelineItem = {
87
+ id: event.llmCallId ?? `llm-${event.agentId}-${event.timestamp}`,
88
+ type: 'llm',
89
+ agentId: event.agentId,
90
+ agentName: getAgentName(event.agentId),
91
+ startedAt: startEvent?.timestamp ?? event.timestamp,
92
+ completedAt: event.timestamp,
93
+ durationMs: startEvent ? event.timestamp - startEvent.timestamp : undefined,
94
+ status: 'error',
95
+ error: event.error,
96
+ llmCallId: event.llmCallId,
97
+ }
98
+
99
+ return {
100
+ ...state,
101
+ items: [...state.items, newItem],
102
+ pendingInferences: newPending,
103
+ }
104
+ }
105
+
106
+ case 'tool_started': {
107
+ const newPending = new Map(state.pendingTools)
108
+ newPending.set(event.toolCallId, event)
109
+ return { ...state, pendingTools: newPending }
110
+ }
111
+
112
+ case 'tool_completed': {
113
+ const startEvent = state.pendingTools.get(event.toolCallId)
114
+ const newPending = new Map(state.pendingTools)
115
+ newPending.delete(event.toolCallId)
116
+
117
+ const newItem: TimelineItem = {
118
+ id: event.toolCallId,
119
+ type: 'tool',
120
+ agentId: event.agentId,
121
+ agentName: getAgentName(event.agentId),
122
+ startedAt: startEvent?.timestamp ?? event.timestamp,
123
+ completedAt: event.timestamp,
124
+ durationMs: startEvent ? event.timestamp - startEvent.timestamp : undefined,
125
+ status: 'success',
126
+ toolName: startEvent?.toolName,
127
+ toolCallId: event.toolCallId,
128
+ toolInput: startEvent?.input,
129
+ toolResult: event.result,
130
+ }
131
+
132
+ return {
133
+ ...state,
134
+ items: [...state.items, newItem],
135
+ pendingTools: newPending,
136
+ }
137
+ }
138
+
139
+ case 'tool_failed': {
140
+ const startEvent = state.pendingTools.get(event.toolCallId)
141
+ const newPending = new Map(state.pendingTools)
142
+ newPending.delete(event.toolCallId)
143
+
144
+ const newItem: TimelineItem = {
145
+ id: event.toolCallId,
146
+ type: 'tool',
147
+ agentId: event.agentId,
148
+ agentName: getAgentName(event.agentId),
149
+ startedAt: startEvent?.timestamp ?? event.timestamp,
150
+ completedAt: event.timestamp,
151
+ durationMs: startEvent ? event.timestamp - startEvent.timestamp : undefined,
152
+ status: 'error',
153
+ toolName: startEvent?.toolName,
154
+ toolCallId: event.toolCallId,
155
+ toolInput: startEvent?.input,
156
+ error: event.error,
157
+ }
158
+
159
+ return {
160
+ ...state,
161
+ items: [...state.items, newItem],
162
+ pendingTools: newPending,
163
+ }
164
+ }
165
+
166
+ case 'context_compacted': {
167
+ const newItem: TimelineItem = {
168
+ id: `compaction-${event.agentId}-${event.timestamp}`,
169
+ type: 'compaction',
170
+ agentId: event.agentId,
171
+ agentName: getAgentName(event.agentId),
172
+ startedAt: event.timestamp,
173
+ completedAt: event.timestamp,
174
+ status: 'success',
175
+ originalTokens: event.originalTokens,
176
+ compactedTokens: event.compactedTokens,
177
+ messagesRemoved: event.messagesRemoved,
178
+ }
179
+
180
+ return {
181
+ ...state,
182
+ items: [...state.items, newItem],
183
+ }
184
+ }
185
+
186
+ default:
187
+ return state
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Get timeline items including running items from pending state.
193
+ */
194
+ export function getTimelineItems(state: TimelineState, registry: AgentRegistryState): TimelineItem[] {
195
+ const items = [...state.items]
196
+
197
+ // Add running items for pending inferences
198
+ for (const [agentId, startEvent] of state.pendingInferences) {
199
+ items.push({
200
+ id: `llm-running-${agentId}-${startEvent.timestamp}`,
201
+ type: 'llm',
202
+ agentId: agentId as AgentId,
203
+ agentName: registry.names.get(agentId as AgentId) ?? 'unknown',
204
+ startedAt: startEvent.timestamp,
205
+ status: 'running',
206
+ })
207
+ }
208
+
209
+ // Add running items for pending tools
210
+ for (const [toolCallId, startEvent] of state.pendingTools) {
211
+ items.push({
212
+ id: toolCallId,
213
+ type: 'tool',
214
+ agentId: startEvent.agentId,
215
+ agentName: registry.names.get(startEvent.agentId) ?? 'unknown',
216
+ startedAt: startEvent.timestamp,
217
+ status: 'running',
218
+ toolName: startEvent.toolName,
219
+ toolCallId: toolCallId as ToolCallId,
220
+ toolInput: startEvent.input,
221
+ })
222
+ }
223
+
224
+ // Sort by startedAt
225
+ items.sort((a, b) => a.startedAt - b.startedAt)
226
+
227
+ return items
228
+ }