inspect-ai 0.3.55__py3-none-any.whl → 0.3.56__py3-none-any.whl

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 (95) hide show
  1. inspect_ai/__init__.py +1 -0
  2. inspect_ai/_cli/common.py +1 -1
  3. inspect_ai/_cli/trace.py +33 -20
  4. inspect_ai/_display/core/active.py +1 -1
  5. inspect_ai/_display/core/display.py +1 -1
  6. inspect_ai/_display/core/footer.py +1 -1
  7. inspect_ai/_display/core/progress.py +0 -6
  8. inspect_ai/_display/core/rich.py +1 -1
  9. inspect_ai/_display/rich/display.py +2 -2
  10. inspect_ai/_display/textual/app.py +15 -17
  11. inspect_ai/_display/textual/widgets/clock.py +3 -3
  12. inspect_ai/_display/textual/widgets/samples.py +6 -13
  13. inspect_ai/_eval/context.py +9 -1
  14. inspect_ai/_eval/score.py +4 -10
  15. inspect_ai/_eval/task/results.py +5 -4
  16. inspect_ai/_eval/task/run.py +6 -12
  17. inspect_ai/_eval/task/task.py +10 -0
  18. inspect_ai/_util/ansi.py +31 -0
  19. inspect_ai/_util/format.py +7 -0
  20. inspect_ai/_util/logger.py +12 -12
  21. inspect_ai/_util/throttle.py +10 -1
  22. inspect_ai/_util/trace.py +43 -47
  23. inspect_ai/_util/transcript.py +4 -0
  24. inspect_ai/_util/vscode.py +51 -0
  25. inspect_ai/_view/notify.py +2 -1
  26. inspect_ai/_view/www/App.css +22 -1
  27. inspect_ai/_view/www/dist/assets/index.css +2374 -2
  28. inspect_ai/_view/www/dist/assets/index.js +29622 -24424
  29. inspect_ai/_view/www/log-schema.json +138 -90
  30. inspect_ai/_view/www/package.json +1 -0
  31. inspect_ai/_view/www/src/App.mjs +1 -0
  32. inspect_ai/_view/www/src/appearance/Icons.mjs +2 -0
  33. inspect_ai/_view/www/src/components/AsciiCinemaPlayer.mjs +74 -0
  34. inspect_ai/_view/www/src/components/CopyButton.mjs +0 -1
  35. inspect_ai/_view/www/src/components/HumanBaselineView.mjs +168 -0
  36. inspect_ai/_view/www/src/components/LightboxCarousel.mjs +217 -0
  37. inspect_ai/_view/www/src/components/Tools.mjs +11 -3
  38. inspect_ai/_view/www/src/samples/transcript/ModelEventView.mjs +3 -2
  39. inspect_ai/_view/www/src/samples/transcript/TranscriptView.mjs +1 -0
  40. inspect_ai/_view/www/src/samples/transcript/state/StateEventRenderers.mjs +56 -0
  41. inspect_ai/_view/www/src/samples/transcript/state/StateEventView.mjs +17 -5
  42. inspect_ai/_view/www/src/types/asciicinema-player.d.ts +26 -0
  43. inspect_ai/_view/www/src/types/log.d.ts +26 -12
  44. inspect_ai/_view/www/yarn.lock +44 -0
  45. inspect_ai/approval/_apply.py +4 -0
  46. inspect_ai/approval/_human/panel.py +5 -8
  47. inspect_ai/dataset/_dataset.py +51 -10
  48. inspect_ai/dataset/_util.py +31 -3
  49. inspect_ai/log/__init__.py +2 -0
  50. inspect_ai/log/_log.py +5 -2
  51. inspect_ai/model/_call_tools.py +4 -2
  52. inspect_ai/model/_chat_message.py +3 -0
  53. inspect_ai/model/_model.py +42 -1
  54. inspect_ai/model/_providers/anthropic.py +4 -0
  55. inspect_ai/model/_render.py +9 -2
  56. inspect_ai/scorer/_metric.py +12 -1
  57. inspect_ai/solver/__init__.py +2 -0
  58. inspect_ai/solver/_human_agent/agent.py +83 -0
  59. inspect_ai/solver/_human_agent/commands/__init__.py +36 -0
  60. inspect_ai/solver/_human_agent/commands/clock.py +70 -0
  61. inspect_ai/solver/_human_agent/commands/command.py +59 -0
  62. inspect_ai/solver/_human_agent/commands/instructions.py +74 -0
  63. inspect_ai/solver/_human_agent/commands/note.py +42 -0
  64. inspect_ai/solver/_human_agent/commands/score.py +80 -0
  65. inspect_ai/solver/_human_agent/commands/status.py +62 -0
  66. inspect_ai/solver/_human_agent/commands/submit.py +151 -0
  67. inspect_ai/solver/_human_agent/install.py +222 -0
  68. inspect_ai/solver/_human_agent/panel.py +252 -0
  69. inspect_ai/solver/_human_agent/service.py +45 -0
  70. inspect_ai/solver/_human_agent/state.py +55 -0
  71. inspect_ai/solver/_human_agent/view.py +24 -0
  72. inspect_ai/solver/_task_state.py +28 -2
  73. inspect_ai/tool/_tool.py +10 -2
  74. inspect_ai/tool/_tools/_web_browser/_web_browser.py +13 -10
  75. inspect_ai/util/__init__.py +8 -4
  76. inspect_ai/{_util/display.py → util/_display.py} +6 -0
  77. inspect_ai/util/_panel.py +31 -9
  78. inspect_ai/util/_sandbox/__init__.py +0 -3
  79. inspect_ai/util/_sandbox/context.py +5 -1
  80. inspect_ai/util/_sandbox/docker/compose.py +16 -10
  81. inspect_ai/util/_sandbox/docker/docker.py +9 -6
  82. inspect_ai/util/_sandbox/docker/internal.py +1 -1
  83. inspect_ai/util/_sandbox/docker/util.py +2 -2
  84. inspect_ai/util/_sandbox/environment.py +6 -5
  85. inspect_ai/util/_sandbox/local.py +1 -1
  86. inspect_ai/util/_sandbox/service.py +22 -7
  87. inspect_ai/util/_store.py +5 -6
  88. inspect_ai/util/_store_model.py +110 -0
  89. inspect_ai/util/_throttle.py +32 -0
  90. {inspect_ai-0.3.55.dist-info → inspect_ai-0.3.56.dist-info}/METADATA +1 -1
  91. {inspect_ai-0.3.55.dist-info → inspect_ai-0.3.56.dist-info}/RECORD +95 -73
  92. {inspect_ai-0.3.55.dist-info → inspect_ai-0.3.56.dist-info}/LICENSE +0 -0
  93. {inspect_ai-0.3.55.dist-info → inspect_ai-0.3.56.dist-info}/WHEEL +0 -0
  94. {inspect_ai-0.3.55.dist-info → inspect_ai-0.3.56.dist-info}/entry_points.txt +0 -0
  95. {inspect_ai-0.3.55.dist-info → inspect_ai-0.3.56.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,168 @@
1
+ // @ts-check
2
+ import { html } from "htm/preact";
3
+ import { useEffect } from "preact/hooks";
4
+ import { formatDateTime, formatTime } from "../utils/Format.mjs";
5
+ import { AsciiCinemaPlayer } from "./AsciiCinemaPlayer.mjs";
6
+ import { TextStyle } from "../appearance/Fonts.mjs";
7
+ import { LightboxCarousel } from "./LightboxCarousel.mjs";
8
+
9
+ /**
10
+ * @typedef {Object} SessionLog
11
+ * @property {string} name - The name of this session
12
+ * @property {string} user - The user for this session
13
+ * @property {string} input - The input for this session
14
+ * @property {string} output - The output for this session
15
+ * @property {string} timing - The timing for this session
16
+ */
17
+
18
+ /**
19
+ * Renders the HumanBaselineView component.
20
+ *
21
+ * @param {Object} props - The properties passed to the component.
22
+ * @param {Date} props.started - When the baselining started
23
+ * @param {boolean} props.running - Whether the baselining is running
24
+ * @param {boolean} [props.completed] - Whether the baselining was completed
25
+ * @param {number} [props.runtime] - Duration of baselining in seconds
26
+ * @param {string} [props.answer] - The answer for the baselining
27
+ * @param {SessionLog[]} [props.sessionLogs] - The session logs for the baselining
28
+ * @returns {import("preact").JSX.Element} The component.
29
+ */
30
+ export const HumanBaselineView = ({
31
+ started,
32
+ runtime,
33
+ answer,
34
+ completed,
35
+ running,
36
+ sessionLogs,
37
+ }) => {
38
+ const player_fns = [];
39
+
40
+ // handle creation and revoking of these URLs
41
+ const revokableUrls = [];
42
+ const revokableUrl = (data) => {
43
+ const blob = new Blob([data], { type: "text/plain" });
44
+ const url = URL.createObjectURL(blob);
45
+ revokableUrls.push(url);
46
+ return url;
47
+ };
48
+
49
+ useEffect(() => {
50
+ return () => {
51
+ revokableUrls.forEach((url) => URL.revokeObjectURL(url));
52
+ };
53
+ }, []);
54
+
55
+ // Make a player for each session log
56
+ let count = 1;
57
+ let maxCols = 0;
58
+
59
+ for (const sessionLog of sessionLogs) {
60
+ const rows = extractSize(sessionLog.output, "LINES");
61
+ const cols = extractSize(sessionLog.output, "COLUMNS");
62
+ maxCols = Math.max(maxCols, parseInt(cols));
63
+
64
+ const currentCount = count;
65
+ const title =
66
+ sessionLogs.length === 1
67
+ ? "Terminal Session"
68
+ : `Terminal Session ${currentCount}`;
69
+
70
+ player_fns.push({
71
+ label: title,
72
+ render: () => html`
73
+ <${AsciiCinemaPlayer}
74
+ id=${`player-${currentCount}`}
75
+ inputUrl=${revokableUrl(sessionLog.input)}
76
+ outputUrl=${revokableUrl(sessionLog.output)}
77
+ timingUrl=${revokableUrl(sessionLog.timing)}
78
+ rows=${rows}
79
+ cols=${cols}
80
+ style=${{
81
+ maxHeight: "100vh",
82
+ maxWidth: "100vw",
83
+ height: `${parseInt(rows) * 2}em`,
84
+ width: `${parseInt(cols) * 2}em`,
85
+ }}
86
+ fit="both"
87
+ />
88
+ `,
89
+ });
90
+ count += 1;
91
+ }
92
+
93
+ const StatusMessage = ({ completed, running, answer }) => {
94
+ if (running) {
95
+ return html`<span style=${{ ...TextStyle.label }}>Running</span>`;
96
+ } else if (completed) {
97
+ return html`<span style=${{ ...TextStyle.label, marginRight: "0.5em" }}
98
+ >Answer</span
99
+ ><span>${answer}</span>`;
100
+ } else {
101
+ return "Unknown status";
102
+ }
103
+ };
104
+
105
+ return html`<div style=${{ display: "flex", justifyContent: "center" }}>
106
+ <div
107
+ style=${{
108
+ display: "grid",
109
+ gridTemplateColumns: "1fr 1fr 1fr",
110
+ gridTemplateRows: "auto auto",
111
+ width: "100%",
112
+ }}
113
+ >
114
+ <div
115
+ style=${{
116
+ justifySelf: "start",
117
+ ...TextStyle.label,
118
+ }}
119
+ >
120
+ ${started ? formatDateTime(started) : ""}${runtime
121
+ ? ` (${formatTime(Math.floor(runtime))})`
122
+ : ""}
123
+ </div>
124
+ <div
125
+ style=${{
126
+ justifySelf: "center",
127
+ ...TextStyle.label,
128
+ }}
129
+ ></div>
130
+ <div
131
+ style=${{
132
+ justifySelf: "end",
133
+ }}
134
+ >
135
+ <${StatusMessage}
136
+ completed=${completed}
137
+ running=${running}
138
+ answer=${answer}
139
+ />
140
+ </div>
141
+ <div
142
+ style=${{
143
+ gridColumn: "span 3",
144
+ width: "100%",
145
+ }}
146
+ >
147
+ <${LightboxCarousel} slides=${player_fns} />
148
+ </div>
149
+ </div>
150
+ </div>`;
151
+ };
152
+
153
+ /**
154
+ * Extracts a numeric size value from a string based on a given label.
155
+ *
156
+ * Searches the input string for a pattern matching the format `LABEL="VALUE"`,
157
+ * where `LABEL` is the provided label and `VALUE` is a numeric value.
158
+ *
159
+ * @param {string} value - The input string to search within.
160
+ * @param {string} label - The label to look for in the string.
161
+ * @returns {string | undefined} The extracted size as a string if found, otherwise `undefined`.
162
+ */
163
+ const extractSize = (value, label) => {
164
+ const regex = new RegExp(`${label}="(\\d+)"`);
165
+ const match = value.match(regex);
166
+ const size = match ? match[1] : undefined;
167
+ return size;
168
+ };
@@ -0,0 +1,217 @@
1
+ import { html } from "htm/preact";
2
+ import { useState, useCallback, useEffect } from "preact/hooks";
3
+ import { ApplicationIcons } from "../appearance/Icons.mjs";
4
+
5
+ /**
6
+ * @typedef {Object} Slide
7
+ * @property {string} label - The label for the slide.
8
+ * @property {() => import("preact").JSX.Element} render - A function that returns another function to render the slide as a JSX element.
9
+ */
10
+
11
+ /**
12
+ * LightboxCarousel component provides a carousel with lightbox functionality.
13
+ * @param {Object} props - Component properties.
14
+ * @property {Array<Slide>} props.slides - Array of slide render functions.
15
+ * @returns {import("preact").JSX.Element} LightboxCarousel component.
16
+ */
17
+ export const LightboxCarousel = ({ slides }) => {
18
+ const [isOpen, setIsOpen] = useState(false);
19
+ const [showOverlay, setShowOverlay] = useState(false);
20
+ const [currentIndex, setCurrentIndex] = useState(0);
21
+
22
+ const openLightbox = (index) => {
23
+ setCurrentIndex(index);
24
+ setShowOverlay(true);
25
+
26
+ // Slight delay before setting isOpen so the fade-in starts from opacity: 0
27
+ setTimeout(() => setIsOpen(true), 10);
28
+ };
29
+
30
+ const closeLightbox = () => {
31
+ setIsOpen(false);
32
+ };
33
+
34
+ // Remove the overlay from the DOM after fade-out completes
35
+ useEffect(() => {
36
+ if (!isOpen && showOverlay) {
37
+ const timer = setTimeout(() => {
38
+ setShowOverlay(false);
39
+ }, 300); // match your transition duration
40
+ return () => clearTimeout(timer);
41
+ }
42
+ }, [isOpen, showOverlay]);
43
+
44
+ const showNext = useCallback(() => {
45
+ setCurrentIndex((prev) => {
46
+ return (prev + 1) % slides.length;
47
+ });
48
+ }, [slides]);
49
+
50
+ const showPrev = useCallback(() => {
51
+ setCurrentIndex((prev) => (prev - 1 + slides.length) % slides.length);
52
+ }, [slides]);
53
+
54
+ // Keyboard Navigation
55
+ useEffect(() => {
56
+ if (!isOpen) return;
57
+ const handleKeyUp = (e) => {
58
+ if (e.key === "Escape") {
59
+ closeLightbox();
60
+ } else if (e.key === "ArrowRight") {
61
+ showNext();
62
+ } else if (e.key === "ArrowLeft") {
63
+ showPrev();
64
+ }
65
+ e.preventDefault();
66
+ e.stopPropagation();
67
+ };
68
+ window.addEventListener("keyup", handleKeyUp, true);
69
+ return () => window.removeEventListener("keyup", handleKeyUp);
70
+ }, [isOpen, showNext, showPrev]);
71
+
72
+ // Common button style
73
+ const buttonStyle = {
74
+ position: "absolute",
75
+ top: "50%",
76
+ transform: "translateY(-50%)",
77
+ background: "none",
78
+ color: "#fff",
79
+ border: "none",
80
+ padding: "0.5em",
81
+ fontSize: "3em",
82
+ cursor: "pointer",
83
+ zIndex: "9999",
84
+ };
85
+
86
+ const prevButtonStyle = {
87
+ ...buttonStyle,
88
+ left: "10px",
89
+ };
90
+
91
+ const nextButtonStyle = {
92
+ ...buttonStyle,
93
+ right: "10px",
94
+ };
95
+
96
+ // Overlay style (fixed to fill the screen, flex for centering)
97
+ const overlayStyle = {
98
+ position: "fixed",
99
+ top: 0,
100
+ left: 0,
101
+ width: "100vw",
102
+ height: "100vh",
103
+ background: "rgba(0,0,0,0.8)",
104
+ display: "flex",
105
+ alignItems: "center",
106
+ justifyContent: "center",
107
+ opacity: isOpen ? "1" : "0",
108
+ visibility: isOpen ? "visible" : "hidden",
109
+ transition: "opacity 0.3s ease, visibility 0.3s ease",
110
+ zIndex: 9998,
111
+ };
112
+
113
+ // Close button style
114
+ const closeButtonWrapperStyle = {
115
+ position: "absolute",
116
+ top: "10px",
117
+ right: "10px",
118
+ };
119
+
120
+ const closeButtonStyle = {
121
+ border: "none",
122
+ background: "none",
123
+ color: "#fff",
124
+ fontSize: "3em",
125
+ fontWeight: "500",
126
+ cursor: "pointer",
127
+ paddingLeft: "1em",
128
+ paddingBottom: "1em",
129
+ zIndex: "10000",
130
+ };
131
+
132
+ // Lightbox content container style
133
+ const contentStyle = {
134
+ maxWidth: "90%",
135
+ maxHeight: "90%",
136
+ display: "flex",
137
+ alignItems: "center",
138
+ justifyContent: "center",
139
+ position: "relative",
140
+ // fade in/out
141
+ opacity: isOpen ? "1" : "0",
142
+ visibility: isOpen ? "visible" : "hidden",
143
+ transition: "opacity 0.3s ease, visibility 0.3s ease",
144
+ zIndex: 9999,
145
+ };
146
+
147
+ return html`
148
+ <div className="lightbox-carousel-container">
149
+ <!-- Thumbnails -->
150
+ <div
151
+ className="carousel-thumbs"
152
+ style=${{
153
+ display: "grid",
154
+ gridTemplateColumns: "auto auto auto auto",
155
+ }}
156
+ >
157
+ ${slides.map((slide, index) => {
158
+ return html`
159
+ <div
160
+ key=${index}
161
+ className="carousel-thumb"
162
+ onClick=${() => openLightbox(index)}
163
+ style=${{
164
+ background: "black",
165
+ color: "white",
166
+ padding: "4em 0",
167
+ border: "0",
168
+ margin: "5px",
169
+ cursor: "pointer",
170
+ textAlign: "center",
171
+ }}
172
+ >
173
+ <div>${slide.label}</div>
174
+ <div>
175
+ <i
176
+ class=${ApplicationIcons.play}
177
+ style=${{ fontSize: "4em" }}
178
+ ></i>
179
+ </div>
180
+ </div>
181
+ `;
182
+ })}
183
+ </div>
184
+
185
+ <!-- Lightbox Overlay -->
186
+ ${showOverlay &&
187
+ html`
188
+ <div className="lightbox-overlay" style=${overlayStyle}>
189
+ <div style=${closeButtonWrapperStyle}>
190
+ <button style=${closeButtonStyle} onClick=${closeLightbox}>
191
+ <i class=${ApplicationIcons.close}></i>
192
+ </button>
193
+ </div>
194
+
195
+ ${slides.length > 1
196
+ ? html` <button style=${prevButtonStyle} onClick=${showPrev}>
197
+ <i class=${ApplicationIcons.previous}></i>
198
+ </button>`
199
+ : ""}
200
+ ${slides.length > 1
201
+ ? html` <button style=${nextButtonStyle} onClick=${showNext}>
202
+ <i class=${ApplicationIcons.next}></i>
203
+ </button>`
204
+ : ""}
205
+
206
+ <div
207
+ key=${`carousel-slide-${currentIndex}`}
208
+ className="lightbox-content"
209
+ style=${contentStyle}
210
+ >
211
+ ${slides[currentIndex].render()}
212
+ </div>
213
+ </div>
214
+ `}
215
+ </div>
216
+ `;
217
+ };
@@ -51,7 +51,7 @@ export const resolveToolInput = (fn, toolArgs) => {
51
51
  * @param {string | undefined } props.input - The main input for this call
52
52
  * @param {string | undefined } props.inputType - The input type for this call
53
53
  * @param {import("../types/log").ToolCallContent} props.view - The tool call view
54
- * @param {string | number | boolean | (import("../types/log").ContentText | import("../types/log").ContentImage)[]} props.output - The tool output
54
+ * @param {string | number | boolean | import("../types/log").ContentText | import("../types/log").ContentImage | (import("../types/log").ContentText | import("../types/log").ContentImage)[]} props.output - The tool output
55
55
  * @param { "compact" | undefined } props.mode - The display mode for this call
56
56
  * @returns {import("preact").JSX.Element} The SampleTranscript component.
57
57
  */
@@ -63,6 +63,14 @@ export const ToolCallView = ({
63
63
  output,
64
64
  mode,
65
65
  }) => {
66
+ // don't collapse if output includes an image
67
+ function isContentImage(value) {
68
+ return value && typeof value === "object" && value.type === "image";
69
+ }
70
+ const collapse = Array.isArray(output)
71
+ ? output.every((item) => !isContentImage(item))
72
+ : !isContentImage(output);
73
+
66
74
  return html`<div>
67
75
  ${mode !== "compact" && (!view || view.title)
68
76
  ? html`<${ToolTitle} title=${view?.title || functionCall} />`
@@ -77,7 +85,7 @@ export const ToolCallView = ({
77
85
  />
78
86
  ${output
79
87
  ? html`
80
- <${ExpandablePanel} collapse=${true} border=${true} lines=${15}>
88
+ <${ExpandablePanel} collapse=${collapse} border=${true} lines=${15}>
81
89
  <${MessageContent} contents=${normalizeContent(output)} />
82
90
  </${ExpandablePanel}>`
83
91
  : ""}
@@ -107,7 +115,7 @@ const ToolTitle = ({ title }) => {
107
115
  /**
108
116
  * Renders the ToolCallView component.
109
117
  *
110
- * @param {string | number | boolean | (import("../types/log").ContentText | import("../types/log").ContentImage)[]} output - The tool output
118
+ * @param {string | number | boolean | import("../types/log").ContentImage | import("../types/log").ContentText | (import("../types/log").ContentText | import("../types/log").ContentImage)[]} output - The tool output
111
119
  * @returns {(import("../Types.mjs").ContentTool | import("../types/log").ContentText | import("../types/log").ContentImage)[]} The SampleTranscript component.
112
120
  */
113
121
  const normalizeContent = (output) => {
@@ -60,10 +60,11 @@ export const ModelEventView = ({ id, event, style }) => {
60
60
  };
61
61
 
62
62
  // For any user messages which immediately preceded this model call, including a
63
- // panel and display those user messages
63
+ // panel and display those user messages (exclude tool_call messages as they
64
+ // are already shown in the tool call above)
64
65
  const userMessages = [];
65
66
  for (const msg of event.input.slice().reverse()) {
66
- if (msg.role === "user") {
67
+ if (msg.role === "user" && !msg.tool_call_id) {
67
68
  userMessages.push(msg);
68
69
  } else {
69
70
  break;
@@ -155,6 +155,7 @@ export const RenderedEventNode = ({ id, node, style }) => {
155
155
  id=${id}
156
156
  event=${node.event}
157
157
  style=${style}
158
+ isStore=${true}
158
159
  />`;
159
160
 
160
161
  case "subtask":
@@ -2,6 +2,7 @@
2
2
  import { html } from "htm/preact";
3
3
  import { ChatView } from "../../../components/ChatView.mjs";
4
4
  import { FontSize, TextStyle } from "../../../appearance/Fonts.mjs";
5
+ import { HumanBaselineView } from "../../../components/HumanBaselineView.mjs";
5
6
 
6
7
  /**
7
8
  * @typedef {Object} Signature
@@ -62,6 +63,58 @@ const add_tools = {
62
63
  },
63
64
  };
64
65
 
66
+ const humanAgentKey = (key) => {
67
+ return `HumanAgentState:${key}`;
68
+ };
69
+ const human_baseline_session = {
70
+ type: "human_baseline_session",
71
+ signature: {
72
+ add: ["HumanAgentState:logs"],
73
+ replace: [],
74
+ remove: [],
75
+ },
76
+ render: (changes, resolvedState) => {
77
+ // Read the session values
78
+ const started = resolvedState[humanAgentKey("started_running")];
79
+ const runtime = resolvedState[humanAgentKey("accumulated_time")];
80
+ const answer = resolvedState[humanAgentKey("answer")];
81
+ const completed = !!answer;
82
+ const running = resolvedState[humanAgentKey("running_state")];
83
+ const rawSessions = resolvedState[humanAgentKey("logs")];
84
+
85
+ // Tweak the date value
86
+ const startedDate = started ? new Date(started * 1000) : undefined;
87
+
88
+ // Convert raw sessions into session logs
89
+ const sessions = {};
90
+ if (rawSessions) {
91
+ for (const key of Object.keys(rawSessions)) {
92
+ const value = rawSessions[key];
93
+ // this pulls the key apart into
94
+ // <user>_<timestamp>.<type>
95
+ const match = key.match(/(.*)_(\d+_\d+)\.(.*)/);
96
+ if (match) {
97
+ const user = match[1];
98
+ const timestamp = match[2];
99
+ const type = match[3];
100
+ sessions[timestamp] = sessions[timestamp] || {};
101
+ sessions[timestamp][type] = value;
102
+ sessions[timestamp]["user"] = user;
103
+ }
104
+ }
105
+ }
106
+
107
+ return html`<${HumanBaselineView}
108
+ started=${startedDate}
109
+ running=${running}
110
+ completed=${completed}
111
+ answer=${answer}
112
+ runtime=${runtime}
113
+ sessionLogs=${Object.values(sessions)}
114
+ />`;
115
+ },
116
+ };
117
+
65
118
  const renderTools = (changes, resolvedState) => {
66
119
  // Find which tools were added in this change
67
120
  const toolIndexes = [];
@@ -136,6 +189,9 @@ export const RenderableChangeTypes = [
136
189
  add_tools,
137
190
  ];
138
191
 
192
+ /** @type {ChangeType[]} */
193
+ export const StoreSpecificRenderableTypes = [human_baseline_session];
194
+
139
195
  /**
140
196
  * @typedef {Object} ToolParameters
141
197
  * @property {string} type - The type of the parameters object, typically "object".
@@ -2,7 +2,10 @@
2
2
  import { html } from "htm/preact";
3
3
 
4
4
  import { EventPanel } from "../EventPanel.mjs";
5
- import { RenderableChangeTypes } from "./StateEventRenderers.mjs";
5
+ import {
6
+ RenderableChangeTypes,
7
+ StoreSpecificRenderableTypes,
8
+ } from "./StateEventRenderers.mjs";
6
9
  import { StateDiffView } from "./StateDiffView.mjs";
7
10
  import { formatDateTime } from "../../../utils/Format.mjs";
8
11
 
@@ -12,10 +15,11 @@ import { formatDateTime } from "../../../utils/Format.mjs";
12
15
  * @param {Object} props - The properties passed to the component.
13
16
  * @param { string } props.id - The id of this event.
14
17
  * @param {import("../../../types/log").StateEvent } props.event - The event object to display.
18
+ * @param { boolean } props.isStore - Whether this event view is rendering a storage (rather than a state)
15
19
  * @param { Object } props.style - The style of this event.
16
20
  * @returns {import("preact").JSX.Element} The component.
17
21
  */
18
- export const StateEventView = ({ id, event, style }) => {
22
+ export const StateEventView = ({ id, event, isStore, style }) => {
19
23
  const summary = summarizeChanges(event.changes);
20
24
 
21
25
  // Synthesize objects for comparison
@@ -32,7 +36,11 @@ export const StateEventView = ({ id, event, style }) => {
32
36
  // This clone is important since the state is used by preact as potential values that are rendered
33
37
  // and as a result may be decorated with additional properties, etc..., resulting in DOM elements
34
38
  // appearing attached to state.
35
- const changePreview = generatePreview(event.changes, structuredClone(after));
39
+ const changePreview = generatePreview(
40
+ event.changes,
41
+ structuredClone(after),
42
+ isStore,
43
+ );
36
44
  if (changePreview) {
37
45
  tabs.unshift(
38
46
  html`<div name="Summary" style=${{ margin: "1em 0em", width: "100%" }}>
@@ -55,11 +63,15 @@ export const StateEventView = ({ id, event, style }) => {
55
63
  *
56
64
  * @param {import("../../../types/log").JsonChange[]} changes - The change object containing the value.
57
65
  * @param {Object} resolvedState - The change object containing the value.
66
+ * @param {boolean} isStore - Is this rendering a store event
58
67
  * @returns {import("preact").JSX.Element|Object|string|undefined} - The rendered HTML template if the value is an object with content and source, otherwise the value itself.
59
68
  */
60
- const generatePreview = (changes, resolvedState) => {
69
+ const generatePreview = (changes, resolvedState, isStore) => {
61
70
  const results = [];
62
- for (const changeType of RenderableChangeTypes) {
71
+ for (const changeType of [
72
+ ...RenderableChangeTypes,
73
+ ...(isStore ? StoreSpecificRenderableTypes : []),
74
+ ]) {
63
75
  // Note that we currently only have renderers that depend upon
64
76
  // add, remove, replace, but we should likely add
65
77
  // move, copy, test
@@ -0,0 +1,26 @@
1
+ declare module "asciinema-player" {
2
+ export const create: (
3
+ src: string | Object,
4
+ el: HTMLElement,
5
+ opts: {
6
+ cols?: number;
7
+ rows?: number;
8
+ autoPlay?: boolean;
9
+ preload?: boolean;
10
+ loop?: boolean;
11
+ theme?: string;
12
+ startAt?: number | string;
13
+ speed?: number;
14
+ idleTimeLimit?: number;
15
+ poster?: string;
16
+ fit?: string;
17
+ controls?: boolean;
18
+ markers?: Array<number> | Array<[number, string]>;
19
+ pauseOnMarkers?: boolean;
20
+ terminalFontSize?: string;
21
+ terminalFontFamily?: string;
22
+ terminalLineHeight?: string;
23
+ logger?: Object;
24
+ },
25
+ ) => any;
26
+ }