@contractspec/example.agent-console 3.7.7 → 3.8.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 (186) hide show
  1. package/.turbo/turbo-build.log +126 -105
  2. package/AGENTS.md +3 -1
  3. package/CHANGELOG.md +29 -0
  4. package/README.md +46 -9
  5. package/dist/agent/agent.handler.d.ts +3 -0
  6. package/dist/agent/agent.handler.js +730 -1
  7. package/dist/agent/index.js +73 -72
  8. package/dist/agent.feature.js +179 -0
  9. package/dist/browser/agent/agent.handler.js +730 -1
  10. package/dist/browser/agent/index.js +73 -72
  11. package/dist/browser/agent.feature.js +179 -0
  12. package/dist/browser/docs/agent-console.docblock.js +11 -8
  13. package/dist/browser/docs/index.js +11 -8
  14. package/dist/browser/example.js +2 -3
  15. package/dist/browser/handlers/agent.handlers.js +1883 -2
  16. package/dist/browser/handlers/index.js +2142 -8
  17. package/dist/browser/index.js +3347 -2433
  18. package/dist/browser/presentations/index.js +49 -49
  19. package/dist/browser/run/index.js +818 -812
  20. package/dist/browser/run/run.handler.js +666 -1
  21. package/dist/browser/shared/index.js +293 -1
  22. package/dist/browser/shared/mock-runs.js +5 -0
  23. package/dist/browser/tool/index.js +331 -331
  24. package/dist/browser/tool/tool.handler.js +479 -3
  25. package/dist/browser/ui/AgentDashboard.js +1204 -319
  26. package/dist/browser/ui/AgentDashboard.visualizations.js +217 -0
  27. package/dist/browser/ui/AgentRunList.js +359 -127
  28. package/dist/browser/ui/hooks/index.js +468 -18
  29. package/dist/browser/ui/hooks/useAgentMutations.js +443 -8
  30. package/dist/browser/ui/hooks/useRunList.js +25 -10
  31. package/dist/browser/ui/index.js +1293 -390
  32. package/dist/browser/ui/renderers/agent-list.markdown.js +14 -5
  33. package/dist/browser/ui/renderers/dashboard.markdown.js +207 -36
  34. package/dist/browser/ui/renderers/index.js +245 -49
  35. package/dist/browser/ui/renderers/run-list.markdown.js +9 -4
  36. package/dist/browser/ui/renderers/tool-registry.markdown.js +15 -4
  37. package/dist/browser/ui/views/RunDataTable.js +326 -0
  38. package/dist/browser/ui/views/RunListView.js +359 -127
  39. package/dist/browser/ui/views/index.js +406 -174
  40. package/dist/browser/ui/views/run-data-table.columns.js +271 -0
  41. package/dist/browser/ui/views/run-list.shared.js +177 -0
  42. package/dist/browser/visualizations/catalog.js +134 -0
  43. package/dist/browser/visualizations/index.js +187 -0
  44. package/dist/browser/visualizations/selectors.js +181 -0
  45. package/dist/docs/agent-console.docblock.js +11 -8
  46. package/dist/docs/index.js +11 -8
  47. package/dist/example.js +2 -3
  48. package/dist/example.test.d.ts +1 -0
  49. package/dist/handlers/agent.handlers.d.ts +2 -0
  50. package/dist/handlers/agent.handlers.js +1883 -2
  51. package/dist/handlers/index.d.ts +1 -3
  52. package/dist/handlers/index.js +2142 -8
  53. package/dist/handlers/mock-handlers.test.d.ts +1 -0
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.js +3347 -2433
  56. package/dist/node/agent/agent.handler.js +730 -1
  57. package/dist/node/agent/index.js +73 -72
  58. package/dist/node/agent.feature.js +179 -0
  59. package/dist/node/docs/agent-console.docblock.js +11 -8
  60. package/dist/node/docs/index.js +11 -8
  61. package/dist/node/example.js +2 -3
  62. package/dist/node/handlers/agent.handlers.js +1883 -2
  63. package/dist/node/handlers/index.js +2142 -8
  64. package/dist/node/index.js +3347 -2433
  65. package/dist/node/presentations/index.js +49 -49
  66. package/dist/node/run/index.js +818 -812
  67. package/dist/node/run/run.handler.js +666 -1
  68. package/dist/node/shared/index.js +293 -1
  69. package/dist/node/shared/mock-runs.js +5 -0
  70. package/dist/node/tool/index.js +331 -331
  71. package/dist/node/tool/tool.handler.js +479 -3
  72. package/dist/node/ui/AgentDashboard.js +1204 -319
  73. package/dist/node/ui/AgentDashboard.visualizations.js +217 -0
  74. package/dist/node/ui/AgentRunList.js +359 -127
  75. package/dist/node/ui/hooks/index.js +468 -18
  76. package/dist/node/ui/hooks/useAgentMutations.js +443 -8
  77. package/dist/node/ui/hooks/useRunList.js +25 -10
  78. package/dist/node/ui/index.js +1293 -390
  79. package/dist/node/ui/renderers/agent-list.markdown.js +14 -5
  80. package/dist/node/ui/renderers/dashboard.markdown.js +207 -36
  81. package/dist/node/ui/renderers/index.js +245 -49
  82. package/dist/node/ui/renderers/run-list.markdown.js +9 -4
  83. package/dist/node/ui/renderers/tool-registry.markdown.js +15 -4
  84. package/dist/node/ui/views/RunDataTable.js +326 -0
  85. package/dist/node/ui/views/RunListView.js +359 -127
  86. package/dist/node/ui/views/index.js +406 -174
  87. package/dist/node/ui/views/run-data-table.columns.js +271 -0
  88. package/dist/node/ui/views/run-list.shared.js +177 -0
  89. package/dist/node/visualizations/catalog.js +134 -0
  90. package/dist/node/visualizations/index.js +187 -0
  91. package/dist/node/visualizations/selectors.js +181 -0
  92. package/dist/presentations/index.js +49 -49
  93. package/dist/proof/index.d.ts +2 -0
  94. package/dist/proof/meetup-proof.d.ts +10 -0
  95. package/dist/proof/meetup-proof.runtime.d.ts +22 -0
  96. package/dist/proof/meetup-proof.scenario.d.ts +2 -0
  97. package/dist/proof/meetup-proof.suite.d.ts +1 -0
  98. package/dist/proof/meetup-proof.test.d.ts +1 -0
  99. package/dist/run/index.js +818 -812
  100. package/dist/run/run.handler.d.ts +4 -0
  101. package/dist/run/run.handler.js +666 -1
  102. package/dist/shared/demo-dashboard-data.d.ts +16 -0
  103. package/dist/shared/demo-runtime-seed.d.ts +17 -0
  104. package/dist/shared/demo-runtime.d.ts +8 -0
  105. package/dist/shared/demo-runtime.test.d.ts +1 -0
  106. package/dist/shared/index.d.ts +3 -0
  107. package/dist/shared/index.js +293 -1
  108. package/dist/shared/mock-runs.d.ts +4 -0
  109. package/dist/shared/mock-runs.js +5 -0
  110. package/dist/tool/index.js +331 -331
  111. package/dist/tool/tool.handler.d.ts +4 -1
  112. package/dist/tool/tool.handler.js +479 -3
  113. package/dist/ui/AgentDashboard.js +1204 -319
  114. package/dist/ui/AgentDashboard.sandbox.test.d.ts +1 -0
  115. package/dist/ui/AgentDashboard.visualizations.d.ts +4 -0
  116. package/dist/ui/AgentDashboard.visualizations.js +218 -0
  117. package/dist/ui/AgentRunList.js +359 -127
  118. package/dist/ui/hooks/index.js +468 -18
  119. package/dist/ui/hooks/useAgentMutations.js +443 -8
  120. package/dist/ui/hooks/useRunList.d.ts +8 -2
  121. package/dist/ui/hooks/useRunList.js +25 -10
  122. package/dist/ui/index.js +1293 -390
  123. package/dist/ui/renderers/agent-list.markdown.js +14 -5
  124. package/dist/ui/renderers/dashboard.markdown.js +207 -36
  125. package/dist/ui/renderers/index.js +245 -49
  126. package/dist/ui/renderers/run-list.markdown.js +9 -4
  127. package/dist/ui/renderers/tool-registry.markdown.d.ts +1 -1
  128. package/dist/ui/renderers/tool-registry.markdown.js +15 -4
  129. package/dist/ui/views/RunDataTable.d.ts +18 -0
  130. package/dist/ui/views/RunDataTable.js +327 -0
  131. package/dist/ui/views/RunListView.js +359 -127
  132. package/dist/ui/views/index.js +406 -174
  133. package/dist/ui/views/run-data-table.columns.d.ts +3 -0
  134. package/dist/ui/views/run-data-table.columns.js +272 -0
  135. package/dist/ui/views/run-list.shared.d.ts +14 -0
  136. package/dist/ui/views/run-list.shared.js +178 -0
  137. package/dist/visualizations/catalog.d.ts +10 -0
  138. package/dist/visualizations/catalog.js +135 -0
  139. package/dist/visualizations/index.d.ts +2 -0
  140. package/dist/visualizations/index.js +188 -0
  141. package/dist/visualizations/selectors.d.ts +3 -0
  142. package/dist/visualizations/selectors.js +182 -0
  143. package/dist/visualizations/selectors.test.d.ts +1 -0
  144. package/package.json +112 -10
  145. package/proofs/agent-console-meetup.replay.json +220 -0
  146. package/src/agent/agent.handler.ts +18 -1
  147. package/src/agent.feature.ts +3 -0
  148. package/src/docs/agent-console.docblock.ts +11 -8
  149. package/src/example.test.ts +75 -0
  150. package/src/example.ts +2 -3
  151. package/src/handlers/agent.handlers.ts +55 -2
  152. package/src/handlers/index.ts +18 -2
  153. package/src/handlers/mock-handlers.test.ts +77 -0
  154. package/src/index.ts +2 -0
  155. package/src/proof/index.ts +2 -0
  156. package/src/proof/meetup-proof.runtime.ts +196 -0
  157. package/src/proof/meetup-proof.scenario.ts +99 -0
  158. package/src/proof/meetup-proof.suite.ts +29 -0
  159. package/src/proof/meetup-proof.test.ts +28 -0
  160. package/src/proof/meetup-proof.ts +130 -0
  161. package/src/run/run.handler.ts +17 -1
  162. package/src/shared/demo-dashboard-data.ts +58 -0
  163. package/src/shared/demo-runtime-seed.ts +139 -0
  164. package/src/shared/demo-runtime.test.ts +169 -0
  165. package/src/shared/demo-runtime.ts +260 -0
  166. package/src/shared/index.ts +11 -0
  167. package/src/shared/mock-runs.ts +5 -0
  168. package/src/tool/tool.handler.ts +21 -4
  169. package/src/ui/AgentDashboard.sandbox.test.tsx +312 -0
  170. package/src/ui/AgentDashboard.tsx +4 -1
  171. package/src/ui/AgentDashboard.visualizations.tsx +35 -0
  172. package/src/ui/hooks/useAgentMutations.ts +19 -11
  173. package/src/ui/hooks/useRunList.ts +41 -9
  174. package/src/ui/renderers/agent-list.markdown.ts +31 -12
  175. package/src/ui/renderers/dashboard.markdown.ts +37 -42
  176. package/src/ui/renderers/run-list.markdown.ts +16 -8
  177. package/src/ui/renderers/tool-registry.markdown.ts +21 -9
  178. package/src/ui/views/RunDataTable.tsx +74 -0
  179. package/src/ui/views/RunListView.tsx +37 -111
  180. package/src/ui/views/run-data-table.columns.tsx +102 -0
  181. package/src/ui/views/run-list.shared.tsx +139 -0
  182. package/src/visualizations/catalog.ts +132 -0
  183. package/src/visualizations/index.ts +2 -0
  184. package/src/visualizations/selectors.test.ts +12 -0
  185. package/src/visualizations/selectors.ts +70 -0
  186. package/tsdown.config.js +17 -0
@@ -5,12 +5,12 @@
5
5
  */
6
6
 
7
7
  import {
8
- mockListAgentsHandler,
9
- mockListRunsHandler,
10
- mockListToolsHandler,
11
- } from '@contractspec/example.agent-console/handlers';
8
+ type AgentConsoleDashboardData,
9
+ getFallbackAgentConsoleDashboardData,
10
+ } from '@contractspec/example.agent-console/shared';
12
11
  import type { PresentationSpec } from '@contractspec/lib.contracts-spec/presentations';
13
12
  import type { PresentationRenderer } from '@contractspec/lib.contracts-spec/presentations/transform-engine';
13
+ import { createAgentVisualizationItems } from '../../visualizations';
14
14
 
15
15
  function formatDuration(ms: number): string {
16
16
  if (ms < 1000) return `${ms}ms`;
@@ -27,7 +27,7 @@ export const agentDashboardMarkdownRenderer: PresentationRenderer<{
27
27
  body: string;
28
28
  }> = {
29
29
  target: 'markdown',
30
- render: async (desc: PresentationSpec) => {
30
+ render: async (desc: PresentationSpec, ctx) => {
31
31
  // Only handle AgentConsoleDashboard
32
32
  if (
33
33
  desc.source.type !== 'component' ||
@@ -38,42 +38,28 @@ export const agentDashboardMarkdownRenderer: PresentationRenderer<{
38
38
  );
39
39
  }
40
40
 
41
- // Fetch all data in parallel
42
- const [agentsData, runsData, toolsData] = await Promise.all([
43
- mockListAgentsHandler({
44
- organizationId: 'demo-org',
45
- limit: 100,
46
- }),
47
- mockListRunsHandler({
48
- limit: 100,
49
- }),
50
- mockListToolsHandler({
51
- organizationId: 'demo-org',
52
- limit: 100,
53
- }),
54
- ]);
41
+ const data =
42
+ (ctx?.data as AgentConsoleDashboardData | undefined) ??
43
+ (await getFallbackAgentConsoleDashboardData());
55
44
 
56
45
  // Calculate stats
57
- const activeAgents = agentsData.items.filter(
46
+ const activeAgents = data.agents.filter(
58
47
  (a) => a.status === 'ACTIVE'
59
48
  ).length;
60
- const completedRuns = runsData.items.filter(
49
+ const completedRuns = data.runs.filter(
61
50
  (r) => r.status === 'COMPLETED'
62
51
  ).length;
63
- const failedRuns = runsData.items.filter(
64
- (r) => r.status === 'FAILED'
65
- ).length;
66
- const totalTokens = runsData.items.reduce(
52
+ const failedRuns = data.runs.filter((r) => r.status === 'FAILED').length;
53
+ const totalTokens = data.runs.reduce(
67
54
  (sum, r) => sum + (r.totalTokens ?? 0),
68
55
  0
69
56
  );
70
- const totalCost = runsData.items.reduce(
57
+ const totalCost = data.runs.reduce(
71
58
  (sum, r) => sum + (r.estimatedCostUsd ?? 0),
72
59
  0
73
60
  );
74
- const activeTools = toolsData.items.filter(
75
- (t) => t.status === 'ACTIVE'
76
- ).length;
61
+ const activeTools = data.tools.filter((t) => t.status === 'ACTIVE').length;
62
+ const visualizationItems = createAgentVisualizationItems(data.runs);
77
63
 
78
64
  // Build dashboard markdown
79
65
  const lines: string[] = [
@@ -85,14 +71,14 @@ export const agentDashboardMarkdownRenderer: PresentationRenderer<{
85
71
  '',
86
72
  '| Metric | Value |',
87
73
  '|--------|-------|',
88
- `| Total Agents | ${agentsData.total} |`,
74
+ `| Total Agents | ${data.summary.totalAgents} |`,
89
75
  `| Active Agents | ${activeAgents} |`,
90
- `| Total Runs | ${runsData.total} |`,
76
+ `| Total Runs | ${data.summary.totalRuns} |`,
91
77
  `| Completed Runs | ${completedRuns} |`,
92
78
  `| Failed Runs | ${failedRuns} |`,
93
79
  `| Total Tokens | ${totalTokens.toLocaleString()} |`,
94
80
  `| Total Cost | $${totalCost.toFixed(4)} |`,
95
- `| Total Tools | ${toolsData.total} |`,
81
+ `| Total Tools | ${data.summary.totalTools} |`,
96
82
  `| Active Tools | ${activeTools} |`,
97
83
  '',
98
84
  '## Agents',
@@ -100,18 +86,20 @@ export const agentDashboardMarkdownRenderer: PresentationRenderer<{
100
86
  ];
101
87
 
102
88
  // Agent list
103
- if (agentsData.items.length === 0) {
89
+ if (data.agents.length === 0) {
104
90
  lines.push('_No agents configured._');
105
91
  } else {
106
92
  lines.push('| Agent | Model | Status | Description |');
107
93
  lines.push('|-------|-------|--------|-------------|');
108
- for (const agent of agentsData.items.slice(0, 5)) {
94
+ for (const agent of data.agents.slice(0, 5)) {
109
95
  lines.push(
110
96
  `| ${agent.name} | ${agent.modelProvider}/${agent.modelName} | ${agent.status} | ${agent.description ?? '-'} |`
111
97
  );
112
98
  }
113
- if (agentsData.items.length > 5) {
114
- lines.push(`| ... | ... | ... | _${agentsData.total - 5} more_ |`);
99
+ if (data.agents.length > 5) {
100
+ lines.push(
101
+ `| ... | ... | ... | _${data.summary.totalAgents - 5} more_ |`
102
+ );
115
103
  }
116
104
  }
117
105
 
@@ -120,30 +108,37 @@ export const agentDashboardMarkdownRenderer: PresentationRenderer<{
120
108
  lines.push('');
121
109
 
122
110
  // Recent runs
123
- if (runsData.items.length === 0) {
111
+ if (data.runs.length === 0) {
124
112
  lines.push('_No runs yet._');
125
113
  } else {
126
114
  lines.push('| Run ID | Agent | Status | Duration | Tokens | Cost |');
127
115
  lines.push('|--------|-------|--------|----------|--------|------|');
128
- for (const run of runsData.items.slice(0, 5)) {
116
+ for (const run of data.runs.slice(0, 5)) {
129
117
  lines.push(
130
118
  `| ${run.id.slice(-8)} | ${run.agentName} | ${run.status} | ${run.durationMs ? formatDuration(run.durationMs) : '-'} | ${run.totalTokens ?? 0} | $${(run.estimatedCostUsd ?? 0).toFixed(4)} |`
131
119
  );
132
120
  }
133
- if (runsData.items.length > 5) {
121
+ if (data.runs.length > 5) {
134
122
  lines.push(
135
- `| ... | ... | ... | ... | ... | _${runsData.total - 5} more_ |`
123
+ `| ... | ... | ... | ... | ... | _${data.summary.totalRuns - 5} more_ |`
136
124
  );
137
125
  }
138
126
  }
139
127
 
128
+ lines.push('');
129
+ lines.push('## Visualization Overview');
130
+ lines.push('');
131
+ for (const item of visualizationItems) {
132
+ lines.push(`- **${item.title}** via \`${item.spec.meta.key}\``);
133
+ }
134
+
140
135
  lines.push('');
141
136
  lines.push('## Tools');
142
137
  lines.push('');
143
138
 
144
139
  // Tool categories
145
- const toolsByCategory: Record<string, typeof toolsData.items> = {};
146
- for (const tool of toolsData.items) {
140
+ const toolsByCategory: Record<string, typeof data.tools> = {};
141
+ for (const tool of data.tools) {
147
142
  const cat = tool.category;
148
143
  if (!toolsByCategory[cat]) toolsByCategory[cat] = [];
149
144
  toolsByCategory[cat].push(tool);
@@ -4,7 +4,10 @@
4
4
  * Uses dynamic import for handlers to ensure correct build order.
5
5
  */
6
6
 
7
- import { mockListRunsHandler } from '@contractspec/example.agent-console/handlers';
7
+ import {
8
+ AGENT_CONSOLE_DEMO_PROJECT_ID,
9
+ createAgentConsoleDemoHandlers,
10
+ } from '@contractspec/example.agent-console/shared';
8
11
  import type { PresentationSpec } from '@contractspec/lib.contracts-spec/presentations';
9
12
  import type { PresentationRenderer } from '@contractspec/lib.contracts-spec/presentations/transform-engine';
10
13
  import type { Run } from '../hooks/useRunList';
@@ -30,7 +33,7 @@ export const runListMarkdownRenderer: PresentationRenderer<{
30
33
  body: string;
31
34
  }> = {
32
35
  target: 'markdown',
33
- render: async (desc: PresentationSpec) => {
36
+ render: async (desc: PresentationSpec, ctx) => {
34
37
  // Only handle RunListView
35
38
  if (
36
39
  desc.source.type !== 'component' ||
@@ -39,12 +42,17 @@ export const runListMarkdownRenderer: PresentationRenderer<{
39
42
  throw new Error('runListMarkdownRenderer: not RunListView');
40
43
  }
41
44
 
42
- // Fetch data using mock handler
43
- const data = (await mockListRunsHandler({
44
- organizationId: 'demo-org',
45
- limit: 20,
46
- offset: 0,
47
- })) as RunListOutput;
45
+ const data = (
46
+ Array.isArray(ctx?.data)
47
+ ? { items: ctx.data as Run[], total: ctx.data.length, hasMore: false }
48
+ : await createAgentConsoleDemoHandlers({
49
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
50
+ }).listRuns({
51
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
52
+ limit: 20,
53
+ offset: 0,
54
+ })
55
+ ) as RunListOutput;
48
56
 
49
57
  // Generate markdown
50
58
  const lines: string[] = [
@@ -4,7 +4,11 @@
4
4
  * Uses dynamic import for handlers to ensure correct build order.
5
5
  */
6
6
 
7
- import { mockListToolsHandler } from '@contractspec/example.agent-console/handlers';
7
+ import {
8
+ AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
9
+ AGENT_CONSOLE_DEMO_PROJECT_ID,
10
+ createAgentConsoleDemoHandlers,
11
+ } from '@contractspec/example.agent-console/shared';
8
12
  import type { PresentationSpec } from '@contractspec/lib.contracts-spec/presentations';
9
13
  import type { PresentationRenderer } from '@contractspec/lib.contracts-spec/presentations/transform-engine';
10
14
 
@@ -18,7 +22,7 @@ interface ToolItem {
18
22
  }
19
23
 
20
24
  /**
21
- * Markdown renderer for agent-console.tool.registry presentation
25
+ * Markdown renderer for agent-console.tool.list presentation
22
26
  * Only handles ToolRegistryView component
23
27
  */
24
28
  export const toolRegistryMarkdownRenderer: PresentationRenderer<{
@@ -26,7 +30,7 @@ export const toolRegistryMarkdownRenderer: PresentationRenderer<{
26
30
  body: string;
27
31
  }> = {
28
32
  target: 'markdown',
29
- render: async (desc: PresentationSpec) => {
33
+ render: async (desc: PresentationSpec, ctx) => {
30
34
  // Only handle ToolRegistryView
31
35
  if (
32
36
  desc.source.type !== 'component' ||
@@ -35,12 +39,20 @@ export const toolRegistryMarkdownRenderer: PresentationRenderer<{
35
39
  throw new Error('toolRegistryMarkdownRenderer: not ToolRegistryView');
36
40
  }
37
41
 
38
- // Fetch data using mock handler
39
- const data = await mockListToolsHandler({
40
- organizationId: 'demo-org',
41
- limit: 50,
42
- offset: 0,
43
- });
42
+ const data = Array.isArray(ctx?.data)
43
+ ? {
44
+ items: ctx.data as ToolItem[],
45
+ total: ctx.data.length,
46
+ hasMore: false,
47
+ }
48
+ : await createAgentConsoleDemoHandlers({
49
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
50
+ }).listTools({
51
+ projectId: AGENT_CONSOLE_DEMO_PROJECT_ID,
52
+ organizationId: AGENT_CONSOLE_DEMO_ORGANIZATION_ID,
53
+ limit: 50,
54
+ offset: 0,
55
+ });
44
56
 
45
57
  // Generate markdown
46
58
  const lines: string[] = [
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+
3
+ import { DataTable } from '@contractspec/lib.design-system';
4
+ import type { ContractTableSort } from '@contractspec/lib.presentation-runtime-core';
5
+ import { useContractTable } from '@contractspec/lib.presentation-runtime-react';
6
+ import * as React from 'react';
7
+ import type { Run } from '../hooks/useRunList';
8
+ import { createRunTableColumns } from './run-data-table.columns';
9
+ import { RunExpandedContent, RunTableToolbar } from './run-list.shared';
10
+
11
+ interface RunDataTableProps {
12
+ runs: Run[];
13
+ totalItems: number;
14
+ pageIndex: number;
15
+ pageSize: number;
16
+ sorting: ContractTableSort[];
17
+ loading?: boolean;
18
+ onSortingChange: (nextSorting: ContractTableSort[]) => void;
19
+ onPaginationChange: (nextPagination: {
20
+ pageIndex: number;
21
+ pageSize: number;
22
+ }) => void;
23
+ onRunClick?: (runId: string) => void;
24
+ }
25
+
26
+ export function RunDataTable({
27
+ runs,
28
+ totalItems,
29
+ pageIndex,
30
+ pageSize,
31
+ sorting,
32
+ loading,
33
+ onSortingChange,
34
+ onPaginationChange,
35
+ onRunClick,
36
+ }: RunDataTableProps) {
37
+ const columns = React.useMemo(() => createRunTableColumns(), []);
38
+ const controller = useContractTable<Run>({
39
+ data: runs,
40
+ columns,
41
+ executionMode: 'server',
42
+ totalItems,
43
+ state: {
44
+ sorting,
45
+ pagination: { pageIndex, pageSize },
46
+ },
47
+ onSortingChange,
48
+ onPaginationChange,
49
+ initialState: {
50
+ columnVisibility: { estimatedCostUsd: false },
51
+ },
52
+ getRowId: (run) => run.id,
53
+ renderExpandedContent: (run) => <RunExpandedContent run={run} />,
54
+ getCanExpand: () => true,
55
+ });
56
+
57
+ return (
58
+ <DataTable
59
+ controller={controller}
60
+ title="Run History"
61
+ description="Server-mode ContractSpec table with shared pagination, sorting, visibility, and expansion."
62
+ loading={loading}
63
+ onRowPress={(row) => onRunClick?.(row.id)}
64
+ toolbar={
65
+ <RunTableToolbar controller={controller} totalRuns={totalItems} />
66
+ }
67
+ emptyState={
68
+ <div className="rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm">
69
+ No runs yet
70
+ </div>
71
+ }
72
+ />
73
+ );
74
+ }
@@ -1,61 +1,40 @@
1
1
  'use client';
2
2
 
3
- /**
4
- * Run List View - Shows agent execution runs with stats
5
- */
6
3
  import {
7
4
  EmptyState,
8
5
  ErrorState,
9
6
  LoaderBlock,
10
7
  StatCard,
11
8
  StatCardGroup,
12
- StatusChip,
13
9
  } from '@contractspec/lib.design-system';
14
- import { type Run, useRunList } from '../hooks/useRunList';
10
+ /**
11
+ * Run List View - Shows agent execution runs with shared ContractSpec table primitives
12
+ */
13
+ import type { ContractTableSort } from '@contractspec/lib.presentation-runtime-core';
14
+ import { useState } from 'react';
15
+ import { useRunList } from '../hooks/useRunList';
16
+ import { RunDataTable } from './RunDataTable';
17
+ import { formatTokens } from './run-list.shared';
15
18
 
16
19
  interface RunListViewProps {
17
20
  agentId?: string;
18
21
  onRunClick?: (runId: string) => void;
19
22
  }
20
23
 
21
- function getStatusTone(
22
- status: Run['status']
23
- ): 'success' | 'warning' | 'neutral' | 'danger' {
24
- switch (status) {
25
- case 'COMPLETED':
26
- return 'success';
27
- case 'RUNNING':
28
- return 'warning';
29
- case 'QUEUED':
30
- return 'neutral';
31
- case 'FAILED':
32
- case 'CANCELLED':
33
- return 'danger';
34
- default:
35
- return 'neutral';
36
- }
37
- }
38
-
39
- function formatDuration(ms?: number): string {
40
- if (!ms) return '-';
41
- if (ms < 1000) return `${ms}ms`;
42
- if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
43
- return `${(ms / 60000).toFixed(1)}m`;
44
- }
45
-
46
- function formatTokens(tokens: number): string {
47
- if (tokens < 1000) return tokens.toString();
48
- if (tokens < 1000000) return `${(tokens / 1000).toFixed(1)}K`;
49
- return `${(tokens / 1000000).toFixed(2)}M`;
50
- }
51
-
52
- function formatCost(cost?: number): string {
53
- if (!cost) return '-';
54
- return `$${cost.toFixed(4)}`;
55
- }
56
-
57
24
  export function RunListView({ agentId, onRunClick }: RunListViewProps) {
58
- const { data, metrics, loading, error, refetch } = useRunList({ agentId });
25
+ const [sorting, setSorting] = useState<ContractTableSort[]>([
26
+ { id: 'queuedAt', desc: true },
27
+ ]);
28
+ const [pagination, setPagination] = useState({
29
+ pageIndex: 0,
30
+ pageSize: 3,
31
+ });
32
+ const { data, metrics, loading, error, refetch } = useRunList({
33
+ agentId,
34
+ pageIndex: pagination.pageIndex,
35
+ pageSize: pagination.pageSize,
36
+ sorting,
37
+ });
59
38
 
60
39
  if (loading && !data) {
61
40
  return <LoaderBlock label="Loading runs..." />;
@@ -83,8 +62,7 @@ export function RunListView({ agentId, onRunClick }: RunListViewProps) {
83
62
 
84
63
  return (
85
64
  <div className="space-y-6">
86
- {/* Metrics Stats */}
87
- {metrics && (
65
+ {metrics ? (
88
66
  <StatCardGroup>
89
67
  <StatCard label="Total Runs" value={metrics.totalRuns} />
90
68
  <StatCard
@@ -100,74 +78,22 @@ export function RunListView({ agentId, onRunClick }: RunListViewProps) {
100
78
  value={`$${metrics.totalCostUsd.toFixed(2)}`}
101
79
  />
102
80
  </StatCardGroup>
103
- )}
104
-
105
- {/* Run List */}
106
- <div className="rounded-lg border border-border">
107
- <table className="w-full">
108
- <thead className="border-border border-b bg-muted/30">
109
- <tr>
110
- <th className="px-4 py-3 text-left font-medium text-muted-foreground text-sm">
111
- Run
112
- </th>
113
- <th className="px-4 py-3 text-left font-medium text-muted-foreground text-sm">
114
- Agent
115
- </th>
116
- <th className="px-4 py-3 text-left font-medium text-muted-foreground text-sm">
117
- Status
118
- </th>
119
- <th className="px-4 py-3 text-right font-medium text-muted-foreground text-sm">
120
- Tokens
121
- </th>
122
- <th className="px-4 py-3 text-right font-medium text-muted-foreground text-sm">
123
- Duration
124
- </th>
125
- <th className="px-4 py-3 text-right font-medium text-muted-foreground text-sm">
126
- Cost
127
- </th>
128
- </tr>
129
- </thead>
130
- <tbody className="divide-y divide-border">
131
- {data.items.map((run: Run) => (
132
- <tr
133
- key={run.id}
134
- className="cursor-pointer transition-colors hover:bg-muted/50"
135
- onClick={() => onRunClick?.(run.id)}
136
- >
137
- <td className="px-4 py-3">
138
- <div className="font-mono text-sm">{run.id.slice(-8)}</div>
139
- <div className="text-muted-foreground text-xs">
140
- {run.queuedAt.toLocaleString()}
141
- </div>
142
- </td>
143
- <td className="px-4 py-3">
144
- <span className="font-medium">{run.agentName}</span>
145
- </td>
146
- <td className="px-4 py-3">
147
- <StatusChip
148
- tone={getStatusTone(run.status)}
149
- label={run.status}
150
- />
151
- </td>
152
- <td className="px-4 py-3 text-right font-mono text-sm">
153
- {formatTokens(run.totalTokens)}
154
- </td>
155
- <td className="px-4 py-3 text-right font-mono text-sm">
156
- {formatDuration(run.durationMs)}
157
- </td>
158
- <td className="px-4 py-3 text-right font-mono text-sm">
159
- {formatCost(run.estimatedCostUsd)}
160
- </td>
161
- </tr>
162
- ))}
163
- </tbody>
164
- </table>
165
- </div>
81
+ ) : null}
166
82
 
167
- {/* Pagination */}
168
- <div className="text-center text-muted-foreground text-sm">
169
- Showing {data.items.length} of {data.total} runs
170
- </div>
83
+ <RunDataTable
84
+ runs={data.items}
85
+ totalItems={data.total}
86
+ pageIndex={pagination.pageIndex}
87
+ pageSize={pagination.pageSize}
88
+ sorting={sorting}
89
+ loading={loading}
90
+ onSortingChange={(nextSorting) => {
91
+ setSorting(nextSorting);
92
+ setPagination((current) => ({ ...current, pageIndex: 0 }));
93
+ }}
94
+ onPaginationChange={setPagination}
95
+ onRunClick={onRunClick}
96
+ />
171
97
  </div>
172
98
  );
173
99
  }
@@ -0,0 +1,102 @@
1
+ 'use client';
2
+
3
+ import { StatusChip } from '@contractspec/lib.design-system';
4
+ import type { ContractTableColumnDef } from '@contractspec/lib.presentation-runtime-react';
5
+ import { VStack } from '@contractspec/lib.ui-kit-web/ui/stack';
6
+ import { Text } from '@contractspec/lib.ui-kit-web/ui/text';
7
+ import type { Run } from '../hooks/useRunList';
8
+ import {
9
+ formatCost,
10
+ formatDuration,
11
+ formatTokens,
12
+ getStatusTone,
13
+ } from './run-list.shared';
14
+
15
+ export function createRunTableColumns(): readonly ContractTableColumnDef<Run>[] {
16
+ return [
17
+ {
18
+ id: 'queuedAt',
19
+ header: 'Run',
20
+ label: 'Run',
21
+ accessor: (run: Run) => run.queuedAt.getTime(),
22
+ cell: ({ item }) => (
23
+ <VStack gap="xs">
24
+ <Text className="font-mono text-sm">{item.id.slice(-8)}</Text>
25
+ <Text className="text-muted-foreground text-xs">
26
+ {item.queuedAt.toLocaleString()}
27
+ </Text>
28
+ </VStack>
29
+ ),
30
+ size: 220,
31
+ minSize: 180,
32
+ canSort: true,
33
+ canHide: true,
34
+ canResize: true,
35
+ },
36
+ {
37
+ id: 'agentName',
38
+ header: 'Agent',
39
+ label: 'Agent',
40
+ accessor: (run: Run) => run.agentName ?? 'Unknown Agent',
41
+ cell: ({ value }) => (
42
+ <Text className="font-medium">
43
+ {typeof value === 'string' ? value : 'Unknown Agent'}
44
+ </Text>
45
+ ),
46
+ size: 220,
47
+ canSort: true,
48
+ canResize: true,
49
+ },
50
+ {
51
+ id: 'status',
52
+ header: 'Status',
53
+ label: 'Status',
54
+ accessorKey: 'status',
55
+ cell: ({ value }) => {
56
+ const status =
57
+ typeof value === 'string' ? (value as Run['status']) : 'QUEUED';
58
+ return <StatusChip tone={getStatusTone(status)} label={status} />;
59
+ },
60
+ size: 150,
61
+ canSort: true,
62
+ canResize: true,
63
+ },
64
+ {
65
+ id: 'totalTokens',
66
+ header: 'Tokens',
67
+ label: 'Tokens',
68
+ accessorKey: 'totalTokens',
69
+ cell: ({ value }) => formatTokens(Number(value ?? 0)),
70
+ align: 'right' as const,
71
+ size: 140,
72
+ canSort: true,
73
+ canResize: true,
74
+ },
75
+ {
76
+ id: 'durationMs',
77
+ header: 'Duration',
78
+ label: 'Duration',
79
+ accessorKey: 'durationMs',
80
+ cell: ({ value }) =>
81
+ formatDuration(typeof value === 'number' ? value : undefined),
82
+ align: 'right' as const,
83
+ size: 140,
84
+ canSort: true,
85
+ canHide: true,
86
+ canResize: true,
87
+ },
88
+ {
89
+ id: 'estimatedCostUsd',
90
+ header: 'Cost',
91
+ label: 'Cost',
92
+ accessorKey: 'estimatedCostUsd',
93
+ cell: ({ value }) =>
94
+ formatCost(typeof value === 'number' ? value : undefined),
95
+ align: 'right' as const,
96
+ size: 140,
97
+ canSort: true,
98
+ canHide: true,
99
+ canResize: true,
100
+ },
101
+ ];
102
+ }