simulo-interfaces 0.19.0__tar.gz → 0.20.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {simulo_interfaces-0.19.0/src/simulo_interfaces.egg-info → simulo_interfaces-0.20.0}/PKG-INFO +1 -1
  2. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/pyproject.toml +1 -1
  3. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/__init__.py +2 -0
  4. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/asset_catalog.py +1 -1
  5. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/enums.py +20 -2
  6. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/manifest.py +3 -3
  7. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/outputs.py +3 -2
  8. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/runs.py +18 -2
  9. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/submit.py +68 -8
  10. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0/src/simulo_interfaces.egg-info}/PKG-INFO +1 -1
  11. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/MANIFEST.in +0 -0
  12. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/PYPI.md +0 -0
  13. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/setup.cfg +0 -0
  14. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/__init__.py +0 -0
  15. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/__init__.py +0 -0
  16. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/actuator.py +0 -0
  17. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/asset.py +0 -0
  18. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/entity.py +0 -0
  19. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/light.py +0 -0
  20. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/materials.py +0 -0
  21. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/physics.py +0 -0
  22. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/pose.py +0 -0
  23. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/primitives.py +0 -0
  24. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/prop.py +0 -0
  25. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/record.py +0 -0
  26. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/robot.py +0 -0
  27. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/scene.py +0 -0
  28. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/sensor.py +0 -0
  29. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/terrain.py +0 -0
  30. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/visual.py +0 -0
  31. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/authoring/world.py +0 -0
  32. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/exceptions.py +0 -0
  33. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/ids.py +0 -0
  34. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/_payload.py +0 -0
  35. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/app.py +0 -0
  36. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/asset.py +0 -0
  37. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/callbacks.py +0 -0
  38. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/debug.py +0 -0
  39. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/domain.py +0 -0
  40. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/job_events.py +0 -0
  41. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/policy_bundle.py +0 -0
  42. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/runtime.py +0 -0
  43. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/systems.py +0 -0
  44. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/platform/volume.py +0 -0
  45. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/py.typed +0 -0
  46. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/__init__.py +0 -0
  47. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/anomaly.py +0 -0
  48. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/components.py +0 -0
  49. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/env.py +0 -0
  50. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/player.py +0 -0
  51. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/policy.py +0 -0
  52. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/scenario.py +0 -0
  53. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/task.py +0 -0
  54. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/tensors.py +0 -0
  55. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo/interfaces/runtime/trainer.py +0 -0
  56. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/SOURCES.txt +0 -0
  57. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
  58. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/requires.txt +0 -0
  59. {simulo_interfaces-0.19.0 → simulo_interfaces-0.20.0}/src/simulo_interfaces.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulo-interfaces
3
- Version: 0.19.0
3
+ Version: 0.20.0
4
4
  Summary: Type contracts for the Simulo SDK. Installed automatically as a dependency of the simulo package.
5
5
  Author-email: Simulo Team <team@simulo.ai>
6
6
  License: BSD-3-Clause
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simulo-interfaces"
7
- version = "0.19.0"
7
+ version = "0.20.0"
8
8
  description = "Type contracts for the Simulo SDK. Installed automatically as a dependency of the simulo package."
9
9
  readme = "PYPI.md"
10
10
  requires-python = ">=3.11"
@@ -228,6 +228,7 @@ from simulo.interfaces.platform.submit import (
228
228
  SEED_JOB_REF_GRAMMAR,
229
229
  SEED_SOURCE_CONFLICT_CODE,
230
230
  SEED_SOURCE_FIELDS,
231
+ WORKER_BOOTSTRAP_ROUTE,
231
232
  WORKER_CLAIM_ROUTE,
232
233
  WORKER_JOB_COMPLETE_ROUTE_TEMPLATE,
233
234
  WORKER_JOB_HEARTBEAT_ROUTE_TEMPLATE,
@@ -351,6 +352,7 @@ __all__ = [
351
352
  "JOB_MODELS_ROUTE_TEMPLATE",
352
353
  "JOB_MODEL_ROUTE_TEMPLATE",
353
354
  "JOB_MODEL_DOWNLOAD_ROUTE_TEMPLATE",
355
+ "WORKER_BOOTSTRAP_ROUTE",
354
356
  "WORKER_CLAIM_ROUTE",
355
357
  "WORKER_JOB_LOGS_ROUTE_TEMPLATE",
356
358
  "WORKER_JOB_HEARTBEAT_ROUTE_TEMPLATE",
@@ -493,7 +493,7 @@ ASSET_VERSION_REPORT_ROUTE_TEMPLATE = "/v1/asset-versions/{version_id}/report"
493
493
  ASSET_VERSION_DOWNLOAD_ROUTE_TEMPLATE = "/v1/asset-versions/{version_id}/download"
494
494
 
495
495
  # --------------------------------------------------------------------------
496
- # Worker routes (bearer = static WORKER_TOKEN; + lease header, see submit.py)
496
+ # Worker routes (bearer = per-worker identity JWT; + lease header, see submit.py)
497
497
  # --------------------------------------------------------------------------
498
498
  #
499
499
  # The claim response (submit.WORKER_CLAIM_ROUTE) additionally carries an
@@ -11,8 +11,9 @@ class JobStatus(StrEnum):
11
11
  """Lifecycle state of a submitted job.
12
12
 
13
13
  A job moves ``QUEUED`` → ``RUNNING`` → one terminal state
14
- (``COMPLETED`` / ``FAILED`` / ``CANCELLED``). Terminal states never change
15
- again — logs and results are final once a job reaches one.
14
+ (``COMPLETED`` / ``FAILED`` / ``CANCELLED`` / ``TIMED_OUT``). Terminal
15
+ states never change again — logs and results are final once a job
16
+ reaches one.
16
17
  """
17
18
 
18
19
  QUEUED = "queued"
@@ -30,6 +31,23 @@ class JobStatus(StrEnum):
30
31
  CANCELLED = "cancelled"
31
32
  """Stopped on request (``simulo cancel``) before completing."""
32
33
 
34
+ TIMED_OUT = "timed_out"
35
+ """Stopped for exceeding its organization's configured runtime limit —
36
+ distinct from :attr:`FAILED`: the job did not crash, it ran past its
37
+ organization's configured runtime limit and was stopped by the platform
38
+ (the supervisor's own kill, or a backstop reaper after the deadline plus
39
+ a grace period). Any checkpoint the run had produced by then is salvaged
40
+ the same way a cancelled run's is.
41
+
42
+ Appended, never inserted: this member was added after the first five and
43
+ keeps its place at the end of the wire enum per this module's own
44
+ append-only rule. A client should treat this value as
45
+ terminal-and-distinct wherever it reaches one directly (e.g. a
46
+ worker-authored `complete` body); the control plane's own public read
47
+ routes may still project it as `FAILED` for compatibility with clients
48
+ published before this member existed — see that rollout's own record for
49
+ when the projection lifts."""
50
+
33
51
 
34
52
  class ResumePolicy(StrEnum):
35
53
  """How a job resumes from a discovered checkpoint."""
@@ -164,9 +164,9 @@ class Manifest:
164
164
  #: this endpoint must not break a newer client.
165
165
  META_ROUTE = "/v1/meta"
166
166
 
167
- #: ``POST`` — the worker's runtime-manifest self-report (bearer = static
168
- #: ``WORKER_TOKEN``, same trust zone as every ``/internal/worker/*`` route —
169
- #: see :mod:`simulo.interfaces.platform.submit`). The worker introspects its
167
+ #: ``POST`` — the worker's runtime-manifest self-report (bearer = the
168
+ #: per-worker identity JWT, same trust zone as every ``/internal/worker/*``
169
+ #: route — see :mod:`simulo.interfaces.platform.submit`). The worker introspects its
170
170
  #: own interpreter (``importlib.metadata.distributions()``) on startup and
171
171
  #: POSTs a :class:`RuntimeManifest` — the reporter *is* the runtime.
172
172
  #: **Best-effort by contract:** a failed report must never stop a worker from
@@ -196,8 +196,9 @@ JOB_OUTPUTS_ROUTE_TEMPLATE = "/v1/jobs/{job_id}/outputs"
196
196
  JOB_OUTPUT_DOWNLOAD_ROUTE_TEMPLATE = "/v1/jobs/{job_id}/outputs/{output_id}/download"
197
197
 
198
198
  # --------------------------------------------------------------------------
199
- # Worker-facing routes (bearer = static WORKER_TOKEN, constant-time compare;
200
- # same auth surface as the recordings/models worker routes in submit.py)
199
+ # Worker-facing routes (bearer = the per-worker identity JWT
200
+ # `WORKER_BOOTSTRAP_ROUTE` mints, verified server-side per call; same auth
201
+ # surface as the recordings/models worker routes in submit.py)
201
202
  # --------------------------------------------------------------------------
202
203
 
203
204
  #: ``POST`` — obtain a presigned upload URL for a new general output (a
@@ -302,7 +302,16 @@ LOG_ARCHIVE_READ_FAILED_CODE = "log_archive_read_failed"
302
302
 
303
303
  #: Statuses after which a job's log stream and record no longer change. A log
304
304
  #: follower drains the remaining bytes and stops once the status is terminal.
305
- TERMINAL_JOB_STATUSES = frozenset({JobStatus.COMPLETED, JobStatus.FAILED, JobStatus.CANCELLED})
305
+ #:
306
+ #: ``TIMED_OUT`` joined this set in `M55-org-runtime-policy` (issue #1537).
307
+ #: The control plane's public read routes do not emit it yet (they project a
308
+ #: `timed_out` job as `FAILED` with `status_reason="timeout"` until a
309
+ #: tolerant-client floor is recorded — see :class:`~simulo.interfaces.
310
+ #: platform.enums.JobStatus`'s own docstring), so this set already carrying
311
+ #: it is what makes a client that widens itself ahead of that projection
312
+ #: lift (per this contract's own unknown-status-is-terminal guidance) treat
313
+ #: the value correctly the moment it does appear on the wire.
314
+ TERMINAL_JOB_STATUSES = frozenset({JobStatus.COMPLETED, JobStatus.FAILED, JobStatus.CANCELLED, JobStatus.TIMED_OUT})
306
315
 
307
316
 
308
317
  @dataclass(frozen=True, slots=True, kw_only=True)
@@ -334,7 +343,14 @@ class JobRecord:
334
343
  """When the job was created (ISO 8601 UTC, e.g. ``2026-07-03T12:00:00Z``)."""
335
344
 
336
345
  args: Mapping[str, Any] = field(default_factory=dict)
337
- """The keyword arguments the job body was invoked with."""
346
+ """The keyword arguments the job body was invoked with, AS SERVED ON
347
+ THIS RECORD — not necessarily what the job actually ran with. The
348
+ control plane redacts any credential-shaped key or value (replacing it
349
+ with the literal string ``"<redacted>"``) when building this public
350
+ record; the job itself executed with the real, unredacted values. A
351
+ client reading this field to display a job's history sees the redacted
352
+ view; nothing about a redacted entry here implies the run itself saw
353
+ ``"<redacted>"``."""
338
354
 
339
355
  started_at: Optional[str] = None
340
356
  """When execution began (ISO 8601 UTC). ``None`` while queued."""
@@ -25,10 +25,16 @@ architecture section for the full picture):
25
25
  * **Client-facing `/v1/...`** — bearer = the user's Cognito JWT;
26
26
  ``require_active_org`` is enforced server-side. These are the routes a
27
27
  human's ``simulo`` CLI/SDK calls.
28
- * **Worker-facing `/internal/worker/...`** — bearer = a single static
29
- ``WORKER_TOKEN`` (constant-time compare on the server, never logged). Empty
30
- server-side config disables every ``/internal/worker/*`` route
31
- fail-closed — there is no "auth disabled" state for these routes.
28
+ * **Worker-facing `/internal/worker/...`** — bearer = a short-lived,
29
+ per-worker identity JWT (see :data:`WORKER_BOOTSTRAP_ROUTE` below). The worker obtains it from an
30
+ IAM-signed bootstrap call, unique per EC2 instance identity; the server
31
+ verifies it (audience, issuer, expiry, and — live, on every call — the
32
+ issuing worker row's revocation status) rather than comparing against a
33
+ single shared secret. Empty server-side signing-secret config disables
34
+ every ``/internal/worker/*`` route fail-closed — there is no "auth
35
+ disabled" state for these routes. (Before this cutover the bearer was a
36
+ single static ``WORKER_TOKEN`` shared by the whole fleet; that scheme is
37
+ retired.)
32
38
  * **Presigned URLs** (`download_url` / `upload_url` fields below) carry their
33
39
  own auth (S3 SigV4 query params, or a control-plane-served one-time token
34
40
  when running on the local storage backend) — a caller must send the
@@ -159,8 +165,28 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
159
165
 
160
166
  ## Worker-facing (`/internal/worker/*`)
161
167
 
162
- * :data:`WORKER_CLAIM_ROUTE` (``POST``) — request ``{"worker_id": "..."}``.
163
- Response ``200``::
168
+ * :data:`WORKER_BOOTSTRAP_ROUTE` (``POST``) — request
169
+ ``{"signature_b64": "..."}``, the caller's own IAM-signed EC2 instance
170
+ identity document exactly as ``dynamic/instance-identity/rsa2048``
171
+ returns it (base64, no armor). Carries NO shared secret of its own — the
172
+ signed document IS the credential. Response ``200``::
173
+
174
+ {"worker_id": "wkr_...", "tier": "tier1", "pinned_organization_id": null,
175
+ "status": "active", "token": "<jwt>", "token_expires_at": "2026-07-03T13:00:00Z"}
176
+
177
+ Idempotent per EC2 instance identity: a repeat call from an instance that
178
+ already holds a worker identity returns that SAME identity (still pinned,
179
+ if pinned) with a freshly-issued ``token`` — the recommended renewal
180
+ cadence is half of the deployment's configured token TTL. ``token`` is
181
+ the bearer every OTHER ``/internal/worker/*`` route now requires (below);
182
+ nothing on the wire ever carries the ``WORKER_TOKEN`` shared bearer this
183
+ route's own token retired.
184
+
185
+ * :data:`WORKER_CLAIM_ROUTE` (``POST``) — request body carries no worker
186
+ identity field: the claiming worker is whoever the ``Authorization``
187
+ bearer (above) verifies to, never a caller-asserted value — a
188
+ pre-cutover claim body's ``{"worker_id": "..."}`` was trusted outright,
189
+ the actual gap this contract change closed. Response ``200``::
164
190
 
165
191
  {"job": <JobRecord>,
166
192
  "package": {"download_url": "...", "tar_sha256": "sha256:...", "size_bytes": 123456},
@@ -495,10 +521,19 @@ MAX_SEED_REF_CHARS = 80
495
521
  SEED_SOURCE_CONFLICT_CODE = "seed_source_conflict"
496
522
 
497
523
  # --------------------------------------------------------------------------
498
- # Worker-facing routes (bearer = static WORKER_TOKEN, constant-time compare;
499
- # empty server-side config disables every route below, fail-closed)
524
+ # Worker-facing routes (bearer = a per-worker identity JWT this bootstrap
525
+ # route mints; empty server-side signing-secret config disables every
526
+ # OTHER route below, fail-closed — see the module docstring's trust zones)
500
527
  # --------------------------------------------------------------------------
501
528
 
529
+ #: ``POST`` — bind (or re-bind) a worker identity to the calling EC2 instance
530
+ #: via an IAM-signed instance identity document. Body
531
+ #: ``{"signature_b64": "..."}``. Carries NO shared secret of its own — the
532
+ #: signed document IS this route's authentication, which is the whole point
533
+ #: of it existing: it is the ONE route that MINTS the token every OTHER
534
+ #: ``/internal/worker/*`` route now requires, so it cannot itself require
535
+ #: that token without a chicken-and-egg cycle.
536
+ WORKER_BOOTSTRAP_ROUTE = "/internal/worker/bootstrap"
502
537
  #: ``POST`` — atomically claim the next queued job (``FOR UPDATE SKIP LOCKED``).
503
538
  WORKER_CLAIM_ROUTE = "/internal/worker/claim"
504
539
  #: ``POST`` — append ordinary-job raw log bytes at a byte offset
@@ -850,6 +885,31 @@ RESERVED_RUNTIME_ENV_KEYS = frozenset(
850
885
  "SIMULO_VIEWSTREAM_ENABLED",
851
886
  "SIMULO_RUNTIME_FACTS_PATH",
852
887
  "MAX_JOB_TIMEOUT_S",
888
+ # M40-job-sandbox (issue #527): the container launcher sets this to
889
+ # advertise the job container's publicly reachable stream endpoint
890
+ # (`--/app/livestream/publicEndpointAddress=<instance IP>`) to Kit —
891
+ # a bridge-networked job container needs this because its own
892
+ # address is NAT'd, unlike the pre-M40 host-networking deployment
893
+ # where Kit's default self-detected address was already correct.
894
+ # `simulo/backend/isaaclab/stream_adapter.py`'s `get_launch_args`
895
+ # merges this env var in LAST, so an unreserved key would let a
896
+ # manifest's own `runtime_env` override the launcher's own address
897
+ # and redirect where the job's live-view media is advertised
898
+ # (ADR-0016's threat table: "Job redirects the stream endpoint via a
899
+ # job-controllable env key"). Reserving it here is the
900
+ # denylist-enforcement half of the fix; `worker._child_env` is the
901
+ # other half, unconditionally scrubbing any ambient value before the
902
+ # launcher re-sets it from the resolved instance address.
903
+ "SIMULO_KIT_EXTRA_ARGS",
904
+ # M40-job-sandbox (issue #527): the same "worker mount wins" seam as
905
+ # SIMULO_CHECKPOINT_DIR / SIMULO_ANOMALY_DEBUG_DIR — the worker
906
+ # pre-resolves and bind-mounts the validation run-dir output
907
+ # contract (verdict/report/facts/runtime-findings) at this path
908
+ # before an asset-validation job's container starts, because that
909
+ # container's own `~` is ephemeral tmpfs the worker could never
910
+ # read back. A manifest-borne override would point the worker's own
911
+ # later read at a job-chosen path instead.
912
+ "SIMULO_VALIDATION_OUTPUT_DIR",
853
913
  }
854
914
  )
855
915
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simulo-interfaces
3
- Version: 0.19.0
3
+ Version: 0.20.0
4
4
  Summary: Type contracts for the Simulo SDK. Installed automatically as a dependency of the simulo package.
5
5
  Author-email: Simulo Team <team@simulo.ai>
6
6
  License: BSD-3-Clause