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