Skip to main content

golem_rust/
lib.rs

1// Copyright 2024-2026 Golem Cloud
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#[cfg(test)]
16test_r::enable!();
17
18pub use uuid::Uuid;
19pub use wasip3;
20
21#[doc(hidden)]
22#[cfg(feature = "export_golem_agentic")]
23pub fn __link_golem_component_exports() {
24    #[cfg(target_arch = "wasm32")]
25    agentic::exports::raw::link();
26}
27
28/// Export the unified Golem guest world without registering an agent, tool, or middleware.
29#[macro_export]
30#[cfg(feature = "export_golem_agentic")]
31macro_rules! export_golem_component {
32    () => {
33        $crate::ctor::__support::ctor_parse!(
34            #[ctor]
35            fn __golem_link_component_exports() {
36                $crate::__link_golem_component_exports();
37            }
38        );
39    };
40}
41
42pub use golem_schema;
43pub use golem_schema::schema;
44pub use golem_schema::schema::wit::direct::{FromWire, IntoWire, WireSchema};
45pub use golem_schema::schema::{
46    FromSchema, IntoSchema, IntoTypedSchemaValue, Quantity, QuantityUnit, Schema,
47    SchemaFingerprintError, SchemaFingerprintV1, SchemaGraph, SchemaType, SchemaValue,
48    TypedSchemaValue, schema_fingerprint_v1,
49};
50#[cfg(feature = "macro")]
51pub use golem_schema::schema::{FromWire, IntoWire, WireSchema};
52pub use golem_schema::{AgentId, CardId, ComponentId, EnvironmentId, PromiseId};
53
54pub fn encode_schema_graph(
55    graph: &SchemaGraph,
56) -> Result<schema::wit::wire::SchemaGraph, schema::wit::EncodeError> {
57    schema::wit::encode_graph(graph)
58}
59
60pub fn decode_schema_graph(
61    graph: &schema::wit::wire::SchemaGraph,
62) -> Result<SchemaGraph, schema::wit::DecodeError> {
63    schema::wit::decode_graph(graph)
64}
65
66pub fn encode_schema_value(
67    value: &SchemaValue,
68) -> Result<schema::wit::wire::SchemaValueTree, schema::wit::EncodeError> {
69    schema::wit::encode_value(value)
70}
71
72pub async fn encode_schema_value_async(
73    value: &SchemaValue,
74) -> Result<schema::wit::wire::SchemaValueTree, schema::wit::EncodeError> {
75    schema::wit::encode_value_async(value).await
76}
77
78pub fn decode_schema_value(
79    value: schema::wit::wire::SchemaValueTree,
80) -> Result<SchemaValue, schema::wit::DecodeError> {
81    schema::wit::decode_value(value)
82}
83
84pub fn encode_typed_schema_value(
85    value: &TypedSchemaValue,
86) -> Result<schema::wit::wire::TypedSchemaValue, schema::wit::EncodeError> {
87    schema::wit::encode_typed(value)
88}
89
90pub async fn encode_typed_schema_value_async(
91    value: &TypedSchemaValue,
92) -> Result<schema::wit::wire::TypedSchemaValue, schema::wit::EncodeError> {
93    schema::wit::encode_typed_async(value).await
94}
95
96pub fn decode_typed_schema_value(
97    value: &schema::wit::wire::TypedSchemaValue,
98) -> Result<TypedSchemaValue, schema::wit::DecodeError> {
99    schema::wit::decode_typed(value)
100}
101
102pub fn encode_typed_schema_value_owned(
103    value: TypedSchemaValue,
104) -> Result<schema::wit::wire::TypedSchemaValue, schema::wit::EncodeError> {
105    schema::wit::encode_typed_owned(value)
106}
107
108pub fn decode_typed_schema_value_owned(
109    value: schema::wit::wire::TypedSchemaValue,
110) -> Result<TypedSchemaValue, schema::wit::DecodeError> {
111    schema::wit::decode_typed_owned(value)
112}
113
114// Compatibility shim for the `wasi:clocks` types the Golem WIT interfaces reference
115// (`wasi:clocks/system-clock@0.3.0.{instant}` and
116// `wasi:clocks/types@0.3.0.{duration}`). The `with:` remaps in every
117// `generate!` block point the generated bindings at these modules so the Rust
118// types come from a single source.
119
120/// Public alias for the instant/datetime type used by Golem's scheduling APIs.
121pub type ScheduledTime = wasip3::clocks::system_clock::Instant;
122
123mod wasi_clocks_compat {
124    pub mod system_clock {
125        pub use wasip3::clocks::system_clock::Instant;
126    }
127    pub mod types {
128        pub use wasip3::clocks::types::Duration;
129    }
130}
131
132mod raw_bindings {
133    use wit_bindgen::generate;
134
135    generate!({
136        path: "wit",
137        world: "golem-rust",
138        generate_all,
139        generate_unused_types: true,
140        pub_export_macro: true,
141        with: {
142            "golem:core/types@2.0.0": golem_schema::schema::wit::wire,
143            "golem:tool/common@0.1.0": golem_schema::schema::tool::wit::wire,
144            "wasi:clocks/system-clock@0.3.0": crate::wasi_clocks_compat::system_clock,
145            "wasi:clocks/types@0.3.0": crate::wasi_clocks_compat::types,
146        }
147    });
148}
149
150/// Component Model `stream` constructor generated by `wit_bindgen` for the
151/// primary bindings world. Used by callers that need to hand a writable
152/// `stream<u8>` to host imports such as `wasi:blobstore` outgoing values.
153pub use crate::raw_bindings::wit_stream;
154
155pub mod bindings {
156    pub use crate::raw_bindings::wasi;
157
158    pub mod golem {
159        pub mod api {
160            pub(crate) use crate::raw_bindings::golem::api::host;
161            pub use crate::raw_bindings::golem::api::{context, oplog, retry};
162        }
163
164        pub mod durability {
165            pub(crate) use crate::raw_bindings::golem::durability::durability;
166        }
167
168        pub mod quota {
169            pub(crate) use crate::raw_bindings::golem::quota::types;
170        }
171
172        pub mod secrets {
173            pub use crate::raw_bindings::golem::secrets::{reveal, types};
174        }
175
176        pub mod agent {
177            pub use crate::raw_bindings::golem::agent::{common, durable_streams, host};
178        }
179
180        pub mod permissions {
181            pub use crate::raw_bindings::golem::permissions::{
182                derive, inspect, kernel_introspection, revoke, types, wallet,
183            };
184        }
185
186        pub mod tool {
187            pub use crate::raw_bindings::golem::tool::{host, streams};
188            pub use crate::schema::tool::wit::wire as common;
189        }
190
191        pub use crate::raw_bindings::golem::{rdbms, websocket};
192    }
193}
194
195#[cfg(feature = "export_load_snapshot")]
196pub mod load_snapshot {
197    use wit_bindgen::generate;
198
199    generate!({
200        path: "wit",
201        world: "golem-rust-load-snapshot",
202        async: ["export:golem:api/load-snapshot@1.5.0#load"],
203        generate_all,
204        generate_unused_types: true,
205        pub_export_macro: true,
206        with: {
207            "golem:core/types@2.0.0": golem_schema::schema::wit::wire,
208            "golem:tool/common@0.1.0": golem_schema::schema::tool::wit::wire,
209            "wasi:clocks/system-clock@0.3.0": crate::wasi_clocks_compat::system_clock,
210            "wasi:clocks/types@0.3.0": crate::wasi_clocks_compat::types,
211
212            "golem:api/host@1.5.0": crate::bindings::golem::api::host,
213            "golem:tool/host@0.1.0": crate::bindings::golem::tool::host,
214            "golem:api/retry@1.5.0": crate::bindings::golem::api::retry,
215            "golem:api/oplog@1.5.0": crate::bindings::golem::api::oplog,
216            "golem:api/context@1.5.0": crate::bindings::golem::api::context,
217            "golem:durability/durability@1.6.0": crate::bindings::golem::durability::durability,
218            "golem:quota/types@1.5.0": crate::bindings::golem::quota::types,
219            "golem:secrets/types@0.1.0": crate::bindings::golem::secrets::types,
220            "golem:secrets/reveal@0.1.0": crate::bindings::golem::secrets::reveal,
221            "golem:rdbms/mysql@1.5.0": crate::bindings::golem::rdbms::mysql,
222            "golem:rdbms/postgres@1.5.0": crate::bindings::golem::rdbms::postgres,
223            "golem:rdbms/types@1.5.0": crate::bindings::golem::rdbms::types,
224            "wasi:blobstore/blobstore": crate::bindings::wasi::blobstore::blobstore,
225            "wasi:blobstore/container": crate::bindings::wasi::blobstore::container,
226            "wasi:blobstore/types": crate::bindings::wasi::blobstore::types,
227            "wasi:keyvalue/eventual-batch@0.1.0": crate::bindings::wasi::keyvalue::eventual_batch,
228            "wasi:keyvalue/eventual@0.1.0": crate::bindings::wasi::keyvalue::eventual,
229            "wasi:keyvalue/types@0.1.0": crate::bindings::wasi::keyvalue::types,
230            "wasi:keyvalue/wasi-keyvalue-error@0.1.0": crate::bindings::wasi::keyvalue::wasi_keyvalue_error,
231            "wasi:logging/logging": crate::bindings::wasi::logging::logging,
232        }
233    });
234
235    pub use __export_golem_rust_load_snapshot_impl as export_load_snapshot;
236}
237
238#[cfg(feature = "export_save_snapshot")]
239pub mod save_snapshot {
240    use wit_bindgen::generate;
241
242    generate!({
243        path: "wit",
244        world: "golem-rust-save-snapshot",
245        async: ["export:golem:api/save-snapshot@1.5.0#save"],
246        generate_all,
247        generate_unused_types: true,
248        pub_export_macro: true,
249        with: {
250            "golem:core/types@2.0.0": golem_schema::schema::wit::wire,
251            "golem:tool/common@0.1.0": golem_schema::schema::tool::wit::wire,
252            "wasi:clocks/system-clock@0.3.0": crate::wasi_clocks_compat::system_clock,
253            "wasi:clocks/types@0.3.0": crate::wasi_clocks_compat::types,
254
255            "golem:api/host@1.5.0": crate::bindings::golem::api::host,
256            "golem:tool/host@0.1.0": crate::bindings::golem::tool::host,
257            "golem:api/retry@1.5.0": crate::bindings::golem::api::retry,
258            "golem:api/oplog@1.5.0": crate::bindings::golem::api::oplog,
259            "golem:api/context@1.5.0": crate::bindings::golem::api::context,
260            "golem:durability/durability@1.6.0": crate::bindings::golem::durability::durability,
261            "golem:quota/types@1.5.0": crate::bindings::golem::quota::types,
262            "golem:secrets/types@0.1.0": crate::bindings::golem::secrets::types,
263            "golem:secrets/reveal@0.1.0": crate::bindings::golem::secrets::reveal,
264            "golem:rdbms/mysql@1.5.0": crate::bindings::golem::rdbms::mysql,
265            "golem:rdbms/postgres@1.5.0": crate::bindings::golem::rdbms::postgres,
266            "golem:rdbms/types@1.5.0": crate::bindings::golem::rdbms::types,
267            "wasi:blobstore/blobstore": crate::bindings::wasi::blobstore::blobstore,
268            "wasi:blobstore/container": crate::bindings::wasi::blobstore::container,
269            "wasi:blobstore/types": crate::bindings::wasi::blobstore::types,
270            "wasi:keyvalue/eventual-batch@0.1.0": crate::bindings::wasi::keyvalue::eventual_batch,
271            "wasi:keyvalue/eventual@0.1.0": crate::bindings::wasi::keyvalue::eventual,
272            "wasi:keyvalue/types@0.1.0": crate::bindings::wasi::keyvalue::types,
273            "wasi:keyvalue/wasi-keyvalue-error@0.1.0": crate::bindings::wasi::keyvalue::wasi_keyvalue_error,
274            "wasi:logging/logging": crate::bindings::wasi::logging::logging,
275        }
276    });
277
278    pub use __export_golem_rust_save_snapshot_impl as export_save_snapshot;
279}
280
281#[cfg(feature = "export_golem_agentic")]
282pub mod golem_agentic {
283    use wit_bindgen::generate;
284
285    generate!({
286        path: "wit",
287        world: "golem-agentic",
288        async: [
289            "export:golem:agent/guest@2.0.0#initialize",
290            "export:golem:agent/guest@2.0.0#invoke",
291            "export:golem:tool/guest@0.1.0#invoke",
292            "export:golem:tool/tool-middleware-guest@0.1.0#invoke-tool-middleware",
293        ],
294        generate_all,
295        generate_unused_types: true,
296        pub_export_macro: true,
297
298        with: {
299            "golem:core/types@2.0.0": golem_schema::schema::wit::wire,
300            "golem:tool/common@0.1.0": golem_schema::schema::tool::wit::wire,
301            "wasi:clocks/system-clock@0.3.0": crate::wasi_clocks_compat::system_clock,
302            "wasi:clocks/types@0.3.0": crate::wasi_clocks_compat::types,
303
304            "golem:api/host@1.5.0": crate::bindings::golem::api::host,
305            "golem:api/retry@1.5.0": crate::bindings::golem::api::retry,
306            "golem:api/oplog@1.5.0": crate::bindings::golem::api::oplog,
307            "golem:api/context@1.5.0": crate::bindings::golem::api::context,
308            "golem:durability/durability@1.6.0": crate::bindings::golem::durability::durability,
309            "golem:quota/types@1.5.0": crate::bindings::golem::quota::types,
310            "golem:secrets/types@0.1.0": crate::bindings::golem::secrets::types,
311            "golem:secrets/reveal@0.1.0": crate::bindings::golem::secrets::reveal,
312            "golem:rdbms/mysql@1.5.0": crate::bindings::golem::rdbms::mysql,
313            "golem:rdbms/postgres@1.5.0": crate::bindings::golem::rdbms::postgres,
314            "golem:rdbms/types@1.5.0": crate::bindings::golem::rdbms::types,
315            "wasi:blobstore/blobstore": crate::bindings::wasi::blobstore::blobstore,
316            "wasi:blobstore/container": crate::bindings::wasi::blobstore::container,
317            "wasi:blobstore/types": crate::bindings::wasi::blobstore::types,
318            "wasi:keyvalue/eventual-batch@0.1.0": crate::bindings::wasi::keyvalue::eventual_batch,
319            "wasi:keyvalue/eventual@0.1.0": crate::bindings::wasi::keyvalue::eventual,
320            "wasi:keyvalue/types@0.1.0": crate::bindings::wasi::keyvalue::types,
321            "wasi:keyvalue/wasi-keyvalue-error@0.1.0": crate::bindings::wasi::keyvalue::wasi_keyvalue_error,
322            "wasi:logging/logging": crate::bindings::wasi::logging::logging,
323            "wasi:config/store@0.2.0-draft": crate::bindings::wasi::config::store,
324        }
325    });
326
327    pub use __export_golem_agentic_impl as export_golem_agentic;
328}
329#[cfg(feature = "export_golem_agentic")]
330pub(crate) use golem_agentic::golem::tool::underlying as tool_underlying_bindings;
331
332#[cfg(feature = "export_golem_agentic")]
333pub use ctor;
334
335#[cfg(feature = "export_golem_agentic")]
336pub use async_trait;
337
338#[cfg(feature = "export_golem_agentic")]
339pub use serde;
340
341#[cfg(feature = "export_golem_agentic")]
342pub use serde_json;
343
344#[cfg(feature = "export_oplog_processor")]
345pub mod oplog_processor {
346    use wit_bindgen::generate;
347
348    pub mod host {
349        pub use crate::raw_bindings::golem::api::host::AgentMetadata;
350    }
351
352    generate!({
353        path: "wit",
354        world: "golem-rust-oplog-processor",
355        generate_all,
356        generate_unused_types: true,
357        pub_export_macro: true,
358        with: {
359            "golem:core/types@2.0.0": golem_schema::schema::wit::wire,
360            "golem:tool/common@0.1.0": golem_schema::schema::tool::wit::wire,
361            "wasi:clocks/system-clock@0.3.0": crate::wasi_clocks_compat::system_clock,
362            "wasi:clocks/types@0.3.0": crate::wasi_clocks_compat::types,
363
364            "golem:api/host@1.5.0": crate::bindings::golem::api::host,
365            "golem:tool/host@0.1.0": crate::bindings::golem::tool::host,
366            "golem:api/retry@1.5.0": crate::bindings::golem::api::retry,
367            "golem:api/oplog@1.5.0": crate::bindings::golem::api::oplog,
368            "golem:api/context@1.5.0": crate::bindings::golem::api::context,
369            "golem:durability/durability@1.6.0": crate::bindings::golem::durability::durability,
370            "golem:quota/types@1.5.0": crate::bindings::golem::quota::types,
371            "golem:secrets/types@0.1.0": crate::bindings::golem::secrets::types,
372            "golem:secrets/reveal@0.1.0": crate::bindings::golem::secrets::reveal,
373            "golem:rdbms/mysql@1.5.0": crate::bindings::golem::rdbms::mysql,
374            "golem:rdbms/postgres@1.5.0": crate::bindings::golem::rdbms::postgres,
375            "golem:rdbms/types@1.5.0": crate::bindings::golem::rdbms::types,
376            "wasi:blobstore/blobstore": crate::bindings::wasi::blobstore::blobstore,
377            "wasi:blobstore/container": crate::bindings::wasi::blobstore::container,
378            "wasi:blobstore/types": crate::bindings::wasi::blobstore::types,
379            "wasi:keyvalue/eventual-batch@0.1.0": crate::bindings::wasi::keyvalue::eventual_batch,
380            "wasi:keyvalue/eventual@0.1.0": crate::bindings::wasi::keyvalue::eventual,
381            "wasi:keyvalue/types@0.1.0": crate::bindings::wasi::keyvalue::types,
382            "wasi:keyvalue/wasi-keyvalue-error@0.1.0": crate::bindings::wasi::keyvalue::wasi_keyvalue_error,
383            "wasi:logging/logging": crate::bindings::wasi::logging::logging,
384        }
385    });
386
387    pub use __export_golem_rust_oplog_processor_impl as export_oplog_processor;
388}
389
390#[cfg(feature = "export_golem_agentic")]
391pub mod agentic;
392
393#[cfg(feature = "export_golem_agentic")]
394pub use agentic::reflection::*;
395
396#[cfg(feature = "durability")]
397pub mod durability;
398
399#[cfg(feature = "json")]
400mod json;
401
402#[cfg(feature = "json")]
403pub use json::*;
404
405#[cfg(feature = "json")]
406pub mod durable_streams;
407
408mod checkpoint;
409pub mod quota;
410pub mod secrets;
411pub mod tool;
412mod transaction;
413
414use std::future::Future;
415
416use bindings::golem::api::host::{
417    self as host_api, get_idempotence_mode, mark_begin_operation, mark_end_operation,
418    set_idempotence_mode,
419};
420
421pub type OplogIndex = u64;
422
423pub mod websocket;
424pub use checkpoint::*;
425pub use quota::*;
426pub use secrets::*;
427pub mod retry;
428pub use transaction::*;
429pub use websocket::{WebSocketCloseInfo, WebSocketError, WebSocketMessage, WebsocketConnection};
430
431#[cfg(feature = "macro")]
432pub use golem_rust_macro::*;
433
434fn schema_uuid_to_wire(value: Uuid) -> schema::wit::wire::Uuid {
435    let (high_bits, low_bits) = value.as_u64_pair();
436    schema::wit::wire::Uuid {
437        high_bits,
438        low_bits,
439    }
440}
441
442fn wire_uuid_to_schema(value: schema::wit::wire::Uuid) -> Uuid {
443    Uuid::from_u64_pair(value.high_bits, value.low_bits)
444}
445
446fn schema_component_id_to_wire(value: ComponentId) -> schema::wit::wire::ComponentId {
447    schema::wit::wire::ComponentId {
448        uuid: schema_uuid_to_wire(value.uuid),
449    }
450}
451
452fn wire_component_id_to_schema(value: schema::wit::wire::ComponentId) -> ComponentId {
453    ComponentId {
454        uuid: wire_uuid_to_schema(value.uuid),
455    }
456}
457
458fn schema_agent_id_to_wire(value: AgentId) -> schema::wit::wire::AgentId {
459    schema::wit::wire::AgentId {
460        component_id: schema_component_id_to_wire(value.component_id),
461        agent_id: value.agent_id,
462    }
463}
464
465fn wire_agent_id_to_schema(value: schema::wit::wire::AgentId) -> AgentId {
466    AgentId {
467        component_id: wire_component_id_to_schema(value.component_id),
468        agent_id: value.agent_id,
469    }
470}
471
472fn schema_promise_id_to_wire(value: &PromiseId) -> schema::wit::wire::PromiseId {
473    schema::wit::wire::PromiseId {
474        agent_id: schema_agent_id_to_wire(value.agent_id.clone()),
475        oplog_idx: value.oplog_idx,
476    }
477}
478
479fn wire_promise_id_to_schema(value: schema::wit::wire::PromiseId) -> PromiseId {
480    PromiseId {
481        agent_id: wire_agent_id_to_schema(value.agent_id),
482        oplog_idx: value.oplog_idx,
483    }
484}
485
486fn schema_environment_id_to_host(value: EnvironmentId) -> host_api::EnvironmentId {
487    host_api::EnvironmentId {
488        uuid: schema_uuid_to_wire(value.uuid),
489    }
490}
491
492fn host_environment_id_to_schema(value: host_api::EnvironmentId) -> EnvironmentId {
493    EnvironmentId {
494        uuid: wire_uuid_to_schema(value.uuid),
495    }
496}
497
498#[derive(
499    Clone, Copy, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema,
500)]
501pub enum UpdateMode {
502    Automatic,
503    SnapshotBased,
504}
505
506impl From<host_api::UpdateMode> for UpdateMode {
507    fn from(value: host_api::UpdateMode) -> Self {
508        match value {
509            host_api::UpdateMode::Automatic => Self::Automatic,
510            host_api::UpdateMode::SnapshotBased => Self::SnapshotBased,
511        }
512    }
513}
514
515impl From<UpdateMode> for host_api::UpdateMode {
516    fn from(value: UpdateMode) -> Self {
517        match value {
518            UpdateMode::Automatic => Self::Automatic,
519            UpdateMode::SnapshotBased => Self::SnapshotBased,
520        }
521    }
522}
523
524#[derive(
525    Clone, Copy, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema,
526)]
527pub enum FilterComparator {
528    Equal,
529    NotEqual,
530    GreaterEqual,
531    Greater,
532    LessEqual,
533    Less,
534}
535
536impl From<host_api::FilterComparator> for FilterComparator {
537    fn from(value: host_api::FilterComparator) -> Self {
538        match value {
539            host_api::FilterComparator::Equal => Self::Equal,
540            host_api::FilterComparator::NotEqual => Self::NotEqual,
541            host_api::FilterComparator::GreaterEqual => Self::GreaterEqual,
542            host_api::FilterComparator::Greater => Self::Greater,
543            host_api::FilterComparator::LessEqual => Self::LessEqual,
544            host_api::FilterComparator::Less => Self::Less,
545        }
546    }
547}
548
549impl From<FilterComparator> for host_api::FilterComparator {
550    fn from(value: FilterComparator) -> Self {
551        match value {
552            FilterComparator::Equal => Self::Equal,
553            FilterComparator::NotEqual => Self::NotEqual,
554            FilterComparator::GreaterEqual => Self::GreaterEqual,
555            FilterComparator::Greater => Self::Greater,
556            FilterComparator::LessEqual => Self::LessEqual,
557            FilterComparator::Less => Self::Less,
558        }
559    }
560}
561
562#[derive(
563    Clone, Copy, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema,
564)]
565pub enum StringFilterComparator {
566    Equal,
567    NotEqual,
568    Like,
569    NotLike,
570    StartsWith,
571}
572
573impl From<host_api::StringFilterComparator> for StringFilterComparator {
574    fn from(value: host_api::StringFilterComparator) -> Self {
575        match value {
576            host_api::StringFilterComparator::Equal => Self::Equal,
577            host_api::StringFilterComparator::NotEqual => Self::NotEqual,
578            host_api::StringFilterComparator::Like => Self::Like,
579            host_api::StringFilterComparator::NotLike => Self::NotLike,
580            host_api::StringFilterComparator::StartsWith => Self::StartsWith,
581        }
582    }
583}
584
585impl From<StringFilterComparator> for host_api::StringFilterComparator {
586    fn from(value: StringFilterComparator) -> Self {
587        match value {
588            StringFilterComparator::Equal => Self::Equal,
589            StringFilterComparator::NotEqual => Self::NotEqual,
590            StringFilterComparator::Like => Self::Like,
591            StringFilterComparator::NotLike => Self::NotLike,
592            StringFilterComparator::StartsWith => Self::StartsWith,
593        }
594    }
595}
596
597#[derive(
598    Clone, Copy, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema,
599)]
600pub enum AgentStatus {
601    Running,
602    Idle,
603    Suspended,
604    Interrupted,
605    Retrying,
606    Failed,
607    Exited,
608}
609
610impl From<host_api::AgentStatus> for AgentStatus {
611    fn from(value: host_api::AgentStatus) -> Self {
612        match value {
613            host_api::AgentStatus::Running => Self::Running,
614            host_api::AgentStatus::Idle => Self::Idle,
615            host_api::AgentStatus::Suspended => Self::Suspended,
616            host_api::AgentStatus::Interrupted => Self::Interrupted,
617            host_api::AgentStatus::Retrying => Self::Retrying,
618            host_api::AgentStatus::Failed => Self::Failed,
619            host_api::AgentStatus::Exited => Self::Exited,
620        }
621    }
622}
623
624impl From<AgentStatus> for host_api::AgentStatus {
625    fn from(value: AgentStatus) -> Self {
626        match value {
627            AgentStatus::Running => Self::Running,
628            AgentStatus::Idle => Self::Idle,
629            AgentStatus::Suspended => Self::Suspended,
630            AgentStatus::Interrupted => Self::Interrupted,
631            AgentStatus::Retrying => Self::Retrying,
632            AgentStatus::Failed => Self::Failed,
633            AgentStatus::Exited => Self::Exited,
634        }
635    }
636}
637
638#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
639pub struct AgentNameFilter {
640    pub comparator: StringFilterComparator,
641    pub value: String,
642}
643
644impl From<host_api::AgentNameFilter> for AgentNameFilter {
645    fn from(value: host_api::AgentNameFilter) -> Self {
646        Self {
647            comparator: Into::into(value.comparator),
648            value: value.value,
649        }
650    }
651}
652
653impl From<AgentNameFilter> for host_api::AgentNameFilter {
654    fn from(value: AgentNameFilter) -> Self {
655        Self {
656            comparator: Into::into(value.comparator),
657            value: value.value,
658        }
659    }
660}
661
662#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
663pub struct AgentStatusFilter {
664    pub comparator: FilterComparator,
665    pub value: AgentStatus,
666}
667
668impl From<host_api::AgentStatusFilter> for AgentStatusFilter {
669    fn from(value: host_api::AgentStatusFilter) -> Self {
670        Self {
671            comparator: Into::into(value.comparator),
672            value: Into::into(value.value),
673        }
674    }
675}
676
677impl From<AgentStatusFilter> for host_api::AgentStatusFilter {
678    fn from(value: AgentStatusFilter) -> Self {
679        Self {
680            comparator: Into::into(value.comparator),
681            value: Into::into(value.value),
682        }
683    }
684}
685
686#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
687pub struct AgentVersionFilter {
688    pub comparator: FilterComparator,
689    pub value: u64,
690}
691
692impl From<host_api::AgentVersionFilter> for AgentVersionFilter {
693    fn from(value: host_api::AgentVersionFilter) -> Self {
694        Self {
695            comparator: Into::into(value.comparator),
696            value: value.value,
697        }
698    }
699}
700
701impl From<AgentVersionFilter> for host_api::AgentVersionFilter {
702    fn from(value: AgentVersionFilter) -> Self {
703        Self {
704            comparator: Into::into(value.comparator),
705            value: value.value,
706        }
707    }
708}
709
710#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
711pub struct AgentCreatedAtFilter {
712    pub comparator: FilterComparator,
713    pub value: u64,
714}
715
716impl From<host_api::AgentCreatedAtFilter> for AgentCreatedAtFilter {
717    fn from(value: host_api::AgentCreatedAtFilter) -> Self {
718        Self {
719            comparator: Into::into(value.comparator),
720            value: value.value,
721        }
722    }
723}
724
725impl From<AgentCreatedAtFilter> for host_api::AgentCreatedAtFilter {
726    fn from(value: AgentCreatedAtFilter) -> Self {
727        Self {
728            comparator: Into::into(value.comparator),
729            value: value.value,
730        }
731    }
732}
733
734#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
735pub struct AgentEnvFilter {
736    pub name: String,
737    pub comparator: StringFilterComparator,
738    pub value: String,
739}
740
741impl From<host_api::AgentEnvFilter> for AgentEnvFilter {
742    fn from(value: host_api::AgentEnvFilter) -> Self {
743        Self {
744            name: value.name,
745            comparator: Into::into(value.comparator),
746            value: value.value,
747        }
748    }
749}
750
751impl From<AgentEnvFilter> for host_api::AgentEnvFilter {
752    fn from(value: AgentEnvFilter) -> Self {
753        Self {
754            name: value.name,
755            comparator: Into::into(value.comparator),
756            value: value.value,
757        }
758    }
759}
760
761#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
762pub struct AgentConfigVarsFilter {
763    pub name: String,
764    pub comparator: StringFilterComparator,
765    pub value: String,
766}
767
768impl From<host_api::AgentConfigVarsFilter> for AgentConfigVarsFilter {
769    fn from(value: host_api::AgentConfigVarsFilter) -> Self {
770        Self {
771            name: value.name,
772            comparator: Into::into(value.comparator),
773            value: value.value,
774        }
775    }
776}
777
778impl From<AgentConfigVarsFilter> for host_api::AgentConfigVarsFilter {
779    fn from(value: AgentConfigVarsFilter) -> Self {
780        Self {
781            name: value.name,
782            comparator: Into::into(value.comparator),
783            value: value.value,
784        }
785    }
786}
787
788#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
789pub enum AgentPropertyFilter {
790    Name(AgentNameFilter),
791    Status(AgentStatusFilter),
792    Version(AgentVersionFilter),
793    CreatedAt(AgentCreatedAtFilter),
794    Env(AgentEnvFilter),
795    Config(AgentConfigVarsFilter),
796}
797
798impl From<host_api::AgentPropertyFilter> for AgentPropertyFilter {
799    fn from(value: host_api::AgentPropertyFilter) -> Self {
800        match value {
801            host_api::AgentPropertyFilter::Name(value) => Self::Name(Into::into(value)),
802            host_api::AgentPropertyFilter::Status(value) => Self::Status(Into::into(value)),
803            host_api::AgentPropertyFilter::Version(value) => Self::Version(Into::into(value)),
804            host_api::AgentPropertyFilter::CreatedAt(value) => Self::CreatedAt(Into::into(value)),
805            host_api::AgentPropertyFilter::Env(value) => Self::Env(Into::into(value)),
806            host_api::AgentPropertyFilter::Config(value) => Self::Config(Into::into(value)),
807        }
808    }
809}
810
811impl From<AgentPropertyFilter> for host_api::AgentPropertyFilter {
812    fn from(value: AgentPropertyFilter) -> Self {
813        match value {
814            AgentPropertyFilter::Name(value) => Self::Name(Into::into(value)),
815            AgentPropertyFilter::Status(value) => Self::Status(Into::into(value)),
816            AgentPropertyFilter::Version(value) => Self::Version(Into::into(value)),
817            AgentPropertyFilter::CreatedAt(value) => Self::CreatedAt(Into::into(value)),
818            AgentPropertyFilter::Env(value) => Self::Env(Into::into(value)),
819            AgentPropertyFilter::Config(value) => Self::Config(Into::into(value)),
820        }
821    }
822}
823
824#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
825pub struct AgentAllFilter {
826    pub filters: Vec<AgentPropertyFilter>,
827}
828
829impl From<host_api::AgentAllFilter> for AgentAllFilter {
830    fn from(value: host_api::AgentAllFilter) -> Self {
831        Self {
832            filters: value.filters.into_iter().map(Into::into).collect(),
833        }
834    }
835}
836
837impl From<AgentAllFilter> for host_api::AgentAllFilter {
838    fn from(value: AgentAllFilter) -> Self {
839        Self {
840            filters: value.filters.into_iter().map(Into::into).collect(),
841        }
842    }
843}
844
845#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
846pub struct AgentAnyFilter {
847    pub filters: Vec<AgentAllFilter>,
848}
849
850impl From<host_api::AgentAnyFilter> for AgentAnyFilter {
851    fn from(value: host_api::AgentAnyFilter) -> Self {
852        Self {
853            filters: value.filters.into_iter().map(Into::into).collect(),
854        }
855    }
856}
857
858impl From<AgentAnyFilter> for host_api::AgentAnyFilter {
859    fn from(value: AgentAnyFilter) -> Self {
860        Self {
861            filters: value.filters.into_iter().map(Into::into).collect(),
862        }
863    }
864}
865
866#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
867pub struct AgentMetadata {
868    pub agent_id: AgentId,
869    pub args: Vec<String>,
870    pub env: Vec<(String, String)>,
871    pub config: Vec<(String, String)>,
872    pub status: AgentStatus,
873    pub component_revision: u64,
874    pub retry_count: u64,
875    pub environment_id: EnvironmentId,
876}
877
878impl From<host_api::AgentMetadata> for AgentMetadata {
879    fn from(value: host_api::AgentMetadata) -> Self {
880        Self {
881            agent_id: wire_agent_id_to_schema(value.agent_id),
882            args: value.args,
883            env: value.env,
884            config: value.config,
885            status: Into::into(value.status),
886            component_revision: value.component_revision,
887            retry_count: value.retry_count,
888            environment_id: host_environment_id_to_schema(value.environment_id),
889        }
890    }
891}
892
893impl From<AgentMetadata> for host_api::AgentMetadata {
894    fn from(value: AgentMetadata) -> Self {
895        Self {
896            agent_id: schema_agent_id_to_wire(value.agent_id),
897            args: value.args,
898            env: value.env,
899            config: value.config,
900            status: Into::into(value.status),
901            component_revision: value.component_revision,
902            retry_count: value.retry_count,
903            environment_id: schema_environment_id_to_host(value.environment_id),
904        }
905    }
906}
907
908pub struct GetAgents {
909    raw: host_api::GetAgents,
910}
911
912pub use host_api::AgentOperationError;
913
914impl GetAgents {
915    pub fn new(component_id: ComponentId, filter: Option<&AgentAnyFilter>, precise: bool) -> Self {
916        let raw_filter = filter.cloned().map(host_api::AgentAnyFilter::from);
917        Self {
918            raw: host_api::GetAgents::new(
919                schema_component_id_to_wire(component_id),
920                raw_filter.as_ref(),
921                precise,
922            ),
923        }
924    }
925
926    pub fn get_next(&self) -> Result<Option<Vec<AgentMetadata>>, AgentOperationError> {
927        self.raw
928            .get_next()
929            .map(|values| values.map(|values| values.into_iter().map(Into::into).collect()))
930    }
931}
932
933#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
934pub struct ForkDetails {
935    pub forked_phantom_id: Uuid,
936}
937
938#[derive(Clone, Debug, Eq, PartialEq, IntoSchema, FromSchema, IntoWire, FromWire, WireSchema)]
939pub enum ForkResult {
940    Original(ForkDetails),
941    Forked(ForkDetails),
942}
943
944impl From<host_api::ForkResult> for ForkResult {
945    fn from(value: host_api::ForkResult) -> Self {
946        match value {
947            host_api::ForkResult::Original(details) => Self::Original(ForkDetails {
948                forked_phantom_id: wire_uuid_to_schema(details.forked_phantom_id),
949            }),
950            host_api::ForkResult::Forked(details) => Self::Forked(ForkDetails {
951                forked_phantom_id: wire_uuid_to_schema(details.forked_phantom_id),
952            }),
953        }
954    }
955}
956
957pub struct PromiseResult {
958    raw: host_api::GetPromiseResult,
959}
960
961impl PromiseResult {
962    /// Awaits the promise result. The agent is suspended until the promise is
963    /// completed; the completed payload is then returned.
964    ///
965    /// On WASI P2 this was a non-blocking `get() -> Option<...>` paired with a
966    /// `subscribe() -> pollable`; the P3 `wasi:io/poll` pollable is gone, so the
967    /// host `get-promise-result.get` is now an `async func` that awaits directly.
968    pub async fn get(&self) -> Vec<u8> {
969        self.raw.get().await
970    }
971}
972
973pub fn create_promise() -> PromiseId {
974    wire_promise_id_to_schema(host_api::create_promise())
975}
976
977pub fn get_promise(promise_id: &PromiseId) -> PromiseResult {
978    PromiseResult {
979        raw: host_api::get_promise(&schema_promise_id_to_wire(promise_id)),
980    }
981}
982
983pub fn complete_promise(promise_id: &PromiseId, data: &[u8]) -> bool {
984    host_api::complete_promise(&schema_promise_id_to_wire(promise_id), data)
985}
986
987pub fn get_oplog_index() -> OplogIndex {
988    host_api::get_oplog_index()
989}
990
991pub fn set_oplog_index(oplog_idx: OplogIndex) {
992    host_api::set_oplog_index(oplog_idx)
993}
994
995pub fn oplog_commit(replicas: u8) {
996    host_api::oplog_commit(replicas)
997}
998
999pub fn get_self_metadata() -> Result<AgentMetadata, AgentOperationError> {
1000    host_api::get_self_metadata().map(Into::into)
1001}
1002
1003pub fn get_agent_metadata(agent_id: &AgentId) -> Option<AgentMetadata> {
1004    host_api::get_agent_metadata(&schema_agent_id_to_wire(agent_id.clone())).map(Into::into)
1005}
1006
1007pub fn update_agent(
1008    agent_id: &AgentId,
1009    target_revision: u64,
1010    mode: UpdateMode,
1011) -> Result<(), AgentOperationError> {
1012    host_api::update_agent(
1013        &schema_agent_id_to_wire(agent_id.clone()),
1014        target_revision,
1015        Into::into(mode),
1016    )
1017}
1018
1019pub fn resolve_component_id(component_reference: &str) -> Option<ComponentId> {
1020    host_api::resolve_component_id(component_reference).map(wire_component_id_to_schema)
1021}
1022
1023pub fn resolve_agent_id(component_reference: &str, agent_name: &str) -> Option<AgentId> {
1024    host_api::resolve_agent_id(component_reference, agent_name).map(wire_agent_id_to_schema)
1025}
1026
1027pub fn resolve_agent_id_strict(component_reference: &str, agent_name: &str) -> Option<AgentId> {
1028    host_api::resolve_agent_id_strict(component_reference, agent_name).map(wire_agent_id_to_schema)
1029}
1030
1031pub fn fork() -> Result<ForkResult, AgentOperationError> {
1032    host_api::fork().map(Into::into)
1033}
1034
1035/// Awaits a promise blocking the execution of the agent. The agent is going to be
1036/// suspended until the promise is completed.
1037///
1038/// Use `await_promise` for an async version of this function, allowing to interleave
1039/// awaiting of the promise with other operations.
1040pub fn blocking_await_promise(promise_id: &PromiseId) -> Vec<u8> {
1041    let promise = get_promise(promise_id);
1042    wit_bindgen::block_on(async move { promise.get().await })
1043}
1044
1045/// Awaits a promise.
1046///
1047/// If only promises or timeouts are awaited simultaneously, the agent is going to be
1048/// suspended until any of them completes.
1049pub async fn await_promise(promise_id: &PromiseId) -> Vec<u8> {
1050    let promise = get_promise(promise_id);
1051    promise.get().await
1052}
1053
1054pub struct IdempotenceModeGuard {
1055    original: bool,
1056}
1057
1058impl Drop for IdempotenceModeGuard {
1059    fn drop(&mut self) {
1060        set_idempotence_mode(self.original);
1061    }
1062}
1063
1064/// Temporarily sets the idempotence mode to the given value.
1065///
1066/// When the returned guard is dropped, the original idempotence mode is restored.
1067#[must_use]
1068pub fn use_idempotence_mode(mode: bool) -> IdempotenceModeGuard {
1069    let original = get_idempotence_mode();
1070    set_idempotence_mode(mode);
1071    IdempotenceModeGuard { original }
1072}
1073
1074/// Executes the given function with the idempotence mode set to the given value.
1075pub fn with_idempotence_mode<R>(mode: bool, f: impl FnOnce() -> R) -> R {
1076    let _guard = use_idempotence_mode(mode);
1077    f()
1078}
1079
1080/// Executes the given async function with the idempotence mode set to the given value.
1081pub async fn with_idempotence_mode_async<R, F: Future<Output = R>>(
1082    mode: bool,
1083    f: impl FnOnce() -> F,
1084) -> R {
1085    let _guard = use_idempotence_mode(mode);
1086    f().await
1087}
1088
1089/// Generates an idempotency key. This operation will never be replayed —
1090/// i.e. not only is this key generated, but it is persisted and committed, such that the key can be used in third-party systems (e.g. payment processing)
1091/// to introduce idempotence.
1092pub fn generate_idempotency_key() -> uuid::Uuid {
1093    Into::into(host_api::generate_idempotency_key())
1094}
1095
1096pub struct AtomicOperationGuard {
1097    begin: OplogIndex,
1098}
1099
1100impl Drop for AtomicOperationGuard {
1101    fn drop(&mut self) {
1102        // If we're unwinding from a panic, leave the atomic region open so the
1103        // worker recovery + replay-time fallback in `mark_begin_operation`
1104        // re-executes the block from the begin marker. WASM panics are wasm
1105        // traps, so the executor will recover the worker.
1106        if !std::thread::panicking() {
1107            mark_end_operation(self.begin);
1108        }
1109    }
1110}
1111
1112/// Marks a block as an atomic operation
1113///
1114/// When the returned guard is dropped, the operation gets committed —
1115/// unless the current thread is panicking, in which case the region is left
1116/// open so worker recovery re-executes the block.
1117#[must_use]
1118pub fn mark_atomic_operation() -> AtomicOperationGuard {
1119    let begin = mark_begin_operation();
1120    AtomicOperationGuard { begin }
1121}
1122
1123/// Executes the given function as an atomic operation.
1124///
1125/// On panic the region is left open and the worker recovers + re-executes
1126/// the block. Use [`atomically_result`] when the body returns a `Result` so
1127/// that error returns also force a trap rather than silently committing the
1128/// region.
1129pub fn atomically<T>(f: impl FnOnce() -> T) -> T {
1130    let _guard = mark_atomic_operation();
1131    f()
1132}
1133
1134/// Executes the given async function as an atomic operation.
1135///
1136/// On panic the region is left open and the worker recovers + re-executes
1137/// the block.
1138pub async fn atomically_async<T, F: Future<Output = T>>(f: impl FnOnce() -> F) -> T {
1139    let _guard = mark_atomic_operation();
1140    f().await
1141}
1142
1143/// Executes the given fallible function as an atomic operation.
1144///
1145/// On `Ok` the region is committed.
1146/// On `Err` the SDK calls the host `trap` function, which surfaces as an
1147/// uncatchable wasm trap so the failure cannot be observed by user code.
1148/// The atomic region is intentionally left open so the existing replay-time
1149/// fallback in `mark_begin_operation` deletes the partial inner side effects
1150/// and re-executes the block.
1151pub fn atomically_result<T, E>(f: impl FnOnce() -> Result<T, E>) -> Result<T, E>
1152where
1153    E: core::fmt::Display,
1154{
1155    let guard = mark_atomic_operation();
1156    match f() {
1157        Ok(v) => Ok(v),
1158        Err(e) => {
1159            // Skip the Drop closing the region — the trap is the terminator.
1160            core::mem::forget(guard);
1161            host_api::trap(&format!("atomic block failed: {e}"));
1162            unreachable!("trap host call must not return")
1163        }
1164    }
1165}
1166
1167/// Async version of [`atomically_result`].
1168pub async fn atomically_result_async<T, E, F>(f: impl FnOnce() -> F) -> Result<T, E>
1169where
1170    E: core::fmt::Display,
1171    F: Future<Output = Result<T, E>>,
1172{
1173    let guard = mark_atomic_operation();
1174    match f().await {
1175        Ok(v) => Ok(v),
1176        Err(e) => {
1177            core::mem::forget(guard);
1178            host_api::trap(&format!("atomic block failed: {e}"));
1179            unreachable!("trap host call must not return")
1180        }
1181    }
1182}