Skip to main content

supercode_interchange/orchestration/
worker.rs

1//! The worker: which harness runs a profile's conversations, and how (§2.2).
2
3use std::collections::BTreeMap;
4
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8use crate::ontology::{HarnessId, SecretRef};
9
10/// A worker environment value: a literal, or a secret by reference.
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
12#[serde(untagged)]
13pub enum EnvValue {
14    /// A plain, non-secret value.
15    Literal(String),
16    /// A secret, named and never held.
17    Secret(SecretRef),
18}
19
20/// What happens to a worker's permission prompt when no human answers.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
22#[serde(rename_all = "snake_case")]
23pub enum PermissionDefault {
24    /// Refuse after the timeout.
25    #[default]
26    Deny,
27    /// Allow after the timeout.
28    Allow,
29}
30
31/// What a prompt raised where nobody attends (a cron fire, a webhook turn) is answered with, at
32/// once (Hermes `approvals.cron_mode`).
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
34#[serde(rename_all = "snake_case")]
35pub enum PermissionUnattended {
36    /// Refuse it.
37    #[default]
38    Deny,
39    /// Approve it.
40    Approve,
41}
42
43impl PermissionUnattended {
44    fn is_deny(&self) -> bool {
45        *self == Self::Deny
46    }
47}
48
49/// How prompts are answered when no human is reachable (§4.6).
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
51pub struct PermissionPolicy {
52    /// Seconds a relayed prompt waits for an answer.
53    #[serde(default = "default_permission_timeout")]
54    pub timeout_seconds: u32,
55    /// The answer given when nobody replies in time.
56    #[serde(default)]
57    pub default: PermissionDefault,
58    /// The answer given at once where nobody attends: a cron fire or a webhook turn.
59    #[serde(default, skip_serializing_if = "PermissionUnattended::is_deny")]
60    pub unattended: PermissionUnattended,
61}
62
63fn default_permission_timeout() -> u32 {
64    300
65}
66
67impl Default for PermissionPolicy {
68    fn default() -> Self {
69        Self {
70            timeout_seconds: 300,
71            default: PermissionDefault::Deny,
72            unattended: PermissionUnattended::Deny,
73        }
74    }
75}
76
77/// The worker specification (O-record; Hermes has no worker choice).
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
79pub struct WorkerSpec {
80    /// Any registry id whose runtime can start a session.
81    pub harness: HarnessId,
82    /// Model, where the harness's door accepts one.
83    #[serde(default)]
84    pub model: Option<String>,
85    /// supercode preset name.
86    #[serde(default)]
87    pub preset: Option<String>,
88    /// Relative to the profile dir; `.` by default.
89    #[serde(default = "default_cwd")]
90    pub cwd: String,
91    /// Extra environment for the worker process; secrets by reference.
92    #[serde(default)]
93    pub env: BTreeMap<String, EnvValue>,
94    /// Permission prompt policy.
95    #[serde(default)]
96    pub permission: PermissionPolicy,
97}
98
99fn default_cwd() -> String {
100    ".".to_string()
101}