@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.
- package/dist/chat-protocol.d.ts +60 -0
- package/dist/chat-protocol.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/lib/domain-utils.d.ts +16 -0
- package/dist/lib/domain-utils.d.ts.map +1 -0
- package/dist/lib/ids.d.ts +18 -0
- package/dist/lib/ids.d.ts.map +1 -0
- package/dist/lib/result.d.ts +26 -0
- package/dist/lib/result.d.ts.map +1 -0
- package/dist/projections/agent-detail-projection.d.ts +91 -0
- package/dist/projections/agent-detail-projection.d.ts.map +1 -0
- package/dist/projections/agent-registry.d.ts +16 -0
- package/dist/projections/agent-registry.d.ts.map +1 -0
- package/dist/projections/agent-tree-projection.d.ts +30 -0
- package/dist/projections/agent-tree-projection.d.ts.map +1 -0
- package/dist/projections/chat-debug.d.ts +34 -0
- package/dist/projections/chat-debug.d.ts.map +1 -0
- package/dist/projections/events.d.ts +9 -0
- package/dist/projections/events.d.ts.map +1 -0
- package/dist/projections/index.d.ts +22 -0
- package/dist/projections/index.d.ts.map +1 -0
- package/dist/projections/mailbox.d.ts +21 -0
- package/dist/projections/mailbox.d.ts.map +1 -0
- package/dist/projections/metrics.d.ts +30 -0
- package/dist/projections/metrics.d.ts.map +1 -0
- package/dist/projections/protocol-status.d.ts +9 -0
- package/dist/projections/protocol-status.d.ts.map +1 -0
- package/dist/projections/services-projection.d.ts +24 -0
- package/dist/projections/services-projection.d.ts.map +1 -0
- package/dist/projections/session-info.d.ts +19 -0
- package/dist/projections/session-info.d.ts.map +1 -0
- package/dist/projections/timeline.d.ts +26 -0
- package/dist/projections/timeline.d.ts.map +1 -0
- package/dist/projections/types.d.ts +182 -0
- package/dist/projections/types.d.ts.map +1 -0
- package/dist/rpc/client.d.ts +79 -0
- package/dist/rpc/client.d.ts.map +1 -0
- package/dist/rpc/index.d.ts +9 -0
- package/dist/rpc/index.d.ts.map +1 -0
- package/dist/src/api-types.d.ts +8 -0
- package/dist/src/index.d.ts +17 -0
- package/dist/src/rpc/admin-methods.d.ts +99 -0
- package/dist/src/rpc/client.d.ts +26 -0
- package/dist/src/rpc/definition.d.ts +39 -0
- package/dist/src/rpc/instance-methods.d.ts +94 -0
- package/dist/src/rpc/methods.d.ts +260 -0
- package/dist/src/rpc/server.d.ts +21 -0
- package/dist/src/workspace-config.d.ts +16 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +36 -0
- package/src/chat-protocol.ts +46 -0
- package/src/globals.d.ts +3 -0
- package/src/index.ts +82 -0
- package/src/lib/domain-utils.ts +26 -0
- package/src/lib/ids.ts +19 -0
- package/src/lib/result.ts +35 -0
- package/src/projections/agent-detail-projection.ts +623 -0
- package/src/projections/agent-registry.ts +37 -0
- package/src/projections/agent-tree-projection.ts +229 -0
- package/src/projections/chat-debug.ts +260 -0
- package/src/projections/events.ts +10 -0
- package/src/projections/index.ts +59 -0
- package/src/projections/mailbox.ts +113 -0
- package/src/projections/metrics.ts +111 -0
- package/src/projections/protocol-status.ts +23 -0
- package/src/projections/services-projection.ts +89 -0
- package/src/projections/session-info.ts +47 -0
- package/src/projections/timeline.ts +228 -0
- package/src/projections/types.ts +237 -0
- package/src/rpc/client.ts +188 -0
- 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
|
+
}
|