@dxos/react-ui-trace 0.11.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/LICENSE +105 -0
- package/README.md +3 -0
- package/dist/lib/index.mjs +2868 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/testing.mjs +338 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/translations.mjs +19 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/types/src/components/Gantt/Gantt.d.ts +68 -0
- package/dist/types/src/components/Gantt/Gantt.d.ts.map +1 -0
- package/dist/types/src/components/Gantt/Gantt.stories.d.ts +30 -0
- package/dist/types/src/components/Gantt/Gantt.stories.d.ts.map +1 -0
- package/dist/types/src/components/Gantt/index.d.ts +2 -0
- package/dist/types/src/components/Gantt/index.d.ts.map +1 -0
- package/dist/types/src/components/ProcessTree/ProcessTree.d.ts +32 -0
- package/dist/types/src/components/ProcessTree/ProcessTree.d.ts.map +1 -0
- package/dist/types/src/components/ProcessTree/ProcessTree.stories.d.ts +8 -0
- package/dist/types/src/components/ProcessTree/ProcessTree.stories.d.ts.map +1 -0
- package/dist/types/src/components/ProcessTree/index.d.ts +2 -0
- package/dist/types/src/components/ProcessTree/index.d.ts.map +1 -0
- package/dist/types/src/components/Timeline/Timeline.d.ts +50 -0
- package/dist/types/src/components/Timeline/Timeline.d.ts.map +1 -0
- package/dist/types/src/components/Timeline/Timeline.stories.d.ts +24 -0
- package/dist/types/src/components/Timeline/Timeline.stories.d.ts.map +1 -0
- package/dist/types/src/components/Timeline/index.d.ts +4 -0
- package/dist/types/src/components/Timeline/index.d.ts.map +1 -0
- package/dist/types/src/components/Timeline/timeline-options.d.ts +9 -0
- package/dist/types/src/components/Timeline/timeline-options.d.ts.map +1 -0
- package/dist/types/src/components/Timeline/timeline-printer.d.ts +7 -0
- package/dist/types/src/components/Timeline/timeline-printer.d.ts.map +1 -0
- package/dist/types/src/components/Timeline/timeline-printer.test.d.ts +2 -0
- package/dist/types/src/components/Timeline/timeline-printer.test.d.ts.map +1 -0
- package/dist/types/src/components/TracePanel/TracePanel.d.ts +28 -0
- package/dist/types/src/components/TracePanel/TracePanel.d.ts.map +1 -0
- package/dist/types/src/components/TracePanel/dxn-extractor.d.ts +19 -0
- package/dist/types/src/components/TracePanel/dxn-extractor.d.ts.map +1 -0
- package/dist/types/src/components/TracePanel/dxn-extractor.test.d.ts +2 -0
- package/dist/types/src/components/TracePanel/dxn-extractor.test.d.ts.map +1 -0
- package/dist/types/src/components/TracePanel/index.d.ts +4 -0
- package/dist/types/src/components/TracePanel/index.d.ts.map +1 -0
- package/dist/types/src/components/TracePanel/trace-filter.d.ts +39 -0
- package/dist/types/src/components/TracePanel/trace-filter.d.ts.map +1 -0
- package/dist/types/src/components/TracePanel/trace-filter.test.d.ts +2 -0
- package/dist/types/src/components/TracePanel/trace-filter.test.d.ts.map +1 -0
- package/dist/types/src/components/TracePanel/useTraceMenu.d.ts +15 -0
- package/dist/types/src/components/TracePanel/useTraceMenu.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +5 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/execution-graph/execution-graph.d.ts +139 -0
- package/dist/types/src/execution-graph/execution-graph.d.ts.map +1 -0
- package/dist/types/src/execution-graph/execution-graph.test.d.ts +2 -0
- package/dist/types/src/execution-graph/execution-graph.test.d.ts.map +1 -0
- package/dist/types/src/execution-graph/index.d.ts +4 -0
- package/dist/types/src/execution-graph/index.d.ts.map +1 -0
- package/dist/types/src/execution-graph/pending-block-status.d.ts +26 -0
- package/dist/types/src/execution-graph/pending-block-status.d.ts.map +1 -0
- package/dist/types/src/execution-graph/pending-block-status.test.d.ts +2 -0
- package/dist/types/src/execution-graph/pending-block-status.test.d.ts.map +1 -0
- package/dist/types/src/execution-graph/remote-snapshot.test.d.ts +2 -0
- package/dist/types/src/execution-graph/remote-snapshot.test.d.ts.map +1 -0
- package/dist/types/src/execution-graph/span-tree.d.ts +106 -0
- package/dist/types/src/execution-graph/span-tree.d.ts.map +1 -0
- package/dist/types/src/execution-graph/span-tree.test.d.ts +2 -0
- package/dist/types/src/execution-graph/span-tree.test.d.ts.map +1 -0
- package/dist/types/src/execution-graph/sub-agent-delegation.test.d.ts +2 -0
- package/dist/types/src/execution-graph/sub-agent-delegation.test.d.ts.map +1 -0
- package/dist/types/src/execution-graph/testing/collect-trace-events.d.ts +40 -0
- package/dist/types/src/execution-graph/testing/collect-trace-events.d.ts.map +1 -0
- package/dist/types/src/execution-graph/testing/index.d.ts +2 -0
- package/dist/types/src/execution-graph/testing/index.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +4 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useExecutionGraph.d.ts +17 -0
- package/dist/types/src/hooks/useExecutionGraph.d.ts.map +1 -0
- package/dist/types/src/hooks/useSessionTimeline.d.ts +17 -0
- package/dist/types/src/hooks/useSessionTimeline.d.ts.map +1 -0
- package/dist/types/src/hooks/useTraceMessages.d.ts +17 -0
- package/dist/types/src/hooks/useTraceMessages.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/session-timeline/index.d.ts +3 -0
- package/dist/types/src/session-timeline/index.d.ts.map +1 -0
- package/dist/types/src/session-timeline/session-timeline.d.ts +19 -0
- package/dist/types/src/session-timeline/session-timeline.d.ts.map +1 -0
- package/dist/types/src/session-timeline/session-timeline.test.d.ts +2 -0
- package/dist/types/src/session-timeline/session-timeline.test.d.ts.map +1 -0
- package/dist/types/src/session-timeline/types.d.ts +129 -0
- package/dist/types/src/session-timeline/types.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +5 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/simulated-agent.d.ts +63 -0
- package/dist/types/src/testing/simulated-agent.d.ts.map +1 -0
- package/dist/types/src/testing/snapshot-playback.d.ts +12 -0
- package/dist/types/src/testing/snapshot-playback.d.ts.map +1 -0
- package/dist/types/src/testing/test-trace.d.ts +5 -0
- package/dist/types/src/testing/test-trace.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +19 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +77 -0
- package/src/components/Gantt/Gantt.stories.tsx +241 -0
- package/src/components/Gantt/Gantt.tsx +589 -0
- package/src/components/Gantt/index.ts +5 -0
- package/src/components/ProcessTree/ProcessTree.stories.tsx +95 -0
- package/src/components/ProcessTree/ProcessTree.tsx +265 -0
- package/src/components/ProcessTree/index.ts +5 -0
- package/src/components/Timeline/Timeline.stories.tsx +326 -0
- package/src/components/Timeline/Timeline.tsx +534 -0
- package/src/components/Timeline/git.drawio.svg +821 -0
- package/src/components/Timeline/index.ts +7 -0
- package/src/components/Timeline/timeline-options.ts +27 -0
- package/src/components/Timeline/timeline-printer.test.ts +76 -0
- package/src/components/Timeline/timeline-printer.ts +223 -0
- package/src/components/TracePanel/TracePanel.tsx +248 -0
- package/src/components/TracePanel/dxn-extractor.test.ts +153 -0
- package/src/components/TracePanel/dxn-extractor.ts +166 -0
- package/src/components/TracePanel/index.ts +7 -0
- package/src/components/TracePanel/trace-filter.test.ts +137 -0
- package/src/components/TracePanel/trace-filter.ts +120 -0
- package/src/components/TracePanel/useTraceMenu.ts +95 -0
- package/src/components/index.ts +8 -0
- package/src/execution-graph/SPEC.md +749 -0
- package/src/execution-graph/execution-graph.test.ts +1205 -0
- package/src/execution-graph/execution-graph.ts +1076 -0
- package/src/execution-graph/index.ts +35 -0
- package/src/execution-graph/pending-block-status.test.ts +118 -0
- package/src/execution-graph/pending-block-status.ts +88 -0
- package/src/execution-graph/remote-snapshot.test.ts +116 -0
- package/src/execution-graph/span-tree.test.ts +370 -0
- package/src/execution-graph/span-tree.ts +404 -0
- package/src/execution-graph/sub-agent-delegation.test.ts +65 -0
- package/src/execution-graph/testing/collect-trace-events.ts +76 -0
- package/src/execution-graph/testing/index.ts +5 -0
- package/src/execution-graph/testing/sub-agent-delegation.json +1285 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/useExecutionGraph.ts +107 -0
- package/src/hooks/useSessionTimeline.ts +53 -0
- package/src/hooks/useTraceMessages.ts +58 -0
- package/src/index.ts +8 -0
- package/src/session-timeline/index.ts +6 -0
- package/src/session-timeline/session-timeline.test.ts +662 -0
- package/src/session-timeline/session-timeline.ts +605 -0
- package/src/session-timeline/types.ts +100 -0
- package/src/testing/index.ts +8 -0
- package/src/testing/simulated-agent.ts +293 -0
- package/src/testing/snapshot-playback.ts +45 -0
- package/src/testing/test-trace.ts +22 -0
- package/src/translations.ts +29 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
|
|
7
|
+
export const LaneKind = Schema.Literals(['session', 'task']);
|
|
8
|
+
export type LaneKind = Schema.Schema.Type<typeof LaneKind>;
|
|
9
|
+
|
|
10
|
+
export const LaneStatus = Schema.Literals(['pending', 'blocked', 'running', 'review', 'done', 'failed']);
|
|
11
|
+
export type LaneStatus = Schema.Schema.Type<typeof LaneStatus>;
|
|
12
|
+
|
|
13
|
+
export const TokenUsage = Schema.Struct({
|
|
14
|
+
input: Schema.Number,
|
|
15
|
+
output: Schema.Number,
|
|
16
|
+
total: Schema.Number,
|
|
17
|
+
});
|
|
18
|
+
export type TokenUsage = Schema.Schema.Type<typeof TokenUsage>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A conversation the timeline draws as a session lane: the chat, reduced to what the join needs so
|
|
22
|
+
* the builder does not depend on the chat's schema. `uri` is what the agent process targets and
|
|
23
|
+
* `feedId` what its trace meta names; `taskIds` is the checklist.
|
|
24
|
+
*/
|
|
25
|
+
export const Session = Schema.Struct({
|
|
26
|
+
id: Schema.String,
|
|
27
|
+
label: Schema.optional(Schema.String),
|
|
28
|
+
uri: Schema.String,
|
|
29
|
+
feedId: Schema.optional(Schema.String),
|
|
30
|
+
taskIds: Schema.Array(Schema.String),
|
|
31
|
+
});
|
|
32
|
+
export type Session = Schema.Schema.Type<typeof Session>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Node in the supervisor lane a child session was spawned from, so the connector can start at the
|
|
36
|
+
* exact marker rather than at the lane's edge.
|
|
37
|
+
*/
|
|
38
|
+
export const DelegationSource = Schema.Struct({
|
|
39
|
+
laneId: Schema.String,
|
|
40
|
+
markerId: Schema.String,
|
|
41
|
+
});
|
|
42
|
+
export type DelegationSource = Schema.Schema.Type<typeof DelegationSource>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* One row of the timeline: a session (supervisor or delegated sub-agent) rendered as a box spanning
|
|
46
|
+
* `start`..`end`, or a task rendered as a thin line grouped under its session.
|
|
47
|
+
*/
|
|
48
|
+
export const Lane = Schema.Struct({
|
|
49
|
+
id: Schema.String,
|
|
50
|
+
kind: LaneKind,
|
|
51
|
+
label: Schema.String,
|
|
52
|
+
status: LaneStatus,
|
|
53
|
+
/** Epoch ms. */
|
|
54
|
+
start: Schema.optional(Schema.Number),
|
|
55
|
+
end: Schema.optional(Schema.Number),
|
|
56
|
+
parentId: Schema.optional(Schema.String),
|
|
57
|
+
sessionId: Schema.optional(Schema.String),
|
|
58
|
+
taskId: Schema.optional(Schema.String),
|
|
59
|
+
pid: Schema.optional(Schema.String),
|
|
60
|
+
/** Lane ids this lane waits on (task dependencies). */
|
|
61
|
+
blockedOn: Schema.optional(Schema.Array(Schema.String)),
|
|
62
|
+
delegatedFrom: Schema.optional(DelegationSource),
|
|
63
|
+
tokens: Schema.optional(TokenUsage),
|
|
64
|
+
toolCalls: Schema.optional(Schema.Number),
|
|
65
|
+
});
|
|
66
|
+
export type Lane = Schema.Schema.Type<typeof Lane>;
|
|
67
|
+
|
|
68
|
+
export const MarkerKind = Schema.Literals(['request', 'operation', 'tool', 'message', 'error', 'delegation', 'task']);
|
|
69
|
+
export type MarkerKind = Schema.Schema.Type<typeof MarkerKind>;
|
|
70
|
+
|
|
71
|
+
export const MarkerLevel = Schema.Literals(['info', 'warn', 'error']);
|
|
72
|
+
export type MarkerLevel = Schema.Schema.Type<typeof MarkerLevel>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A point event on a lane, rendered as a node inside the session box.
|
|
76
|
+
*/
|
|
77
|
+
export const Marker = Schema.Struct({
|
|
78
|
+
id: Schema.String,
|
|
79
|
+
laneId: Schema.String,
|
|
80
|
+
kind: MarkerKind,
|
|
81
|
+
timestamp: Schema.Number,
|
|
82
|
+
label: Schema.String,
|
|
83
|
+
level: Schema.optional(MarkerLevel),
|
|
84
|
+
pid: Schema.optional(Schema.String),
|
|
85
|
+
detail: Schema.optional(Schema.Unknown),
|
|
86
|
+
});
|
|
87
|
+
export type Marker = Schema.Schema.Type<typeof Marker>;
|
|
88
|
+
|
|
89
|
+
export const TimeRange = Schema.Struct({
|
|
90
|
+
start: Schema.Number,
|
|
91
|
+
end: Schema.Number,
|
|
92
|
+
});
|
|
93
|
+
export type TimeRange = Schema.Schema.Type<typeof TimeRange>;
|
|
94
|
+
|
|
95
|
+
export const SessionTimeline = Schema.Struct({
|
|
96
|
+
lanes: Schema.Array(Lane),
|
|
97
|
+
markers: Schema.Array(Marker),
|
|
98
|
+
range: TimeRange,
|
|
99
|
+
});
|
|
100
|
+
export type SessionTimeline = Schema.Schema.Type<typeof SessionTimeline>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
export * from './simulated-agent.ts';
|
|
6
|
+
export * from './snapshot-playback.ts';
|
|
7
|
+
export * from './test-trace.ts';
|
|
8
|
+
export { default as subAgentDelegationFixture } from '../execution-graph/testing/sub-agent-delegation.json';
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
|
|
8
|
+
import { type ProcessManager } from '@dxos/compute-runtime';
|
|
9
|
+
import * as Process from '@dxos/compute/Process';
|
|
10
|
+
import * as Trace from '@dxos/compute/Trace';
|
|
11
|
+
import { EntityId } from '@dxos/keys';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* One simulated operation. Rendered as a child process in the tree and as an `operation.start`/
|
|
15
|
+
* `operation.end` span on the timeline.
|
|
16
|
+
*/
|
|
17
|
+
export interface SimulatedTool {
|
|
18
|
+
readonly key: string;
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly icon?: string;
|
|
21
|
+
readonly durationMs: number;
|
|
22
|
+
/** Nested operations, spawned under this one so the tree exercises more than one level. */
|
|
23
|
+
readonly children?: readonly SimulatedTool[];
|
|
24
|
+
/** Run `children` concurrently, producing parallel lanes on the timeline. */
|
|
25
|
+
readonly concurrent?: boolean;
|
|
26
|
+
/** End with `outcome: 'failure'` and fail the process. */
|
|
27
|
+
readonly fail?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface AgentScenario {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly prompt: string;
|
|
33
|
+
readonly tools: readonly SimulatedTool[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const agentScenarios: readonly AgentScenario[] = [
|
|
37
|
+
{
|
|
38
|
+
name: 'Create organization',
|
|
39
|
+
prompt: 'Create an organization called "Cyberdyne Systems"',
|
|
40
|
+
tools: [
|
|
41
|
+
{ key: 'list-schemas', name: 'List Schemas', icon: 'ph--list--regular', durationMs: 800 },
|
|
42
|
+
{
|
|
43
|
+
key: 'create-object',
|
|
44
|
+
name: 'Create Object',
|
|
45
|
+
icon: 'ph--plus-circle--regular',
|
|
46
|
+
durationMs: 1_600,
|
|
47
|
+
children: [{ key: 'validate', name: 'Validate', icon: 'ph--check--regular', durationMs: 600 }],
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'Research',
|
|
53
|
+
prompt: 'Search for all organizations and persons',
|
|
54
|
+
tools: [
|
|
55
|
+
{ key: 'list-schemas', name: 'List Schemas', icon: 'ph--list--regular', durationMs: 700 },
|
|
56
|
+
{
|
|
57
|
+
key: 'delegate',
|
|
58
|
+
name: 'Delegate Research',
|
|
59
|
+
icon: 'ph--users-three--regular',
|
|
60
|
+
durationMs: 2_400,
|
|
61
|
+
concurrent: true,
|
|
62
|
+
children: [
|
|
63
|
+
{ key: 'query', name: 'Query Organizations', icon: 'ph--magnifying-glass--regular', durationMs: 1_800 },
|
|
64
|
+
{ key: 'query', name: 'Query Persons', icon: 'ph--magnifying-glass--regular', durationMs: 1_200 },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{ key: 'summarize', name: 'Summarize', icon: 'ph--article--regular', durationMs: 900 },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'Failing run',
|
|
72
|
+
prompt: 'Create a person named "John Connor"',
|
|
73
|
+
tools: [
|
|
74
|
+
{ key: 'list-schemas', name: 'List Schemas', icon: 'ph--list--regular', durationMs: 600 },
|
|
75
|
+
{ key: 'create-object', name: 'Create Object', icon: 'ph--plus-circle--regular', durationMs: 1_200, fail: true },
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const delay = (ms: number) => Effect.promise(() => new Promise<void>((resolve) => setTimeout(resolve, ms)));
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Write a trace event and yield to the event loop so FeedTraceSink flushes it to ECHO before continuing.
|
|
84
|
+
*/
|
|
85
|
+
const writeAndFlush = <T>(eventType: Trace.EventType<T>, payload: T) =>
|
|
86
|
+
Effect.gen(function* () {
|
|
87
|
+
yield* Trace.write(eventType, payload);
|
|
88
|
+
yield* delay(100);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
//
|
|
92
|
+
// Agent.
|
|
93
|
+
//
|
|
94
|
+
|
|
95
|
+
const AgentStep = Schema.Union([
|
|
96
|
+
Schema.Struct({ _tag: Schema.Literal('begin'), prompt: Schema.String }),
|
|
97
|
+
Schema.Struct({ _tag: Schema.Literal('toolCall'), toolCallId: Schema.String, name: Schema.String }),
|
|
98
|
+
Schema.Struct({ _tag: Schema.Literal('toolResult'), toolCallId: Schema.String, name: Schema.String }),
|
|
99
|
+
Schema.Struct({ _tag: Schema.Literal('end'), status: Schema.Literals(['success', 'error']) }),
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
type AgentStep = Schema.Schema.Type<typeof AgentStep>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Storybook fixture: a {@link Process} standing in for a conversational agent. Each input advances
|
|
106
|
+
* it one step, so the caller can interleave the agent's own trace events with the child operation
|
|
107
|
+
* processes that {@link runScenario} spawns beneath it.
|
|
108
|
+
*/
|
|
109
|
+
export const SimulatedAgent = Process.make(
|
|
110
|
+
{
|
|
111
|
+
key: 'org.dxos.testing.process.agent',
|
|
112
|
+
input: AgentStep,
|
|
113
|
+
output: Schema.Void,
|
|
114
|
+
services: [Trace.TraceService],
|
|
115
|
+
},
|
|
116
|
+
(ctx) =>
|
|
117
|
+
Effect.gen(function* () {
|
|
118
|
+
// One message id spans the whole turn, so every block groups under a single message.
|
|
119
|
+
const messageId = EntityId.random();
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
onInput: (step: AgentStep) =>
|
|
123
|
+
Effect.gen(function* () {
|
|
124
|
+
switch (step._tag) {
|
|
125
|
+
case 'begin': {
|
|
126
|
+
yield* writeAndFlush(Trace.AgentRequestBegin, {});
|
|
127
|
+
yield* writeAndFlush(Trace.CompleteBlock, {
|
|
128
|
+
messageId,
|
|
129
|
+
role: 'user',
|
|
130
|
+
block: { _tag: 'text', text: step.prompt },
|
|
131
|
+
});
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
case 'toolCall': {
|
|
136
|
+
yield* writeAndFlush(Trace.CompleteBlock, {
|
|
137
|
+
messageId,
|
|
138
|
+
role: 'assistant',
|
|
139
|
+
block: {
|
|
140
|
+
_tag: 'toolCall',
|
|
141
|
+
toolCallId: step.toolCallId,
|
|
142
|
+
name: step.name,
|
|
143
|
+
input: '{}',
|
|
144
|
+
providerExecuted: false,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
case 'toolResult': {
|
|
151
|
+
yield* writeAndFlush(Trace.CompleteBlock, {
|
|
152
|
+
messageId,
|
|
153
|
+
role: 'assistant',
|
|
154
|
+
block: {
|
|
155
|
+
_tag: 'toolResult',
|
|
156
|
+
toolCallId: step.toolCallId,
|
|
157
|
+
name: step.name,
|
|
158
|
+
providerExecuted: false,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
case 'end': {
|
|
165
|
+
yield* writeAndFlush(Trace.AgentRequestEnd, { status: step.status });
|
|
166
|
+
if (step.status === 'error') {
|
|
167
|
+
ctx.fail(new Error('Simulated agent failure'));
|
|
168
|
+
} else {
|
|
169
|
+
ctx.succeed();
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}).pipe(Effect.orDie),
|
|
175
|
+
onAlarm: () => Effect.void,
|
|
176
|
+
onChildEvent: () => Effect.void,
|
|
177
|
+
};
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
//
|
|
182
|
+
// Operation.
|
|
183
|
+
//
|
|
184
|
+
|
|
185
|
+
const OperationInput = Schema.Struct({
|
|
186
|
+
key: Schema.String,
|
|
187
|
+
name: Schema.String,
|
|
188
|
+
icon: Schema.optional(Schema.String),
|
|
189
|
+
durationMs: Schema.Number,
|
|
190
|
+
fail: Schema.optional(Schema.Boolean),
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
type OperationInput = Schema.Schema.Type<typeof OperationInput>;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Storybook fixture: a {@link Process} that emits the `operation.start`/`operation.end` pair a real
|
|
197
|
+
* operation process emits, with a simulated execution time in between.
|
|
198
|
+
*/
|
|
199
|
+
export const SimulatedOperation = Process.make(
|
|
200
|
+
{
|
|
201
|
+
key: 'org.dxos.testing.process.operation',
|
|
202
|
+
input: OperationInput,
|
|
203
|
+
output: Schema.Void,
|
|
204
|
+
services: [Trace.TraceService],
|
|
205
|
+
},
|
|
206
|
+
(ctx) =>
|
|
207
|
+
Effect.succeed({
|
|
208
|
+
onInput: ({ key, name, icon, durationMs, fail }: OperationInput) =>
|
|
209
|
+
Effect.gen(function* () {
|
|
210
|
+
yield* writeAndFlush(Trace.OperationStart, { key, name, icon });
|
|
211
|
+
yield* delay(durationMs);
|
|
212
|
+
yield* writeAndFlush(Trace.OperationEnd, {
|
|
213
|
+
key,
|
|
214
|
+
name,
|
|
215
|
+
icon,
|
|
216
|
+
outcome: fail ? 'failure' : 'success',
|
|
217
|
+
error: fail ? `Simulated failure in '${name}'` : undefined,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
if (fail) {
|
|
221
|
+
ctx.fail(new Error(`Simulated failure in '${name}'`));
|
|
222
|
+
} else {
|
|
223
|
+
ctx.succeed();
|
|
224
|
+
}
|
|
225
|
+
}).pipe(Effect.orDie),
|
|
226
|
+
onAlarm: () => Effect.void,
|
|
227
|
+
onChildEvent: () => Effect.void,
|
|
228
|
+
}),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
//
|
|
232
|
+
// Orchestration.
|
|
233
|
+
//
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Spawns one simulated operation (and its children) beneath `parentProcessId`.
|
|
237
|
+
* A process cannot spawn its own children, so the tree is assembled from the caller's side.
|
|
238
|
+
*/
|
|
239
|
+
const runTool = (
|
|
240
|
+
manager: ProcessManager.Manager,
|
|
241
|
+
tool: SimulatedTool,
|
|
242
|
+
parentProcessId: Process.ID,
|
|
243
|
+
environment: Process.Environment,
|
|
244
|
+
): Effect.Effect<void> =>
|
|
245
|
+
Effect.gen(function* () {
|
|
246
|
+
const handle = yield* manager.spawn(SimulatedOperation, { parentProcessId, name: tool.name, environment });
|
|
247
|
+
yield* handle.submitInput({
|
|
248
|
+
key: tool.key,
|
|
249
|
+
name: tool.name,
|
|
250
|
+
icon: tool.icon,
|
|
251
|
+
durationMs: tool.durationMs,
|
|
252
|
+
fail: tool.fail,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const children = tool.children ?? [];
|
|
256
|
+
if (children.length > 0) {
|
|
257
|
+
const effects = children.map((child) => runTool(manager, child, handle.pid, environment));
|
|
258
|
+
yield* tool.concurrent ? Effect.all(effects, { concurrency: 'unbounded' }) : Effect.all(effects);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// A failing operation surfaces as a defect, which `Effect.ignore` would not catch; the scenario
|
|
262
|
+
// only needs the trace it wrote, and carries on to close out the agent.
|
|
263
|
+
yield* Effect.exit(handle.runToCompletion());
|
|
264
|
+
}).pipe(Effect.orDie);
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Runs one {@link agentScenarios} entry end to end: an agent process with a child operation process
|
|
268
|
+
* per tool, so the panel has both a populated process tree and a timeline with nested spans.
|
|
269
|
+
*/
|
|
270
|
+
export const runScenario = (
|
|
271
|
+
manager: ProcessManager.Manager,
|
|
272
|
+
index: number,
|
|
273
|
+
environment: Process.Environment,
|
|
274
|
+
): Effect.Effect<void> =>
|
|
275
|
+
Effect.gen(function* () {
|
|
276
|
+
const scenario = agentScenarios[index % agentScenarios.length];
|
|
277
|
+
const agent = yield* manager.spawn(SimulatedAgent, { name: `Agent (${scenario.name})`, environment });
|
|
278
|
+
yield* agent.submitInput({ _tag: 'begin', prompt: scenario.prompt });
|
|
279
|
+
|
|
280
|
+
let failed = false;
|
|
281
|
+
for (const tool of scenario.tools) {
|
|
282
|
+
const toolCallId = EntityId.random();
|
|
283
|
+
yield* agent.submitInput({ _tag: 'toolCall', toolCallId, name: tool.key });
|
|
284
|
+
yield* runTool(manager, tool, agent.pid, environment);
|
|
285
|
+
if (tool.fail) {
|
|
286
|
+
failed = true;
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
yield* agent.submitInput({ _tag: 'toolResult', toolCallId, name: tool.key });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
yield* agent.submitInput({ _tag: 'end', status: failed ? 'error' : 'success' });
|
|
293
|
+
}).pipe(Effect.orDie);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Dispatch, type SetStateAction, useEffect, useRef, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Interval (ms) between auto-played trace messages in the snapshot playback story.
|
|
9
|
+
*/
|
|
10
|
+
export const PLAYBACK_INTERVAL_MS = 250;
|
|
11
|
+
|
|
12
|
+
export const STEP_STORAGE_KEY = 'plugin-assistant.trace-panel.snapshot.step';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `useState<number>` that hydrates from and persists to `localStorage`.
|
|
16
|
+
* The third tuple member is `true` iff a valid value was loaded on first render.
|
|
17
|
+
*/
|
|
18
|
+
export const useLocalStorageNumber = (
|
|
19
|
+
key: string,
|
|
20
|
+
initial: number,
|
|
21
|
+
): [number, Dispatch<SetStateAction<number>>, boolean] => {
|
|
22
|
+
const initRef = useRef<{ value: number; hydrated: boolean } | null>(null);
|
|
23
|
+
if (initRef.current === null) {
|
|
24
|
+
let value = initial;
|
|
25
|
+
let hydrated = false;
|
|
26
|
+
if (typeof window !== 'undefined') {
|
|
27
|
+
const stored = window.localStorage.getItem(key);
|
|
28
|
+
const parsed = stored === null ? Number.NaN : Number(stored);
|
|
29
|
+
if (Number.isInteger(parsed) && parsed >= 0) {
|
|
30
|
+
value = parsed;
|
|
31
|
+
hydrated = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
initRef.current = { value, hydrated };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const [value, setValue] = useState(initRef.current.value);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (typeof window !== 'undefined') {
|
|
40
|
+
window.localStorage.setItem(key, String(value));
|
|
41
|
+
}
|
|
42
|
+
}, [key, value]);
|
|
43
|
+
|
|
44
|
+
return [value, setValue, initRef.current.hydrated];
|
|
45
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Option from 'effect/Option';
|
|
6
|
+
|
|
7
|
+
import * as Process from '@dxos/compute/Process';
|
|
8
|
+
import { Annotation } from '@dxos/echo';
|
|
9
|
+
|
|
10
|
+
export const makeProcess = (
|
|
11
|
+
overrides: Partial<Process.Info> & Pick<Process.Info, 'pid' | 'state'> & { name: string },
|
|
12
|
+
): Process.Info => ({
|
|
13
|
+
parentPid: null,
|
|
14
|
+
key: `test.process.${overrides.name}`,
|
|
15
|
+
params: { name: overrides.name, annotations: Annotation.buildDictionary(() => {}) },
|
|
16
|
+
environment: {},
|
|
17
|
+
error: null,
|
|
18
|
+
startedAt: Date.now() - 10_000,
|
|
19
|
+
completedAt: Option.none(),
|
|
20
|
+
metrics: { wallTime: 0, inputCount: 0, outputCount: 0 },
|
|
21
|
+
...overrides,
|
|
22
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Resource } from '@dxos/react-ui';
|
|
6
|
+
|
|
7
|
+
export const translationKey = '@dxos/react-ui-trace';
|
|
8
|
+
|
|
9
|
+
export const translations = [
|
|
10
|
+
{
|
|
11
|
+
'en-US': {
|
|
12
|
+
[translationKey]: {
|
|
13
|
+
'no-commits.message': 'No events yet',
|
|
14
|
+
|
|
15
|
+
'trace.label': 'Trace',
|
|
16
|
+
'trace-processes.label': 'Processes',
|
|
17
|
+
'trace-details.label': 'Details',
|
|
18
|
+
|
|
19
|
+
'trace-filter.menu': 'Filter processes',
|
|
20
|
+
'trace-clear-selection.label': 'Clear process selection',
|
|
21
|
+
'trace-filter-all.label': 'Show all',
|
|
22
|
+
'trace-filter-none.label': 'Hide all',
|
|
23
|
+
'trace-environment-app.label': 'App',
|
|
24
|
+
'trace-environment-space.label': 'Space',
|
|
25
|
+
'trace-environment-conversation.label': 'Conversation',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
] as const satisfies Resource[];
|