Skip to content

Promote device-py SDK: mirurobotics/python-device-sdk-staging -> mirurobotics/python-device-sdk - #12

Merged
stlc-promoter-bot[bot] merged 9 commits into
mainfrom
stlc/release
Oct 1, 2026
Merged

stlc-promoter-bot[bot] merged 9 commits into
mainfrom
stlc/release

Conversation

@stlc-promoter-bot

@stlc-promoter-bot stlc-promoter-bot Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Promote the device-py SDK from staging to production.

  • Staging: mirurobotics/python-device-sdk-staging
  • Production: mirurobotics/python-device-sdk

This PR is for review/visibility of the exact SHA being
promoted. Production main does NOT advance by merging this PR:
it advances by a fast-forward push of staging's reviewed
commit(s)
(no merge commit). Once main is fast-forwarded to
the head of this branch, GitHub auto-closes this PR as merged with
no merge commit (head == base, a pure fast-forward).

Note: opened by the stlc Promoter Bot app (Pull requests: write).

Changes (production/main..origin/main):

c620e55 feat(client): reach the agent over loopback TCP with its discovery file (#3)
63afc05 feat(device): bearer auth and SDK client options for the agent's loopback TCP API (#302)
88be15a ci: AI review of pull requests (#1)
542e06f feat(configs): Windows file rule globs and read-only os fields (#293)
c1555c7 feat(device): add file_rule_ids to releases and GET /file_rules/{file_rule_id} (#291)
8218315 chore(sdkgen): repin stlc-python to surgical re-lock (570bf02) (#287)
d202ddf chore(sdkgen): repin stlc-python at a7fb576, set python floor >= 3.10 (#285)
926c448 feat(cli): add POST /config_schemas/validate dry-run endpoint (#242)
1cae83b fix(stlc): make generate CI install work on Blacksmith (ssh-auth + npm cache) (#147)

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

stlc-bot and others added 9 commits June 27, 2026 03:29
…m cache) (#147)

Stainless-Generated-From: 097dd3a49950a713b5410842ac6680f911bf2607
Stainless-Generated-From: 67a902eca03a9fcc2b2c9abd489f08d22978b885
… (#285)

Stainless-Generated-From: ff7275bbcc2769850710869be2df9eeba287ae78
Stainless-Generated-From: a9d02554b0e04697604f55e7aeaf8a021666c231
…_rule_id} (#291)

Stainless-Generated-From: 6b4dcc4c65ca27b288212e4c7e2b46aaab38909b
Stainless-Generated-From: 4a85f0562b4acc8f1fb20a2c05b3653f6174eeae
## Why

Hand-written SDK changes (custom code on top of Stainless output) will
land on staging `main` by pull request, once mirurobotics/infra#294
allows PR merges here. This adds the org's AI review to those PRs, like
the other Miru repos.

## What

New `.github/workflows/ai-review.yml`. It calls `mirurobotics/.github`'s
reusable AI review workflow, pinned to the same commit as openapi's, and
posts the `ai-review` status.

- **`pull_request_target`:** runs from the base branch, so a PR can't
edit or remove its own review. As a consequence, **this PR doesn't
review itself**; the first PR reviewed is the one after it.
- **Credential:** uses the org secret `CLAUDE_CODE_OAUTH_TOKEN`, which
this repo already sees, so no new secrets are needed.
- **Staging only:** the job runs only in `*-staging` repos. Promote
copies this file to the public production repo, where it is skipped.
- **Kept on regeneration:** this file is custom code. The next `stlc
build` seals it, so codegen keeps it.

## Follow-up

Once this is merged, a small infra PR adds `ai-review` as a required
status in the staging `protect-sdk-main` ruleset.

## Merge order

This can't merge until infra#294 is applied: today the ruleset blocks
every change to `main`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/mirurobotics/codesmith/python-device-sdk-staging/pr/1?autoLogin=true&ref=codesmith_pr_footer"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1793465555&installation_model_id=425273&pr_number=1&ref=codesmith_pr_footer&repository=mirurobotics%2Fpython-device-sdk-staging&return_to=https%3A%2F%2Fgithub.com%2Fmirurobotics%2Fpython-device-sdk-staging%2Fpull%2F1&signature=9246884df003a38fa1c2a62e0d8944614ba1040c6cdcc2a4beda46fb3979a29d"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>@codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…back TCP API (#302)

Stainless-Generated-From: 1a769a355d35ca905ebdfa44cd646d64e943aa88
…le (#3)

## Summary

The Python device SDK can now reach the Miru Agent over loopback TCP,
using the bearer token the agent publishes in its discovery file
(mirurobotics/agent#273). TCP is the default on Windows; the Unix socket
stays the default elsewhere.

This replaces #2. The bearer token is now a **Stainless-generated**
option, and our custom code feeds it through the generated client's own
hooks instead of rewriting requests in a transport.

## Review the second commit

- **`chore: regenerate ...`** is Stainless output for
mirurobotics/openapi#302, not hand-written: the `agent_transport`,
`discovery_file` and `bearer_token` options, plus the generated
`Authorization: Bearer` header. Once #302 merges and codegen pushes the
same output to `main`, I'll merge `main` into this branch and this
commit drops out of the diff.
- **`feat(client): ...`** is the hand-written code. Review this one.

## What

**`src/miru_device_sdk/_client.py` (custom edits to the generated
client):**
- **Token:** `auth_headers` falls back to the discovery-file token when
`bearer_token` isn't set. The header itself is still built by the
generated code.
- **Address:** a `_prepare_url` override points requests at
`127.0.0.1:<port>` from the discovery file. It uses `127.0.0.1` rather
than `localhost` because the agent listens on IPv4 loopback only and
`localhost` can resolve to `::1`.
- **Restarts:** a `_should_retry` override retries a 401 once, and only
if the file now has a different token.
- **Transport:** the builders pick the transport. The Unix socket is
used as before, plain TCP when `bearer_token` is set, and TCP with
discovery otherwise.

**`src/miru_device_sdk/lib/device_api.py` (new).** It's in `lib/`, which
Stainless never generates into.
- `read_discovery`: reads the file and closes it straight away, because
on Windows an open handle blocks the agent from replacing the file.
- Reads the agent's `{"port", "token"}` file, checks the port (1–65535)
and a non-empty token, and ignores other fields.
- Raises `DiscoveryError` with a plain message for a missing file, a
permission error (on Windows it names the `Miru Agent Users` group from
agent#276), invalid JSON or invalid fields.
- `DiscoveryFile`: caches the last read of each path. All clients using
the same path share it, including `copy()`.
  - It re-reads only after being marked stale.
- If the file is missing during an agent restart, it keeps the last
values, so the request's retry gets another chance.
- `DiscoveryTransport` / `AsyncDiscoveryTransport`: only mark the file
stale when the agent refuses a connection, then re-raise. The client's
retry, or its next request, reads the file again. They don't rewrite
requests.
- `resolve_transport` and `default_discovery_file`: unchanged from #2.

**Setting `bearer_token` turns discovery off.** The client then uses
`base_url` and the token as given. An empty `MIRU_AGENT_TOKEN` counts as
unset.

**`README.md`:** a "Connecting to the agent" section. It replaces the
generated advice to keep `MIRU_AGENT_TOKEN` in a `.env` file, which
doesn't fit a token that changes on every agent start.

## Testing

`tests/test_device_api.py` has 43 tests:
- reading and checking the file;
- the cache: staleness, keeping the last values while the file is
missing, the shared instance per path;
- through the client with a scripted agent:
- a 401 retried with a new token, and not retried when the token is
unchanged;
  - a refused connection retried on a new port;
  - recovery with `max_retries=0`;
  - a missing file;
  - `copy()`;
  - an explicit token;
  - no token over the Unix socket;
- end to end against a real local HTTP server that requires the token
and switches to a new one mid-test, sync and async;
- transport selection.

`./scripts/lint` (ruff, pyright, mypy) and `./scripts/test` are clean:
521 tests pass on Pydantic v2 and 508 on v1.

## After merge

Run openapi's `stlc-sync-tracking.yml` to record this as sealed custom
code, then promote and release.

## Merge order

1. mirurobotics/openapi#302 (the bearer auth scheme and client options),
then its codegen
2. this PR (the AI review must pass)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/mirurobotics/codesmith/python-device-sdk-staging/pr/3?autoLogin=true&ref=codesmith_pr_footer"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1793470816&installation_model_id=425273&pr_number=3&ref=codesmith_pr_footer&repository=mirurobotics%2Fpython-device-sdk-staging&return_to=https%3A%2F%2Fgithub.com%2Fmirurobotics%2Fpython-device-sdk-staging%2Fpull%2F3&signature=24ba6e774f0aad2e73c6698b7a5f5734747fbf3042ec8c9512a6acdd5c59c6e4"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with [code]smith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>@codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@stlc-promoter-bot
stlc-promoter-bot Bot merged commit c620e55 into main Oct 1, 2026
3 checks passed
@stlc-promoter-bot
stlc-promoter-bot Bot deleted the stlc/release branch October 1, 2026 22:05
@stlc-promoter-bot
stlc-promoter-bot Bot restored the stlc/release branch October 1, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant