driftstack-sdk 0.2.0__tar.gz → 0.3.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.
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/CHANGELOG.md +72 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/PKG-INFO +1 -1
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/pyproject.toml +1 -1
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/_generated/models.py +52 -8
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/_version.py +1 -1
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/errors.py +13 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/agent_sessions.py +3 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/.gitignore +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/LICENSE +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/README.md +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/__init__.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/_generated/__init__.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/client.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/http.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/pagination.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/py.typed +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/__init__.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/_common.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/account.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/api_keys.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/archetypes.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/audit_log.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/auth.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/billing.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/crypto_orders.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/egress.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/email_preferences.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/legal.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/mfa.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/profile_snapshots.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/profiles.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/recipes.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/sessions.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/team.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/usage.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/resources/webhooks.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/retry.py +0 -0
- {driftstack_sdk-0.2.0 → driftstack_sdk-0.3.0}/src/driftstack/webhook_signature.py +0 -0
|
@@ -6,6 +6,78 @@ follows [SemVer](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.3.0] - 2026-09-22
|
|
10
|
+
|
|
11
|
+
**Nothing was removed.** Every name 0.2.0 exported, every method, every
|
|
12
|
+
keyword argument and every error class is still here and still means the
|
|
13
|
+
same thing, so upgrading takes no code change on its own — except where
|
|
14
|
+
**Migration** below says a string comparison needs updating. Every addition
|
|
15
|
+
below exists on **both** `Driftstack` and `AsyncDriftstack`.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **`EgressCapabilities.safeguards`** — `"passed"`, `"failed"` or
|
|
20
|
+
`"unverified"`, summarising whether every egress safeguard held for a
|
|
21
|
+
session. `"failed"` wins whenever any check did not pass; `"passed"` only
|
|
22
|
+
when the device declared the full set of checks a healthy session reports
|
|
23
|
+
and every one of them reported back; `"unverified"` otherwise. The field is
|
|
24
|
+
**absent**, not `None`, on a session reported before it existed — do not
|
|
25
|
+
read an absent value as `"unverified"` or `"passed"`. Rides everywhere
|
|
26
|
+
`egress_capabilities` already does: `sessions.get()` / `.list()` /
|
|
27
|
+
`.create()`, `profiles.launch()`, and the
|
|
28
|
+
`session.egress_capability_changed` webhook payload.
|
|
29
|
+
- **`measured_by`** on a `?check=full` proxy test result
|
|
30
|
+
(`egress.test_proxy(proxy_id)`) — `"phone"` when a real phone session took
|
|
31
|
+
the measurement, `"driftstack"` when Driftstack itself did because no phone
|
|
32
|
+
could be reached in time. The field this replaces, `measured_from`, is
|
|
33
|
+
still sent beside it with its original values for existing integrations,
|
|
34
|
+
but is no longer documented; read `measured_by` from here on.
|
|
35
|
+
- **`direct_reading` and `website_like_reading`** on `os_fingerprint` — the
|
|
36
|
+
same two facts `single_host_vantage` and `web_port_vantage` already carry,
|
|
37
|
+
under plainer names, added beside the originals rather than replacing
|
|
38
|
+
them. Present on the proxy test result **and** on each saved
|
|
39
|
+
proxy returned by `egress.list_proxies()` / `.update_proxy(proxy_id, body)`.
|
|
40
|
+
- **`"page_unreadable"`** joins the `AgentNoticeReason` values a
|
|
41
|
+
`plan-executed` result can carry: the page could not be read to plan the
|
|
42
|
+
next step, so the task stopped rather than guess. Send `continue` to try
|
|
43
|
+
again.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **Two dead `egress_capabilities.warnings` codes are retired from the
|
|
48
|
+
documentation**: `quic_disabled_fallback_http2` and
|
|
49
|
+
`dns_remote_resolve_unsupported_by_proxy`. Neither has ever been sent, so
|
|
50
|
+
this is a documentation correction, not a behavioural change.
|
|
51
|
+
|
|
52
|
+
### Migration
|
|
53
|
+
|
|
54
|
+
Two closed-string fields were narrowed — values removed, not added — inside
|
|
55
|
+
the `?check=full` proxy test result. `egress.test_proxy()` and
|
|
56
|
+
`egress.list_proxies()` return a plain `dict`, not a validated model, so the
|
|
57
|
+
affected `AccountProxyTestResult*` / `OsFingerprint` models in
|
|
58
|
+
`driftstack._generated.models` are typing-only for these two fields: neither
|
|
59
|
+
change raises at call time, and code comparing a value against one of the old
|
|
60
|
+
strings simply stops matching, silently. The server has sent the new values
|
|
61
|
+
only since 2026-09-21.
|
|
62
|
+
|
|
63
|
+
- **`not_run`** — `"node_busy"`, `"node_error"` and `"no_node"` merged into
|
|
64
|
+
`"check_unavailable"` (you can do exactly one thing about any of the
|
|
65
|
+
three: try again shortly, or contact support if it persists);
|
|
66
|
+
`"unresolvable"` is now `"config_unresolvable"`, matching the word the
|
|
67
|
+
"why a launch is refused" vocabulary already used for the identical fact.
|
|
68
|
+
`"live_session"` is unchanged.
|
|
69
|
+
- **`os_fingerprint_unavailable`** — `"vpn_tunnel"` is now
|
|
70
|
+
`"not_available_for_vpn"`, `"not_observed"` is now `"not_captured"`, and
|
|
71
|
+
`"observer_off"` is now `"not_offered_here"`.
|
|
72
|
+
|
|
73
|
+
Update any code that compares `not_run` or `os_fingerprint_unavailable`
|
|
74
|
+
against one of the old strings to compare against its replacement instead.
|
|
75
|
+
|
|
76
|
+
### Pre-1.0 stability
|
|
77
|
+
|
|
78
|
+
The SDK is pre-1.0. Pin `driftstack-sdk~=0.3.0` rather than an exact version
|
|
79
|
+
and read this file before bumping.
|
|
80
|
+
|
|
9
81
|
## [0.2.0] - 2026-09-20
|
|
10
82
|
|
|
11
83
|
The release the guide [Run AI tasks from your
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: driftstack-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Driftstack Python SDK — stealth iPhone Safari automation. Import as `driftstack`.
|
|
5
5
|
Project-URL: Homepage, https://driftstack.io
|
|
6
6
|
Project-URL: Repository, https://github.com/driftstackdev/driftstack-api
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# generated by datamodel-codegen:
|
|
2
2
|
# filename: openapi.json
|
|
3
|
-
# timestamp: 2026-09-
|
|
3
|
+
# timestamp: 2026-09-21T22:53:06+00:00
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
@@ -149,7 +149,14 @@ class EgressCapabilities(BaseModel):
|
|
|
149
149
|
udp_associate: bool
|
|
150
150
|
quic_route: Literal["proxy", "direct", "disabled"]
|
|
151
151
|
dns_remote_resolve: bool
|
|
152
|
+
safeguards: Literal["passed", "failed", "unverified"] | None = None
|
|
153
|
+
"""
|
|
154
|
+
Whether every egress safeguard held for this session. Absent (the key is missing entirely, never `null`) on a row written before this field existed — read an absent value as unknown, never as `unverified` and never as `passed`. `passed` — every safeguard check the device reported passed, and the device declared the full set of checks a healthy session reports, so nothing was left unchecked. `failed` — at least one safeguard check did not pass. Stop relying on the session and contact support with the session id. Takes precedence over `unverified` whenever both could apply. `unverified` — we could not establish that every safeguard held: no checks were reported, a declared check never reported back, or the device declared no expected set of checks at all. Nothing is known to have failed, but completeness could not be confirmed either — for anything that matters, treat the session the same way you would `failed`.
|
|
155
|
+
"""
|
|
152
156
|
warnings: list[str] | None = []
|
|
157
|
+
"""
|
|
158
|
+
Codes naming anything that did not work as asked for this session. Read them as opaque strings: a code you do not recognise is ignored rather than fatal, and a new code can appear without an SDK upgrade. Published vocabulary, with what you can do about each: `udp_unsupported_by_proxy` (the proxy refused UDP, so QUIC cannot travel through it — use a UDP-capable proxy if you need HTTP/3); `quic_unavailable` (QUIC was asked for but could not be used, and traffic fell back to HTTP/2 — retry on a new session if HTTP/3 matters); `dead_proxy` (the proxy stopped answering mid-session — check it is reachable before starting another); `streaming_blank` (the live view produced no picture; the session itself kept running — reopen the view); `streaming_failed` (the live view stopped — start a new session if you need to watch it); `safeguards_unverified` (we could not confirm every egress safeguard ran — treat the egress as unverified and start a new session if that matters); `safeguard_failed` (an egress safeguard did not pass — stop relying on the session and contact support with the session id); `safeguard_failed:direct_internet_block` (the check that nothing leaves outside your proxy did not pass — contact support with the session id); `safeguard_failed:browser_integrity` (the check that the session ran the expected browser build did not pass — contact support with the session id); `safeguard_failed:proxy_egress_verification` (the check that traffic actually left through your proxy did not pass — confirm your proxy and contact support with the session id); `safeguard_failed:live_view_capture` (the live view could not be captured; the session’s own browsing is unaffected).
|
|
159
|
+
"""
|
|
153
160
|
|
|
154
161
|
|
|
155
162
|
class Session(BaseModel):
|
|
@@ -1462,11 +1469,19 @@ class OsFingerprint(BaseModel):
|
|
|
1462
1469
|
observed_via: Literal["proxy_host", "exit_ip"]
|
|
1463
1470
|
single_host_vantage: bool
|
|
1464
1471
|
"""
|
|
1465
|
-
True only when the proxy host you entered, the machine that opened the connection and the exit address are all one machine, so the reading describes the same path a website sees. Absent or false: do not draw a match/mismatch conclusion from it.
|
|
1472
|
+
True only when the proxy host you entered, the machine that opened the connection and the exit address are all one machine, so the reading describes the same path a website sees. Absent or false: do not draw a match/mismatch conclusion from it. Same fact as `direct_reading` below, kept under its original name for existing integrations.
|
|
1466
1473
|
"""
|
|
1467
1474
|
web_port_vantage: bool
|
|
1468
1475
|
"""
|
|
1469
|
-
True when the reading was taken on the standard HTTPS port at the proxy's IP address, with no CDN in front — the same path a website connects on. It describes what a site sees on that path; with observed_via "proxy_host" it is still a reading, not a guarantee.
|
|
1476
|
+
True when the reading was taken on the standard HTTPS port at the proxy's IP address, with no CDN in front — the same path a website connects on. It describes what a site sees on that path; with observed_via "proxy_host" it is still a reading, not a guarantee. Same fact as `website_like_reading` below, kept under its original name for existing integrations.
|
|
1477
|
+
"""
|
|
1478
|
+
direct_reading: bool | None = None
|
|
1479
|
+
"""
|
|
1480
|
+
Whether nothing sat between what was read and what your traffic actually exits from: true only when the address you gave, the address that answered, and your exit address are one machine. The customer-worded name for `single_host_vantage`.
|
|
1481
|
+
"""
|
|
1482
|
+
website_like_reading: bool | None = None
|
|
1483
|
+
"""
|
|
1484
|
+
Whether the reading was taken the way a real website connection is — a literal address on the standard secure-web port, not a name that could route to shared infrastructure. The customer-worded name for `web_port_vantage`.
|
|
1470
1485
|
"""
|
|
1471
1486
|
|
|
1472
1487
|
|
|
@@ -1551,9 +1566,16 @@ class AccountProxyTestResult1(BaseModel):
|
|
|
1551
1566
|
os_fingerprint: OsFingerprint | None = None
|
|
1552
1567
|
os_fingerprint_at: str | None = None
|
|
1553
1568
|
os_fingerprint_unavailable: (
|
|
1554
|
-
Literal["
|
|
1569
|
+
Literal["not_available_for_vpn", "not_captured", "not_offered_here"] | None
|
|
1555
1570
|
) = None
|
|
1571
|
+
"""
|
|
1572
|
+
Why no OS fingerprint was taken, when Driftstack knows the cause: `not_available_for_vpn` (an openvpn or wireguard proxy has no single address of its own to read a stack from — no retry can help); `not_captured` (the reading was attempted and produced nothing this time — a retry may help); `not_offered_here` (this deployment does not take this reading at all — no retry can help).
|
|
1573
|
+
"""
|
|
1556
1574
|
measured_from: Literal["fleet", "control_plane"] | None = None
|
|
1575
|
+
measured_by: Literal["phone", "driftstack"] | None = None
|
|
1576
|
+
"""
|
|
1577
|
+
Where this result was measured. `phone` — a real phone session took the measurement, the same machine `check=full` dispatches to. `driftstack` — Driftstack itself measured it: the same path `check=quick` always takes, and the honest fallback when a `check=full` request could not reach a phone in time. Present only on a `check=full` result; `check=quick` is always `driftstack` and carries no field to say so.
|
|
1578
|
+
"""
|
|
1557
1579
|
|
|
1558
1580
|
|
|
1559
1581
|
class ExitObserved1(BaseModel):
|
|
@@ -1569,7 +1591,16 @@ class AccountProxyTestResult2(BaseModel):
|
|
|
1569
1591
|
ok: Literal[False]
|
|
1570
1592
|
reason: str
|
|
1571
1593
|
measured_from: Literal["fleet", "control_plane"] | None = None
|
|
1572
|
-
|
|
1594
|
+
measured_by: Literal["phone", "driftstack"] | None = None
|
|
1595
|
+
"""
|
|
1596
|
+
Where this result was measured. `phone` — a real phone session took the measurement, the same machine `check=full` dispatches to. `driftstack` — Driftstack itself measured it: the same path `check=quick` always takes, and the honest fallback when a `check=full` request could not reach a phone in time. Present only on a `check=full` result; `check=quick` is always `driftstack` and carries no field to say so.
|
|
1597
|
+
"""
|
|
1598
|
+
not_run: (
|
|
1599
|
+
Literal["live_session", "config_unresolvable", "check_unavailable"] | None
|
|
1600
|
+
) = None
|
|
1601
|
+
"""
|
|
1602
|
+
Present when NOTHING RAN, so `ok:false` is not a result about the proxy: `live_session` (a full check of a VPN proxy was skipped because a live session is browsing through it — end the session to check it); `config_unresolvable` (the stored configuration could not be used, so nothing was dialled — re-add it); `check_unavailable` (the full check could not be completed on our side right now — try again shortly, or full checks are not available on this deployment).
|
|
1603
|
+
"""
|
|
1573
1604
|
exit_observed: ExitObserved1 | None = None
|
|
1574
1605
|
|
|
1575
1606
|
|
|
@@ -1585,8 +1616,17 @@ class AccountProxyTestResult3(BaseModel):
|
|
|
1585
1616
|
ok: bool
|
|
1586
1617
|
reason: str | None = None
|
|
1587
1618
|
measured_from: Literal["fleet"]
|
|
1619
|
+
measured_by: Literal["phone"]
|
|
1620
|
+
"""
|
|
1621
|
+
Where this result was measured. `phone` — a real phone session took the measurement, the same machine `check=full` dispatches to. `driftstack` — Driftstack itself measured it: the same path `check=quick` always takes, and the honest fallback when a `check=full` request could not reach a phone in time. Present only on a `check=full` result; `check=quick` is always `driftstack` and carries no field to say so.
|
|
1622
|
+
"""
|
|
1588
1623
|
node_id: str
|
|
1589
|
-
not_run:
|
|
1624
|
+
not_run: (
|
|
1625
|
+
Literal["live_session", "config_unresolvable", "check_unavailable"] | None
|
|
1626
|
+
) = None
|
|
1627
|
+
"""
|
|
1628
|
+
Present when NOTHING RAN, so `ok:false` is not a result about the proxy: `live_session` (a full check of a VPN proxy was skipped because a live session is browsing through it — end the session to check it); `config_unresolvable` (the stored configuration could not be used, so nothing was dialled — re-add it); `check_unavailable` (the full check could not be completed on our side right now — try again shortly, or full checks are not available on this deployment).
|
|
1629
|
+
"""
|
|
1590
1630
|
reachable: bool | None = None
|
|
1591
1631
|
auth_ok: bool | None = None
|
|
1592
1632
|
udp_associate: bool | None = None
|
|
@@ -1607,8 +1647,11 @@ class AccountProxyTestResult3(BaseModel):
|
|
|
1607
1647
|
os_fingerprint: OsFingerprint | None = None
|
|
1608
1648
|
os_fingerprint_at: str | None = None
|
|
1609
1649
|
os_fingerprint_unavailable: (
|
|
1610
|
-
Literal["
|
|
1650
|
+
Literal["not_available_for_vpn", "not_captured", "not_offered_here"] | None
|
|
1611
1651
|
) = None
|
|
1652
|
+
"""
|
|
1653
|
+
Why no OS fingerprint was taken, when Driftstack knows the cause: `not_available_for_vpn` (an openvpn or wireguard proxy has no single address of its own to read a stack from — no retry can help); `not_captured` (the reading was attempted and produced nothing this time — a retry may help); `not_offered_here` (this deployment does not take this reading at all — no retry can help).
|
|
1654
|
+
"""
|
|
1612
1655
|
|
|
1613
1656
|
|
|
1614
1657
|
class AccountProxyTestResult(
|
|
@@ -2024,6 +2067,7 @@ class AgentMessageResponse1(BaseModel):
|
|
|
2024
2067
|
"no_progress",
|
|
2025
2068
|
"repeated_step",
|
|
2026
2069
|
"ai_unavailable",
|
|
2070
|
+
"page_unreadable",
|
|
2027
2071
|
"question",
|
|
2028
2072
|
"declined",
|
|
2029
2073
|
]
|
|
@@ -2031,7 +2075,7 @@ class AgentMessageResponse1(BaseModel):
|
|
|
2031
2075
|
| None
|
|
2032
2076
|
) = None
|
|
2033
2077
|
"""
|
|
2034
|
-
Why the turn ended, in one word, whenever `notice` is present — never without it. `step_limit`: the task needs more steps than one message runs; send "continue". `time_limit`: the message was taking too long; send "continue". `budget_low`: too little of the session’s AI budget is left; start a new session. `no_progress`: the page stopped changing and the next step would repeat one that changed nothing; a person should say what to try instead. `repeated_step`: the next step would have repeated an action that already ran, which could do it twice; check the page, then send "continue" if it is safe. `ai_unavailable`: the next steps could not be worked out just now; send "continue" to try again. `question`: the agent asked you something part-way; `notice` is the question, and your answer is the next message. `declined`: the agent stopped rather than carry on; a person should decide what to do. Treat it as an OPEN string: match the values you know, and show `notice` for any other.
|
|
2078
|
+
Why the turn ended, in one word, whenever `notice` is present — never without it. `step_limit`: the task needs more steps than one message runs; send "continue". `time_limit`: the message was taking too long; send "continue". `budget_low`: too little of the session’s AI budget is left; start a new session. `no_progress`: the page stopped changing and the next step would repeat one that changed nothing; a person should say what to try instead. `repeated_step`: the next step would have repeated an action that already ran, which could do it twice; check the page, then send "continue" if it is safe. `ai_unavailable`: the next steps could not be worked out just now; send "continue" to try again. `page_unreadable`: the page could not be read to plan the next step; send "continue" to try again. `question`: the agent asked you something part-way; `notice` is the question, and your answer is the next message. `declined`: the agent stopped rather than carry on; a person should decide what to do. Treat it as an OPEN string: match the values you know, and show `notice` for any other.
|
|
2035
2079
|
"""
|
|
2036
2080
|
usage: AgentMessageUsage | None = None
|
|
2037
2081
|
|
|
@@ -33,6 +33,19 @@ def _coerce_int(value: Any) -> int:
|
|
|
33
33
|
return 0
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def _coerce_optional_int(value: Any) -> int | None:
|
|
37
|
+
"""Like :func:`_coerce_int`, but ``None`` stays ``None`` rather than
|
|
38
|
+
becoming 0 — for fields whose ABSENCE is meaningful (e.g. a 'balance'
|
|
39
|
+
refusal carries no ``debt_credits`` at all, which must not read as a
|
|
40
|
+
debt of exactly zero)."""
|
|
41
|
+
if value is None:
|
|
42
|
+
return None
|
|
43
|
+
try:
|
|
44
|
+
return int(value)
|
|
45
|
+
except (TypeError, ValueError):
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
36
49
|
class DriftstackError(Exception):
|
|
37
50
|
"""Base for every error raised by the Driftstack SDK.
|
|
38
51
|
|
|
@@ -115,6 +115,8 @@ CanonicalModifier = Literal["cmd", "ctrl", "shift", "option"]
|
|
|
115
115
|
#: ``"continue"`` if it is safe.
|
|
116
116
|
#: - ``"ai_unavailable"`` — the next steps could not be worked out just now.
|
|
117
117
|
#: Send ``"continue"`` to try again.
|
|
118
|
+
#: - ``"page_unreadable"`` — the page could not be read to plan the next step.
|
|
119
|
+
#: Send ``"continue"`` to try again.
|
|
118
120
|
#: - ``"question"`` — the agent asked you something part-way. ``notice`` is the
|
|
119
121
|
#: question; send your answer as the next message.
|
|
120
122
|
#: - ``"declined"`` — the agent stopped rather than carry on. A person should
|
|
@@ -131,6 +133,7 @@ AgentNoticeReason = (
|
|
|
131
133
|
"no_progress",
|
|
132
134
|
"repeated_step",
|
|
133
135
|
"ai_unavailable",
|
|
136
|
+
"page_unreadable",
|
|
134
137
|
"question",
|
|
135
138
|
"declined",
|
|
136
139
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|