1use std::collections::HashMap;
6
7use chrono::{DateTime, Utc};
8pub use everruns_core::driver_registry::{
9 OPENROUTER_HTTP_REFERER_METADATA_KEY, OPENROUTER_X_TITLE_METADATA_KEY,
10};
11use everruns_core::network_access::NetworkAccessList;
12use everruns_core::{
13 Agent, AgentCapabilityConfig, AgentId, AgentStatus, DEFAULT_ORG_PUBLIC_ID, Harness, HarnessId,
14 HarnessStatus, ModelId, PrincipalId, ScopedMcpServers, Session, SessionId, SessionStatus,
15 ToolDefinition, plugin_capability_id,
16};
17use uuid::Uuid;
18
19#[derive(Debug, Clone)]
25pub struct HarnessBuilder {
26 id: HarnessId,
27 name: String,
28 display_name: Option<String>,
29 description: Option<String>,
30 system_prompt: String,
31 parent_harness_id: Option<HarnessId>,
32 default_model_id: Option<ModelId>,
33 tags: Vec<String>,
34 capabilities: Vec<AgentCapabilityConfig>,
35 initial_files: Vec<everruns_core::InitialFile>,
36 network_access: Option<NetworkAccessList>,
37 parallel_tool_calls: Option<bool>,
38 mcp_servers: ScopedMcpServers,
39 embedder_metadata: HashMap<String, String>,
40 is_built_in: bool,
41 status: HarnessStatus,
42 created_at: Option<DateTime<Utc>>,
43 updated_at: Option<DateTime<Utc>>,
44}
45
46impl HarnessBuilder {
47 pub fn new(name: impl Into<String>, system_prompt: impl Into<String>) -> Self {
49 Self {
50 id: HarnessId::new(),
51 name: name.into(),
52 display_name: None,
53 description: None,
54 system_prompt: system_prompt.into(),
55 parent_harness_id: None,
56 default_model_id: None,
57 tags: Vec::new(),
58 capabilities: Vec::new(),
59 initial_files: Vec::new(),
60 network_access: None,
61 parallel_tool_calls: None,
62 mcp_servers: ScopedMcpServers::default(),
63 embedder_metadata: HashMap::new(),
64 is_built_in: false,
65 status: HarnessStatus::Active,
66 created_at: None,
67 updated_at: None,
68 }
69 }
70
71 pub fn id(mut self, id: HarnessId) -> Self {
73 self.id = id;
74 self
75 }
76
77 pub fn harness_id(&self) -> HarnessId {
79 self.id
80 }
81
82 pub fn name(mut self, name: impl Into<String>) -> Self {
83 self.name = name.into();
84 self
85 }
86
87 pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
88 self.display_name = Some(display_name.into());
89 self
90 }
91
92 pub fn description(mut self, description: impl Into<String>) -> Self {
93 self.description = Some(description.into());
94 self
95 }
96
97 pub fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
98 self.system_prompt = system_prompt.into();
99 self
100 }
101
102 pub fn parent_harness_id(mut self, parent_harness_id: HarnessId) -> Self {
103 self.parent_harness_id = Some(parent_harness_id);
104 self
105 }
106
107 pub fn default_model_id(mut self, default_model_id: ModelId) -> Self {
108 self.default_model_id = Some(default_model_id);
109 self
110 }
111
112 pub fn tag(mut self, tag: impl Into<String>) -> Self {
113 self.tags.push(tag.into());
114 self
115 }
116
117 pub fn tags<I, S>(mut self, tags: I) -> Self
118 where
119 I: IntoIterator<Item = S>,
120 S: Into<String>,
121 {
122 self.tags.extend(tags.into_iter().map(Into::into));
123 self
124 }
125
126 pub fn capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
127 self.capabilities.push(capability.into());
128 self
129 }
130
131 pub fn with_capability(self, capability: impl Into<AgentCapabilityConfig>) -> Self {
132 self.capability(capability)
133 }
134
135 pub fn capabilities<I, C>(mut self, capabilities: I) -> Self
136 where
137 I: IntoIterator<Item = C>,
138 C: Into<AgentCapabilityConfig>,
139 {
140 self.capabilities
141 .extend(capabilities.into_iter().map(Into::into));
142 self
143 }
144
145 pub fn initial_file(mut self, file: everruns_core::InitialFile) -> Self {
146 self.initial_files.push(file);
147 self
148 }
149
150 pub fn network_access(mut self, network_access: NetworkAccessList) -> Self {
151 self.network_access = Some(network_access);
152 self
153 }
154
155 pub fn parallel_tool_calls(mut self, parallel_tool_calls: bool) -> Self {
157 self.parallel_tool_calls = Some(parallel_tool_calls);
158 self
159 }
160
161 pub fn mcp_servers(mut self, mcp_servers: ScopedMcpServers) -> Self {
162 self.mcp_servers = mcp_servers;
163 self
164 }
165
166 pub fn metadata_entry(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
167 self.embedder_metadata.insert(key.into(), value.into());
168 self
169 }
170
171 pub fn metadata_entries<I, K, V>(mut self, entries: I) -> Self
172 where
173 I: IntoIterator<Item = (K, V)>,
174 K: Into<String>,
175 V: Into<String>,
176 {
177 self.embedder_metadata
178 .extend(entries.into_iter().map(|(k, v)| (k.into(), v.into())));
179 self
180 }
181
182 pub fn openrouter_attribution(
187 mut self,
188 http_referer: impl Into<String>,
189 title: impl Into<String>,
190 ) -> Self {
191 self.embedder_metadata.insert(
192 OPENROUTER_HTTP_REFERER_METADATA_KEY.to_string(),
193 http_referer.into(),
194 );
195 self.embedder_metadata
196 .insert(OPENROUTER_X_TITLE_METADATA_KEY.to_string(), title.into());
197 self
198 }
199
200 pub fn is_built_in(mut self, is_built_in: bool) -> Self {
201 self.is_built_in = is_built_in;
202 self
203 }
204
205 pub fn status(mut self, status: HarnessStatus) -> Self {
206 self.status = status;
207 self
208 }
209
210 pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
211 self.created_at = Some(created_at);
212 self
213 }
214
215 pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
216 self.updated_at = Some(updated_at);
217 self
218 }
219
220 pub fn build(self) -> Harness {
222 let created_at = self.created_at.unwrap_or_else(Utc::now);
223 let updated_at = self.updated_at.unwrap_or(created_at);
224
225 Harness {
226 id: self.id,
227 name: self.name,
228 display_name: self.display_name,
229 description: self.description,
230 system_prompt: (!self.system_prompt.trim().is_empty()).then_some(self.system_prompt),
233 parent_harness_id: self.parent_harness_id,
234 default_model_id: self.default_model_id,
235 tags: self.tags,
236 capabilities: self.capabilities,
237 initial_files: self.initial_files,
238 network_access: self.network_access,
239 parallel_tool_calls: self.parallel_tool_calls,
240 mcp_servers: self.mcp_servers,
241 embedder_metadata: self.embedder_metadata,
242 is_built_in: self.is_built_in,
243 status: self.status,
244 created_at,
245 updated_at,
246 archived_at: None,
247 deleted_at: None,
248 }
249 }
250}
251
252#[derive(Debug, Clone)]
254pub struct AgentBuilder {
255 id: AgentId,
256 name: String,
257 display_name: Option<String>,
258 description: Option<String>,
259 system_prompt: String,
260 default_model_id: Option<ModelId>,
261 harness_id: HarnessId,
262 tags: Vec<String>,
263 capabilities: Vec<AgentCapabilityConfig>,
264 initial_files: Vec<everruns_core::InitialFile>,
265 network_access: Option<NetworkAccessList>,
266 max_iterations: Option<usize>,
267 parallel_tool_calls: Option<bool>,
268 tools: Vec<ToolDefinition>,
269 mcp_servers: ScopedMcpServers,
270 status: AgentStatus,
271 created_at: Option<DateTime<Utc>>,
272 updated_at: Option<DateTime<Utc>>,
273}
274
275impl AgentBuilder {
276 pub fn new(name: impl Into<String>, system_prompt: impl Into<String>) -> Self {
278 Self {
279 id: AgentId::new(),
280 name: name.into(),
281 display_name: None,
282 description: None,
283 system_prompt: system_prompt.into(),
284 default_model_id: None,
285 harness_id: HarnessId::new(),
286 tags: Vec::new(),
287 capabilities: Vec::new(),
288 initial_files: Vec::new(),
289 network_access: None,
290 max_iterations: None,
291 parallel_tool_calls: None,
292 tools: Vec::new(),
293 mcp_servers: ScopedMcpServers::default(),
294 status: AgentStatus::Active,
295 created_at: None,
296 updated_at: None,
297 }
298 }
299
300 pub fn id(mut self, id: AgentId) -> Self {
302 self.id = id;
303 self
304 }
305
306 pub fn agent_id(&self) -> AgentId {
308 self.id
309 }
310
311 pub fn name(mut self, name: impl Into<String>) -> Self {
312 self.name = name.into();
313 self
314 }
315
316 pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
317 self.display_name = Some(display_name.into());
318 self
319 }
320
321 pub fn description(mut self, description: impl Into<String>) -> Self {
322 self.description = Some(description.into());
323 self
324 }
325
326 pub fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
327 self.system_prompt = system_prompt.into();
328 self
329 }
330
331 pub fn default_model_id(mut self, default_model_id: ModelId) -> Self {
332 self.default_model_id = Some(default_model_id);
333 self
334 }
335
336 pub fn harness_id(mut self, harness_id: HarnessId) -> Self {
337 self.harness_id = harness_id;
338 self
339 }
340
341 pub fn tag(mut self, tag: impl Into<String>) -> Self {
342 self.tags.push(tag.into());
343 self
344 }
345
346 pub fn tags<I, S>(mut self, tags: I) -> Self
347 where
348 I: IntoIterator<Item = S>,
349 S: Into<String>,
350 {
351 self.tags.extend(tags.into_iter().map(Into::into));
352 self
353 }
354
355 pub fn capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
356 self.capabilities.push(capability.into());
357 self
358 }
359
360 pub fn with_capability(self, capability: impl Into<AgentCapabilityConfig>) -> Self {
361 self.capability(capability)
362 }
363
364 pub fn capabilities<I, C>(mut self, capabilities: I) -> Self
365 where
366 I: IntoIterator<Item = C>,
367 C: Into<AgentCapabilityConfig>,
368 {
369 self.capabilities
370 .extend(capabilities.into_iter().map(Into::into));
371 self
372 }
373
374 pub fn initial_file(mut self, file: everruns_core::InitialFile) -> Self {
375 self.initial_files.push(file);
376 self
377 }
378
379 pub fn network_access(mut self, network_access: NetworkAccessList) -> Self {
380 self.network_access = Some(network_access);
381 self
382 }
383
384 pub fn max_iterations(mut self, max_iterations: usize) -> Self {
385 self.max_iterations = Some(max_iterations);
386 self
387 }
388
389 pub fn parallel_tool_calls(mut self, parallel_tool_calls: bool) -> Self {
391 self.parallel_tool_calls = Some(parallel_tool_calls);
392 self
393 }
394
395 pub fn tool(mut self, tool: ToolDefinition) -> Self {
396 self.tools.push(tool);
397 self
398 }
399
400 pub fn tools<I>(mut self, tools: I) -> Self
401 where
402 I: IntoIterator<Item = ToolDefinition>,
403 {
404 self.tools.extend(tools);
405 self
406 }
407
408 pub fn mcp_servers(mut self, mcp_servers: ScopedMcpServers) -> Self {
409 self.mcp_servers = mcp_servers;
410 self
411 }
412
413 pub fn status(mut self, status: AgentStatus) -> Self {
414 self.status = status;
415 self
416 }
417
418 pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
419 self.created_at = Some(created_at);
420 self
421 }
422
423 pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
424 self.updated_at = Some(updated_at);
425 self
426 }
427
428 pub fn build(self) -> Agent {
430 let created_at = self.created_at.unwrap_or_else(Utc::now);
431 let updated_at = self.updated_at.unwrap_or(created_at);
432
433 Agent {
434 public_id: self.id,
435 internal_id: Uuid::nil(),
436 name: self.name,
437 display_name: self.display_name,
438 description: self.description,
439 system_prompt: self.system_prompt,
440 default_model_id: self.default_model_id,
441 harness_id: self.harness_id,
442 default_version_id: None,
443 forked_from_agent_id: None,
444 forked_from_version_id: None,
445 root_agent_id: None,
446 tags: self.tags,
447 capabilities: self.capabilities,
448 initial_files: self.initial_files,
449 network_access: self.network_access,
450 max_iterations: self.max_iterations,
451 parallel_tool_calls: self.parallel_tool_calls,
452 tools: self.tools,
453 mcp_servers: self.mcp_servers,
454 status: self.status,
455 created_at,
456 updated_at,
457 archived_at: None,
458 deleted_at: None,
459 usage: None,
460 }
461 }
462}
463
464#[derive(Debug, Clone)]
466pub struct SessionBuilder {
467 id: SessionId,
468 organization_id: String,
469 harness_id: HarnessId,
470 agent_id: Option<AgentId>,
471 owner_principal_id: PrincipalId,
472 title: Option<String>,
473 goal: Option<String>,
474 locale: Option<String>,
475 tags: Vec<String>,
476 model_id: Option<ModelId>,
477 capabilities: Vec<AgentCapabilityConfig>,
478 tools: Vec<ToolDefinition>,
479 mcp_servers: ScopedMcpServers,
480 system_prompt: Option<String>,
481 initial_files: Vec<everruns_core::InitialFile>,
482 network_access: Option<NetworkAccessList>,
483 max_iterations: Option<usize>,
484 parallel_tool_calls: Option<bool>,
485 status: SessionStatus,
486 created_at: Option<DateTime<Utc>>,
487 updated_at: Option<DateTime<Utc>>,
488}
489
490impl SessionBuilder {
491 pub fn new(harness_id: HarnessId) -> Self {
493 Self {
494 id: SessionId::new(),
495 organization_id: DEFAULT_ORG_PUBLIC_ID.to_string(),
496 harness_id,
497 agent_id: None,
498 owner_principal_id: PrincipalId::from_seed(1),
499 title: None,
500 goal: None,
501 locale: None,
502 tags: Vec::new(),
503 model_id: None,
504 capabilities: Vec::new(),
505 tools: Vec::new(),
506 mcp_servers: ScopedMcpServers::default(),
507 system_prompt: None,
508 initial_files: Vec::new(),
509 network_access: None,
510 max_iterations: None,
511 parallel_tool_calls: None,
512 status: SessionStatus::Started,
513 created_at: None,
514 updated_at: None,
515 }
516 }
517
518 pub fn id(mut self, id: SessionId) -> Self {
520 self.id = id;
521 self
522 }
523
524 pub fn session_id(&self) -> SessionId {
526 self.id
527 }
528
529 pub fn organization_id(mut self, organization_id: impl Into<String>) -> Self {
530 self.organization_id = organization_id.into();
531 self
532 }
533
534 pub fn harness(mut self, harness_id: HarnessId) -> Self {
535 self.harness_id = harness_id;
536 self
537 }
538
539 pub fn agent(mut self, agent_id: AgentId) -> Self {
540 self.agent_id = Some(agent_id);
541 self
542 }
543
544 pub fn owner_principal_id(mut self, owner_principal_id: PrincipalId) -> Self {
545 self.owner_principal_id = owner_principal_id;
546 self
547 }
548
549 pub fn title(mut self, title: impl Into<String>) -> Self {
550 self.title = Some(title.into());
551 self
552 }
553
554 pub fn goal(mut self, goal: impl Into<String>) -> Self {
555 self.goal = Some(goal.into());
556 self
557 }
558
559 pub fn locale(mut self, locale: impl Into<String>) -> Self {
560 self.locale = Some(locale.into());
561 self
562 }
563
564 pub fn tag(mut self, tag: impl Into<String>) -> Self {
565 self.tags.push(tag.into());
566 self
567 }
568
569 pub fn tags<I, S>(mut self, tags: I) -> Self
570 where
571 I: IntoIterator<Item = S>,
572 S: Into<String>,
573 {
574 self.tags.extend(tags.into_iter().map(Into::into));
575 self
576 }
577
578 pub fn model_id(mut self, model_id: ModelId) -> Self {
579 self.model_id = Some(model_id);
580 self
581 }
582
583 pub fn capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
584 self.capabilities.push(capability.into());
585 self
586 }
587
588 pub fn with_capability(self, capability: impl Into<AgentCapabilityConfig>) -> Self {
589 self.capability(capability)
590 }
591
592 pub fn capabilities<I, C>(mut self, capabilities: I) -> Self
593 where
594 I: IntoIterator<Item = C>,
595 C: Into<AgentCapabilityConfig>,
596 {
597 self.capabilities
598 .extend(capabilities.into_iter().map(Into::into));
599 self
600 }
601
602 pub fn tool(mut self, tool: ToolDefinition) -> Self {
603 self.tools.push(tool);
604 self
605 }
606
607 pub fn tools<I>(mut self, tools: I) -> Self
608 where
609 I: IntoIterator<Item = ToolDefinition>,
610 {
611 self.tools.extend(tools);
612 self
613 }
614
615 pub fn mcp_servers(mut self, mcp_servers: ScopedMcpServers) -> Self {
616 self.mcp_servers = mcp_servers;
617 self
618 }
619
620 pub fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
621 self.system_prompt = Some(system_prompt.into());
622 self
623 }
624
625 pub fn initial_file(mut self, file: everruns_core::InitialFile) -> Self {
626 self.initial_files.push(file);
627 self
628 }
629
630 pub fn network_access(mut self, network_access: NetworkAccessList) -> Self {
631 self.network_access = Some(network_access);
632 self
633 }
634
635 pub fn max_iterations(mut self, max_iterations: usize) -> Self {
636 self.max_iterations = Some(max_iterations);
637 self
638 }
639
640 pub fn parallel_tool_calls(mut self, parallel_tool_calls: bool) -> Self {
642 self.parallel_tool_calls = Some(parallel_tool_calls);
643 self
644 }
645
646 pub fn status(mut self, status: SessionStatus) -> Self {
647 self.status = status;
648 self
649 }
650
651 pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
652 self.created_at = Some(created_at);
653 self
654 }
655
656 pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
657 self.updated_at = Some(updated_at);
658 self
659 }
660
661 pub fn build(self) -> Session {
663 let created_at = self.created_at.unwrap_or_else(Utc::now);
664 let updated_at = self.updated_at.unwrap_or(created_at);
665
666 Session {
667 id: self.id,
668 workspace_id: everruns_core::WorkspaceId::from_uuid((self.id).uuid()),
669 organization_id: self.organization_id,
670 harness_id: self.harness_id,
671 agent_id: self.agent_id,
672 agent_version_id: None,
673 agent_identity_id: None,
674 owner_principal_id: self.owner_principal_id,
675 resolved_owner_user_id: None,
676 owner: None,
677 effective_owner: None,
678 title: self.title,
679 goal: self.goal,
680 locale: self.locale,
681 preview: None,
682 output_preview: None,
683 tags: self.tags,
684 model_id: self.model_id,
685 capabilities: self.capabilities,
686 tools: self.tools,
687 mcp_servers: self.mcp_servers,
688 system_prompt: self.system_prompt,
689 initial_files: self.initial_files,
690 hints: None,
691 network_access: self.network_access,
692 max_iterations: self.max_iterations,
693 parallel_tool_calls: self.parallel_tool_calls,
694 status: self.status,
695 created_at,
696 updated_at,
697 started_at: None,
698 finished_at: None,
699 usage: None,
700 is_pinned: None,
701 active_schedule_count: None,
702 features: Vec::new(),
703 parent_session_id: None,
704 forked_from_session_id: None,
705 forked_from_sequence: None,
706 blueprint_id: None,
707 blueprint_config: None,
708 }
709 }
710}
711
712#[derive(Debug, Clone)]
717pub struct SingleSessionBuilder {
718 harness: HarnessBuilder,
719 agent: AgentBuilder,
720 session: SessionBuilder,
721}
722
723impl Default for SingleSessionBuilder {
724 fn default() -> Self {
725 let harness = HarnessBuilder::new("embedded-harness", "");
726 let agent = AgentBuilder::new("embedded-agent", "");
727 let session = SessionBuilder::new(harness.harness_id()).agent(agent.agent_id());
728 Self {
729 harness,
730 agent,
731 session,
732 }
733 }
734}
735
736impl SingleSessionBuilder {
737 pub fn harness(mut self, name: impl Into<String>, system_prompt: impl Into<String>) -> Self {
741 let harness_id = self.harness.harness_id();
742 self.harness = self.harness.name(name).system_prompt(system_prompt);
743 self.session = self.session.harness(harness_id);
744 self
745 }
746
747 pub fn agent(mut self, name: impl Into<String>, system_prompt: impl Into<String>) -> Self {
751 let agent_id = self.agent.agent_id();
752 self.agent = self.agent.name(name).system_prompt(system_prompt);
753 self.session = self.session.agent(agent_id);
754 self
755 }
756
757 pub fn with_capability(self, capability: impl Into<AgentCapabilityConfig>) -> Self {
759 self.harness_capability(capability)
760 }
761
762 pub fn harness_capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
764 self.harness = self.harness.capability(capability);
765 self
766 }
767
768 pub fn agent_capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
770 self.agent = self.agent.capability(capability);
771 self
772 }
773
774 pub fn agent_plugin(mut self, name: &str) -> Self {
787 self.agent = self.agent.capability(plugin_capability_id(name));
788 self
789 }
790
791 pub fn session_capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
793 self.session = self.session.capability(capability);
794 self
795 }
796
797 pub fn session_mcp_servers(mut self, mcp_servers: ScopedMcpServers) -> Self {
800 self.session = self.session.mcp_servers(mcp_servers);
801 self
802 }
803
804 pub fn harness_display_name(mut self, display_name: impl Into<String>) -> Self {
805 self.harness = self.harness.display_name(display_name);
806 self
807 }
808
809 pub fn agent_display_name(mut self, display_name: impl Into<String>) -> Self {
810 self.agent = self.agent.display_name(display_name);
811 self
812 }
813
814 pub fn harness_description(mut self, description: impl Into<String>) -> Self {
815 self.harness = self.harness.description(description);
816 self
817 }
818
819 pub fn openrouter_attribution(
820 mut self,
821 http_referer: impl Into<String>,
822 title: impl Into<String>,
823 ) -> Self {
824 self.harness = self.harness.openrouter_attribution(http_referer, title);
825 self
826 }
827
828 pub fn agent_description(mut self, description: impl Into<String>) -> Self {
829 self.agent = self.agent.description(description);
830 self
831 }
832
833 pub fn session_title(mut self, title: impl Into<String>) -> Self {
834 self.session = self.session.title(title);
835 self
836 }
837
838 pub fn locale(mut self, locale: impl Into<String>) -> Self {
839 self.session = self.session.locale(locale);
840 self
841 }
842
843 pub fn tag(mut self, tag: impl Into<String>) -> Self {
844 let tag = tag.into();
845 self.harness = self.harness.tag(tag.clone());
846 self.agent = self.agent.tag(tag.clone());
847 self.session = self.session.tag(tag);
848 self
849 }
850
851 pub fn session_model_id(mut self, model_id: ModelId) -> Self {
852 self.session = self.session.model_id(model_id);
853 self
854 }
855
856 pub fn harness_default_model_id(mut self, model_id: ModelId) -> Self {
857 self.harness = self.harness.default_model_id(model_id);
858 self
859 }
860
861 pub fn agent_default_model_id(mut self, model_id: ModelId) -> Self {
862 self.agent = self.agent.default_model_id(model_id);
863 self
864 }
865
866 pub fn agent_max_iterations(mut self, max_iterations: usize) -> Self {
867 self.agent = self.agent.max_iterations(max_iterations);
868 self
869 }
870
871 pub fn session_max_iterations(mut self, max_iterations: usize) -> Self {
872 self.session = self.session.max_iterations(max_iterations);
873 self
874 }
875
876 pub fn agent_tool(mut self, tool: ToolDefinition) -> Self {
877 self.agent = self.agent.tool(tool);
878 self
879 }
880
881 pub fn session_tool(mut self, tool: ToolDefinition) -> Self {
882 self.session = self.session.tool(tool);
883 self
884 }
885
886 pub fn harness_initial_file(mut self, file: everruns_core::InitialFile) -> Self {
887 self.harness = self.harness.initial_file(file);
888 self
889 }
890
891 pub fn agent_initial_file(mut self, file: everruns_core::InitialFile) -> Self {
892 self.agent = self.agent.initial_file(file);
893 self
894 }
895
896 pub fn session_initial_file(mut self, file: everruns_core::InitialFile) -> Self {
897 self.session = self.session.initial_file(file);
898 self
899 }
900
901 pub fn harness_network_access(mut self, network_access: NetworkAccessList) -> Self {
902 self.harness = self.harness.network_access(network_access);
903 self
904 }
905
906 pub fn agent_network_access(mut self, network_access: NetworkAccessList) -> Self {
907 self.agent = self.agent.network_access(network_access);
908 self
909 }
910
911 pub fn session_network_access(mut self, network_access: NetworkAccessList) -> Self {
912 self.session = self.session.network_access(network_access);
913 self
914 }
915
916 pub fn harness_id(&self) -> HarnessId {
917 self.harness.harness_id()
918 }
919
920 pub fn agent_id(&self) -> AgentId {
921 self.agent.agent_id()
922 }
923
924 pub fn session_id(mut self, id: SessionId) -> Self {
931 self.session = self.session.id(id);
932 self
933 }
934
935 pub(crate) fn build(self) -> (Harness, Agent, Session, SessionId) {
936 let session_id = self.session.session_id();
937 (
938 self.harness.build(),
939 self.agent.build(),
940 self.session.build(),
941 session_id,
942 )
943 }
944}
945
946#[cfg(test)]
947mod tests {
948 use super::*;
949
950 #[test]
951 fn harness_builder_openrouter_attribution_adds_metadata_keys() {
952 let harness = HarnessBuilder::new("app", "prompt")
953 .openrouter_attribution("https://app.example", "Example App")
954 .build();
955
956 assert_eq!(
957 harness
958 .embedder_metadata
959 .get(OPENROUTER_HTTP_REFERER_METADATA_KEY)
960 .map(String::as_str),
961 Some("https://app.example")
962 );
963 assert_eq!(
964 harness
965 .embedder_metadata
966 .get(OPENROUTER_X_TITLE_METADATA_KEY)
967 .map(String::as_str),
968 Some("Example App")
969 );
970 }
971
972 #[test]
973 fn single_session_builder_openrouter_attribution_configures_harness() {
974 let (harness, _agent, _session, _session_id) = SingleSessionBuilder::default()
975 .openrouter_attribution("https://single.example", "Single App")
976 .build();
977
978 assert_eq!(
979 harness
980 .embedder_metadata
981 .get(OPENROUTER_HTTP_REFERER_METADATA_KEY)
982 .map(String::as_str),
983 Some("https://single.example")
984 );
985 assert_eq!(
986 harness
987 .embedder_metadata
988 .get(OPENROUTER_X_TITLE_METADATA_KEY)
989 .map(String::as_str),
990 Some("Single App")
991 );
992 }
993}