codai-sdk 0.7.0__tar.gz → 0.52.1__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 (87) hide show
  1. codai_sdk-0.52.1/CHANGELOG.md +731 -0
  2. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/LICENSE +1 -1
  3. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/PKG-INFO +22 -1
  4. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/README.md +21 -0
  5. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/pyproject.toml +1 -1
  6. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/__init__.py +111 -1
  7. codai_sdk-0.52.1/src/codai/_operations.py +388 -0
  8. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/_resolve_types.py +14 -0
  9. codai_sdk-0.52.1/src/codai/_types.py +9035 -0
  10. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/__init__.py +83 -0
  11. codai_sdk-0.52.1/src/codai/resources/abuse_reports.py +44 -0
  12. codai_sdk-0.52.1/src/codai/resources/batches.py +161 -0
  13. codai_sdk-0.52.1/src/codai/resources/billing.py +64 -0
  14. codai_sdk-0.52.1/src/codai/resources/brand_kits.py +125 -0
  15. codai_sdk-0.52.1/src/codai/resources/cards.py +249 -0
  16. codai_sdk-0.52.1/src/codai/resources/core.py +880 -0
  17. codai_sdk-0.52.1/src/codai/resources/creatives.py +691 -0
  18. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/environments.py +68 -0
  19. codai_sdk-0.52.1/src/codai/resources/exports.py +48 -0
  20. codai_sdk-0.52.1/src/codai/resources/lists.py +97 -0
  21. codai_sdk-0.52.1/src/codai/resources/memory.py +51 -0
  22. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/platform.py +45 -2
  23. codai_sdk-0.52.1/src/codai/resources/prompts.py +86 -0
  24. codai_sdk-0.52.1/src/codai/resources/provenance.py +45 -0
  25. codai_sdk-0.52.1/src/codai/resources/referrals.py +28 -0
  26. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/resolve.py +33 -1
  27. codai_sdk-0.52.1/src/codai/resources/sandboxes.py +220 -0
  28. codai_sdk-0.52.1/src/codai/resources/schedules.py +166 -0
  29. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/sessions.py +32 -4
  30. codai_sdk-0.52.1/src/codai/resources/shares.py +141 -0
  31. codai_sdk-0.52.1/src/codai/resources/templates.py +35 -0
  32. codai_sdk-0.52.1/src/codai/resources/transcripts.py +319 -0
  33. codai_sdk-0.52.1/src/codai/resources/usage_keys.py +39 -0
  34. codai_sdk-0.52.1/src/codai/resources/voice_revocations.py +29 -0
  35. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/tests/conftest.py +3 -2
  36. codai_sdk-0.52.1/tests/test_abuse_reports.py +37 -0
  37. codai_sdk-0.52.1/tests/test_batches.py +93 -0
  38. codai_sdk-0.52.1/tests/test_billing_referrals.py +132 -0
  39. codai_sdk-0.52.1/tests/test_brand_kits.py +178 -0
  40. codai_sdk-0.52.1/tests/test_cards.py +129 -0
  41. codai_sdk-0.52.1/tests/test_creatives.py +202 -0
  42. codai_sdk-0.52.1/tests/test_creatives_documents.py +84 -0
  43. codai_sdk-0.52.1/tests/test_creatives_e11.py +78 -0
  44. codai_sdk-0.52.1/tests/test_creatives_e11b.py +89 -0
  45. codai_sdk-0.52.1/tests/test_environments_exec.py +55 -0
  46. codai_sdk-0.52.1/tests/test_exports.py +123 -0
  47. codai_sdk-0.52.1/tests/test_lists.py +92 -0
  48. codai_sdk-0.52.1/tests/test_memory.py +42 -0
  49. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/tests/test_parity.py +7 -1
  50. codai_sdk-0.52.1/tests/test_provenance.py +29 -0
  51. codai_sdk-0.52.1/tests/test_resources.py +1213 -0
  52. codai_sdk-0.52.1/tests/test_sandboxes.py +133 -0
  53. codai_sdk-0.52.1/tests/test_settings.py +45 -0
  54. codai_sdk-0.52.1/tests/test_shares_e12a.py +149 -0
  55. codai_sdk-0.52.1/tests/test_templates.py +58 -0
  56. codai_sdk-0.52.1/tests/test_transcripts.py +174 -0
  57. codai_sdk-0.52.1/tests/test_voice_revocations.py +23 -0
  58. codai_sdk-0.52.1/uv.lock +294 -0
  59. codai_sdk-0.7.0/CHANGELOG.md +0 -149
  60. codai_sdk-0.7.0/src/codai/_operations.py +0 -148
  61. codai_sdk-0.7.0/src/codai/_types.py +0 -3160
  62. codai_sdk-0.7.0/src/codai/resources/core.py +0 -209
  63. codai_sdk-0.7.0/tests/test_resources.py +0 -455
  64. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  65. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  66. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/.github/dependabot.yml +0 -0
  67. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/.github/pull_request_template.md +0 -0
  68. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/.github/workflows/publish.yml +0 -0
  69. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/.gitignore +0 -0
  70. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/CODE_OF_CONDUCT.md +0 -0
  71. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/CONTRIBUTING.md +0 -0
  72. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/SECURITY.md +0 -0
  73. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/examples/agent.py +0 -0
  74. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/examples/chat.py +0 -0
  75. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/examples/feedback.py +0 -0
  76. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/examples/streaming.py +0 -0
  77. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/scripts/gen-types.py +0 -0
  78. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/scripts/smoke_local.py +0 -0
  79. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/_http.py +0 -0
  80. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/_base.py +0 -0
  81. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/agents.py +0 -0
  82. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/chat.py +0 -0
  83. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/messages.py +0 -0
  84. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/responses.py +0 -0
  85. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/src/codai/resources/triggers.py +0 -0
  86. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/tests/test_http.py +0 -0
  87. {codai_sdk-0.7.0 → codai_sdk-0.52.1}/tests/test_resolve.py +0 -0
@@ -0,0 +1,731 @@
1
+ # Changelog
2
+
3
+ All notable changes to `codai-sdk` are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.52.1] - 2026-10-16
10
+
11
+ - `WalletLedgerEntry.source` literal gains `sandbox_charge`: the agent sandbox exec-time debit (EUR 0.10 per
12
+ exec-hour after 10 free exec-hours a month), written every 5 minutes once sandbox billing is switched on.
13
+
14
+ ## [0.52.0] - 2026-10-16
15
+
16
+ - `provenance.check(file, content_type=...)` — public AI-provenance checker (`POST /v1/public/provenance/check`,
17
+ no key needed, at most 25 MB; `file` is bytes or a path): reports C2PA manifest presence and claimed issuer, the
18
+ WAV RIFF INFO, MP3 ID3 and MP4 `udta/meta` AI marks and image XMP. `signature_verified` and `synthid_checked`
19
+ are always `False`.
20
+
21
+ ## [0.51.0] - 2026-10-16
22
+
23
+ - `client.sandboxes` (beta) — agent sandboxes over REST (`/v1/sandboxes`): `create` / `list` / `retrieve`, `exec`
24
+ (blocking; `exit_code`, `stdout`, `stderr` and a reproducibility `receipt`), `exec_stream` (generator of event
25
+ dicts: `start`, `stdout`, `stderr`, `truncated`, `exit`, `error`), `files.put` / `files.get` (returns `SandboxFile`
26
+ with `data` + `sha256`) / `files.list`, `artifacts` (scanned files from `/workspace/out` with signed 1 h links),
27
+ `snapshot`, `delete` and one-shot `run`. `exec`, `exec_stream` and `run` accept `idempotency_key=`.
28
+ - Chat `tools` items may now be `{"type": "codai_sandbox", "egress": ..., "template": ...}` (`ChatSandboxTool` in
29
+ `codai._types`): the gateway runs bash / Python server-side in a sandbox per session and returns the artifacts.
30
+ - REST exec over Environments: `environments.exec(env_id, {"command": ..., "op": ..., "args": ..., "timeout_ms": ...})`
31
+ (blocking relay op, shell results flattened to `exit_code` / `stdout` / `stderr`), `environments.files.list` /
32
+ `get` / `put` (UTF-8 text, ≤ 2 MB) and `environments.jobs.start` / `get` (status + output). No WebSocket client
33
+ needed; 409 `environment_offline`, 504 `host_timeout`.
34
+
35
+ ## [0.50.0] - 2026-10-16
36
+
37
+ - `abuse_reports.create(share_token, surface, category, explanation, reporter_name, reporter_email, good_faith=True)` —
38
+ `POST /v1/studio/abuse-reports` (public, no key needed): DSA art. 16 notice about illegal or abusive content on a
39
+ codai Studio share (`surface="share"`) or review (`"review"`) link — categories `voice_without_consent`,
40
+ `impersonation`, `fraud`, `election`, `illegal_other`, `other`. Answers 202
41
+ `{id, object: "studio.abuse_report", status: "open"}`, also for an unknown link (no enumeration). Never retried.
42
+ `CreateStudioAbuseReportBody` / `CreateStudioAbuseReportResponse` types.
43
+
44
+ ## [0.49.0] - 2026-10-16
45
+
46
+ - `resolve.list(status=None, limit=None, cursor=None)` — `GET /v1/resolve/jobs`, your Resolve jobs newest first
47
+ (`{"jobs", "next_cursor"}`; `status` = a string or a list sent as a comma list), and `resolve.iterate(status, limit)`,
48
+ a generator that follows `next_cursor`. `ResolveJob` gains `issue_url` and `issue_title`; `ResolveJobList` type.
49
+ - `billing` (READ-ONLY, never starts a payment): `billing.get()` — `GET /v1/billing`; `billing.invoices(limit=None)` —
50
+ `GET /v1/billing/invoices`; `billing.watch_balance(threshold_eur, on_low, interval_s=60, max_checks=None)` polls
51
+ `get()` and calls `on_low(balance_eur, billing)` once per crossing below the threshold.
52
+ - `referrals`: `referrals.get()` — `GET /v1/referrals`; `referrals.redeem(code)` and `referrals.set_consent(bool)` call
53
+ `PATCH /v1/account` with `referral_code` / `referral_consent`.
54
+
55
+ ## [0.48.0] - 2026-10-16
56
+
57
+ - `voice_revocations.create(email, voice_owner_name, details, evidence_url=None)` — `POST /v1/studio/voice-revocations`
58
+ (public, no key needed): asks for a voice cloned in codai Studio to be taken down; answers 202
59
+ `{id, object: "studio.voice_revocation", status: "open"}` and never echoes the input (only the sha256 of the e-mail
60
+ is stored). Never retried. `CreateVoiceRevocationBody` / `CreateVoiceRevocationResponse` types. Custom (cloned) voices
61
+ themselves are not in the SDK while the feature flag is off.
62
+
63
+ ## [0.47.0] - 2026-10-16
64
+
65
+ - Lottie export: `creatives.export_lottie(id, format=None, formats=None, seq=None, container=None)` —
66
+ `GET /v1/studio/creatives/{id}/export.lottie` exports a video creative as a Lottie animation (Bodymovin JSON with
67
+ embedded images, or a dotLottie `.lottie` archive with `container="dotlottie"`; `formats="all"` for every format),
68
+ free and synchronous: the export comes back already `succeeded` with a signed `url` and the fidelity report in
69
+ `warnings`. `creatives.download_lottie(...)` returns the file itself (`{bytes, filename, content_type}`).
70
+ `StudioExport` gains `kind` (`"fcpxml"` | `"lottie"`) and `container` (Lottie only); `ExportCreativeLottieResponse`.
71
+
72
+ ## [0.46.0] - 2026-10-16
73
+
74
+ - `ai_label` (`"none"` | `"corner"` | `"end_card"`, EU AI Act art. 50(4)): in the `renders.create` / `renders.render`
75
+ body, as `creatives.render(id, ai_label=...)` and `batches.create(..., ai_label=...)` / `dry_run`. Burns a visible
76
+ "AI-generated" label (corner for the whole video, or a ~2 s end card) into the MP4. Omitted = `corner` when the render
77
+ uses TTS narration or AI-generated images, else `none`; `none` with TTS narration is a 400 `ai_label_required`.
78
+ Render replies report the resolved `ai_label`, and so do `renders.list` items (`"none"` for renders from before the
79
+ label existed).
80
+
81
+ ## [0.45.2] - 2026-10-16
82
+
83
+ - `client.memory.list/search/create/delete` — your persistent memory over REST (`/v1/memory`), the same store
84
+ the MCP memory tools use. Search and create are metered as `memory:search` / `memory:create`.
85
+
86
+ ## [0.45.1] - 2026-10-15
87
+
88
+ - `allow_remix` on view share links now defaults to `false` server-side (E-13 legal review): pass
89
+ `allow_remix: true` to let link holders remix the creative into their own account.
90
+
91
+ ## [0.45.0] - 2026-10-15
92
+
93
+ - Share links: `creatives.create_share(id, kind, label=None, expires_in_days=None, seq=None, allow_remix=None,
94
+ can_approve=None)` — `POST /v1/studio/creatives/{id}/shares` returns the public `url` and its `token` once;
95
+ `creatives.list_shares(id)` and `creatives.revoke_share(id, share_id)`.
96
+ - Review: `creatives.review(id)`, `request_review(id)`, `reset_review(id)` (`draft` → `in_review` → `approved` |
97
+ `changes_requested`, approval pins the version); threads: `review_threads(id, status=None)`,
98
+ `create_review_thread(id, body, node_id=None, scene_id=None, t_ms=None)`, `reply_review_thread`,
99
+ `resolve_review_thread`, `reopen_review_thread`, `delete_review_comment`.
100
+ - New `client.shares` (token or whole link): `get(link, format=None, locale=None)` (no key needed),
101
+ `remix(link, title=None)`, and through review links `comment(link, name, body, …)`, `reply`, `resolve`, `reopen`,
102
+ `decide(link, name, decision, seq, note=None)`. `codai.share_token_from(value)` extracts the token. All free.
103
+ - Generated types: `StudioShare*`, `StudioReview*`, `StudioPublicShare`, `StudioPublicReview`, `StudioRemix*`.
104
+
105
+ ## [0.44.0] - 2026-10-15
106
+
107
+ - Localisation: `creatives.locales(id, locale=None, units=None, offset=None, limit=None)` —
108
+ `GET /v1/studio/creatives/{id}/locales`; `creatives.localize(id, locales, tone=None, glossary=None, only_missing=None,
109
+ voice=None, base_seq=None, force=None)` — `POST /v1/studio/creatives/{id}/localize` translates every text into up to
110
+ 10 BCP-47 locales as per-locale overrides in one new version (token-metered; `voice=True` narrates voice clips again,
111
+ billed); `creatives.fork_locale(id, locale, title=None, seq=None)` —
112
+ `POST /v1/studio/creatives/{id}/locales/{locale}/fork` saves a locale as a new creative.
113
+ - `locale=` keyword on `creatives.compiled`, `render`, `frame`, `captions_srt`, `captions_vtt` and `lint`. Lint
114
+ codes `missing_translation`, `stale_translation`, `text_overflow_locale` and issue `locale`; new ops
115
+ `setLocaleText`, `setLocale`, `setLocalization`.
116
+ - Batches: new `client.batches` — `create(source, csv=None, sheet=None, delimiter=None, mapping=None, title=None,
117
+ render=None, quality=None, format=None, localize=None)`, `dry_run(...)` (same arguments, nothing stored), `list`,
118
+ `get(id, rows=None, status=None, offset=None, limit=None)`, `cancel`, `retry`, `export_csv` (status CSV text),
119
+ `zip` (ZIP of finished renders) and `wait(id, interval=3.0, timeout=600.0)`.
120
+ - Generated types: `CreativeLocaleStatus`, `CreativeLocaleUnit`, `CreativeLocalizeRequest`, `CreativeLocalizeResult`,
121
+ `CreativeLocaleForkRequest`, `CreativeLocaleForkResult`, `StudioBatch*`.
122
+
123
+ ## [0.43.0] - 2026-10-15
124
+
125
+ - Auto-captions: `creatives.captions(id, clip_id=None, preset=None, node_id=None, script=None, language=None,
126
+ retranscribe=None, base_seq=None, force=None)` — `POST /v1/studio/creatives/{id}/captions` transcribes a voice clip
127
+ word by word (words cached on the asset, free next time), corrects the spelling with the script and adds or updates a
128
+ captions node as one new version (`node_id`, `words`, `cached`, `corrected`, `cost_micro_usd`; a fresh
129
+ transcription is billed at the speech-to-text rate). `creatives.captions_srt(id, node_id=None, seq=None)` /
130
+ `captions_vtt(...)` return the captions as SRT / WebVTT text.
131
+ - Platforms: `creatives.adapt(id, platforms, focal=None, base_seq=None, force=None)` —
132
+ `POST /v1/studio/creatives/{id}/adapt` adds one format per platform (`tiktok`, `reels`, `shorts`, `feed-square`,
133
+ `feed-portrait`, `youtube`) with text kept inside the platform safe zone; `focal="vision"` (token-metered) finds
134
+ image focal points. New ops `adaptFormat` and `setControl`.
135
+ - Lint: issues gain `format` and `fixable` and the codes `empty_list`, `off_brand_color`,
136
+ `outside_platform_safe_zone`, `text_too_small`, `missing_alt`, `missing_captions`, `reading_speed`, `flashing`;
137
+ the result adds `fixable`. `creatives.lint_fix(id, codes=None, agent=None, base_seq=None, force=None)` —
138
+ `POST /v1/studio/creatives/{id}/lint/fix`: deterministic fixes are free, `agent=True` adds one billed `codai` turn.
139
+ - Tweak panel: `creatives.controls(id)` — `GET /v1/studio/creatives/{id}/controls`.
140
+ - Render aspect ratio `4:5` (1080×1350).
141
+ - Generated types: `CreativeCaptionsRequest`, `CreativeCaptionsResult`, `CaptionPresetId`, `PlatformId`,
142
+ `CreativeAdaptRequest`, `CreativeAdaptResult`, `CreativeLintCode`, `CreativeLintFixRequest`,
143
+ `CreativeLintFixResult`, `Control`, `PanelControl`, `CreativeControls`.
144
+
145
+ ## [0.42.0] - 2026-10-14
146
+
147
+ - Brand kits as Design Tokens (DTCG 2025.10): kits now carry `dtcg` (the canonical document), `contrast` (WCAG ratio
148
+ - `aa` for text/background, primary/background, background/primary, accent/background, secondary/background) and
149
+ `updated_at`; `tokens` also carries the document aliases (`color.bg`, `color.fg`, `color.accent`,
150
+ `font.heading`, `font.body`), extra colours and `radius`.
151
+ - `brand_kits.create(name, tokens=… | dtcg=…, logo_asset_id=…, source_url=…)` — `POST /v1/studio/brand-kits`
152
+ (exactly one of `tokens` / `dtcg`, else `ValueError`; 400 `invalid_tokens`), `brand_kits.update(id, name=…,
153
+ tokens=…, dtcg=…, logo_asset_id=…)` — `PATCH /v1/studio/brand-kits/{id}` (`logo_asset_id=None` removes the logo),
154
+ `brand_kits.export_dtcg(id)` — `GET /v1/studio/brand-kits/{id}/dtcg`. Free.
155
+ - `brand_kits.extract(url=None, name=None, logo_asset_id=None)` — exactly one of `url` / `logo_asset_id` (else
156
+ `ValueError`); from a logo image one vision turn reads the palette (400 `unsupported_media`).
157
+ - `creatives.restyle(id, brand_kit_id=None, tokens=None, base_seq=None, snap=None, fonts=None, force=None)` —
158
+ `POST /v1/studio/creatives/{id}/restyle`: re-applies a kit as one new version (literal brand colours become token
159
+ refs, `snap` maps the rest to the nearest kit colour, fonts by role, locked nodes skipped unless `force`). Free.
160
+ - Generated types: `BrandKitDtcg`, `BrandKitContrastPair`, `BrandKitCreateRequest`, `BrandKitUpdateRequest`,
161
+ `CreativeRestyleRequest`.
162
+
163
+ ## [0.41.0] - 2026-10-14
164
+
165
+ - Documents as creatives: `creatives.from_document(document_id)` — `POST /v1/studio/documents/{id}/creative` turns a
166
+ Studio deck / doc / sheet into an editable creative (slides = scenes with `layout` + `notes`, doc pages = a `flow`
167
+ group, sheets = a `cell_grid` + chart nodes; nodes carry semantic `role`s). Idempotent: returns the creative plus
168
+ `document_id` and `existing` (HTTP 201 new / 200 already linked). Free.
169
+ - `creatives.build(creative_id, seq=None, preview=None, filename=None)` — `POST /v1/studio/creatives/{id}/build` builds
170
+ the creative back into PPTX / DOCX / XLSX as a new version of its document: `{object: "studio.creative_build",
171
+ creative_id, seq, warnings, document}`; 400 `not_supported_for_kind` / `invalid_spec`. Files are free;
172
+ `preview=True` queues the billed document preview.
173
+ - Regenerated `_types.py`: `StudioCreativeFromDocument`, `CreativeBuildRequest`, `StudioCreativeBuild`,
174
+ `Document.creative_id`, `CreativeCompiled.kind`, the `createCreativeFromDocument` / `buildCreative` operations.
175
+
176
+ ## [0.40.0] - 2026-10-14
177
+
178
+ - FCPXML export: `creatives.export_fcpxml(id, format=None, seq=None)` — `GET /v1/studio/creatives/{id}/export.fcpxml`
179
+ queues a zip with an FCPXML 1.10 project (one clip + marker per scene, audio on its own lanes) and its media
180
+ (per-scene MP4 segments, voice / music / sfx files) for Final Cut Pro, DaVinci Resolve or Premiere (FCPXML import).
181
+ Returns `{id, status: "queued", creative_id, seq, format, fps, filename, url, expires_at, bytes, rendered_seconds,
182
+ cost_micro_usd, warnings, error, created_at}`; 400 `unsupported_kind` / `unsupported_format` / `too_long`. Only newly
183
+ rendered seconds are billed, at the final render price (cached segments are free).
184
+ - `exports.retrieve(id)` — `GET /v1/studio/exports/{id}` (free); `exports.wait(id, interval=3.0, timeout=900.0)` polls
185
+ until `succeeded` (signed `url`, valid 1 h) or `failed` and returns the final export; `TimeoutError` after `timeout`.
186
+
187
+ ## [0.39.0] - 2026-10-14
188
+
189
+ - Templates from your own creatives: `creatives.save_template(id, vars=[…], title=…, seq=…)` —
190
+ `POST /v1/studio/creatives/{id}/template`: each `vars` entry (`{"node_id", "prop": "text" | "image", "key",
191
+ "label"?}`) turns a text node's text or an image node's asset into a template variable (current value = default).
192
+ Returns `{id, title, kind, source_creative_id, source_seq, vars_schema, thumbnail_url, created_at, doc}`; 400
193
+ `node_not_found` / `invalid_pick` / `duplicate_key`. Free; templates are private.
194
+ - `templates.list(kind=…, limit=…)`, `.retrieve(id)` (with `doc`), `.delete(id)` — free.
195
+ - `creatives.create({"kind": …, "template": …, "vars": {…}})` starts version 1 from a template with the var values
196
+ applied (400 `kind_mismatch` / `unknown_var` / `invalid_var_value`, 404 unknown template).
197
+
198
+ ## [0.38.0] - 2026-10-14
199
+
200
+ - Cards: `cards.product(url, lang=None)` — `POST /v1/cards/product`: a `products` card block with one item read from
201
+ a public https product page's schema.org `Product` JSON-LD (or OpenGraph product tags): name, image, price +
202
+ currency (lowest offer), old price, rating (0-5), reviews, seller, availability badge (en/ro). Only what the page
203
+ states, never sponsored; SSRF-guarded like `unfurl`, cached 6 h, shares its 500/day limit; 422 `product_not_found`.
204
+
205
+ ## [0.37.0] - 2026-10-14
206
+
207
+ - Brand kits: `brand_kits.extract(url, name=…)` — `POST /v1/studio/brand-kits/extract`: fetches a public https page
208
+ (SSRF-guarded), one bounded `codai` model turn picks the palette (primary, secondary, accent, background, text) and
209
+ the heading / body fonts, the logo is stored as an `image` asset. Returns `{id, name, source_url, tokens,
210
+ logo_asset_id, logo_url, logo_mime_type, created_at}`; 400 `url_blocked`, 422 `fetch_failed` / `extract_failed`,
211
+ 504 `timeout`. Token-metered (`codai::brand-kit-extract`).
212
+ - `brand_kits.list(limit=…)`, `.retrieve(id)`, `.delete(id)` — free.
213
+ - `creatives.create({"kind": …, "brand_kit_id": …})` applies a kit to version 1 (`brand = {kitId, tokens}` + logo asset).
214
+
215
+ ## [0.36.0] - 2026-10-14
216
+
217
+ - A/B variants of a creative: `creatives.variants(id, base_seq=…, count=…, focus=[…], brief=…, render=…, format=…)`
218
+ — `POST /v1/studio/creatives/{id}/variants`: one bounded `codai` model turn proposes up to 5 variants (focus hook /
219
+ title / cta / visual / copy), each saved as a fork with the ops applied plus a queued draft render. Returns
220
+ `{group_id, base_seq, variants: [{creative_id, label, rationale, applied_ops, head_seq, render_id}]}`; 422
221
+ `no_edit`, 504 `timeout` after 90 s. Token-metered (`codai::creative-variants`), draft renders billed on their poll.
222
+ - `creatives.list_variants(id)` — `GET …/variants`: the latest group with `status` and the stored render state.
223
+ - `creatives.pick_variant(id, variant_id, base_seq=…)` — `POST …/variants/{variantId}/pick`: the winner's ops onto the
224
+ original (rebased like `edit`, 409 `stale` on overlap), statuses updated.
225
+
226
+ ## [0.35.0] - 2026-10-14
227
+
228
+ - `SessionControlKind` gains `pause` and `resume`: `POST /v1/sessions/{id}/control` with `kind="pause"`
229
+ parks the running turn at its next step boundary (after the current tool calls), `kind="resume"` continues the
230
+ same turn. Editors may send both (same trust as `cancel`). Executors report the state with a `run_state` event
231
+ `{"state": "paused" | "running"}`.
232
+
233
+ ## [0.34.0] - 2026-10-12
234
+
235
+ - `creatives.comment(id, t_ms=…, comment=…, base_seq=…, node_id=…, scene_id=…, apply=…)` —
236
+ `POST /v1/studio/creatives/{id}/comments`: a comment pinned on a frame becomes an edit (one bounded `codai` model
237
+ turn proposes ops, applied like `edit`). Returns `{head_seq, applied_ops, summary, version}`; 409 `stale` on an
238
+ overlapping change, 422 `no_edit` when the model made no edit, 504 `timeout` after 60 s. Token-metered
239
+ (`codai::creative-comment`).
240
+
241
+ ## [0.33.0] - 2026-10-12
242
+
243
+ - `creatives.frame(id, t_ms=[…], format=…, exact=…, version=…)` — `POST /v1/studio/creatives/{id}/frame` now returns
244
+ PNG still frames (`{object: "studio.creative.frames", status: "succeeded", frames, billed_micro_usd}`) or a running
245
+ job (`{object: "studio.creative.frame_job", status: "running", frame_job_id}`) when they are not ready within ~90 s.
246
+ `exact=True` cuts frames from a draft render of the covering scenes. Cached frames are free; 60 frame jobs per UTC
247
+ day are free, then billed. The old `frame(id, body)` dict form is replaced by keyword arguments.
248
+ - `creatives.frame_job(id, job_id)` — `GET /v1/studio/creatives/{id}/frames/{jobId}`, polls a frame job.
249
+ - `creatives.render` may now answer 200 with the identical existing render (`cached: True`, no new charge); new
250
+ renders carry `cached: False`.
251
+
252
+ ## [0.32.0] - 2026-10-12
253
+
254
+ - `cards.route(from_=…, to=…, origin=…, destination=…, mode=…, depart_at=…)` — `GET /v1/cards/route`, travel time
255
+ from Google Routes (`route` summary + `map` block); billed $0.010 traffic-aware driving, $0.005 walk / bike / transit.
256
+ - `cards.track(awb, carrier=…)` — `GET /v1/cards/track`, parcel history (Sameday live; FAN Courier, Cargus, DPD with a
257
+ gateway carrier contract only).
258
+ - `cards.efactura(limit=…)` — `GET /v1/cards/efactura`, your fiscal documents issued through Brivio with their ANAF
259
+ e-Factura status.
260
+
261
+ ## [0.31.0] - 2026-10-12
262
+
263
+ - `client.creatives` — codai Studio editable creative documents (ECD) with an append-only version history:
264
+ `list(kind, before, limit)`, `create(body)`, `retrieve(id, seq=None)`, `outline`, `find(id, query)`,
265
+ `subtree(id, ids=None, scene_id=None)`, `edit(id, base_seq, ops, intent=None, author=None, force=None)`
266
+ (`POST /v1/studio/creatives/{id}/ops`; a stale base is rebased, overlapping changes raise `CodaiError` 409
267
+ `stale`), `versions`, `revert(id, seq)`, `fork`, `delete`, `lint`, `compiled` (preview HTML with signed asset URLs)
268
+ and `render` (queues a `codai-render`, HTTP 202; poll `renders.retrieve`). `frame` is reserved (501
269
+ `not_implemented`). Everything except `render` is free.
270
+ - `create_timeline` replies carry `creative_id`; `renders.retrieve` reports `creative_id` for renders linked to a
271
+ creative.
272
+ - Generated types: `Creative*` TypedDicts and the 15 creatives operation aliases.
273
+
274
+ ## [0.30.0] - 2026-10-12
275
+
276
+ - `lists` resource — codai Lists + Share to codai: `list`, `create`, `reorder`, `update`, `delete`, `items`, `add`,
277
+ `update_item`, `delete_item`, `image` (`ListItemImage(data, content_type)`), `export_markdown` (str), `share` (`POST /v1/share`,
278
+ 201 created / 200 dedupe) and `stream()` (generator) (`event: list` frames).
279
+ - `schedules.audit({ schedule_id?, limit? })` — `GET /v1/schedules/audit`, the append-only scheduler audit log.
280
+ - Generated types: 14 new operations (lists, share, schedule audit).
281
+
282
+ ## [0.29.0] - 2026-10-12
283
+
284
+ - `cards.render(block, format=None, width=None, height=None, theme=None, scale=None)` — `POST /v1/cards/render`:
285
+ renders a `chart` or `qr` card block to PNG (default) or SVG on the gateway and returns
286
+ `CardsRendered(data, content_type, width, height)`. Free, 2000 renders per user per day. Mermaid `diagram` / `math`
287
+ blocks are not supported yet (422 `render_unsupported`).
288
+ - Generated types: operation `cardsRender`.
289
+
290
+ ## [0.28.0] - 2026-10-11
291
+
292
+ - Studio scene timelines on `client.renders`: `create_timeline(body)` for `POST /v1/studio/timelines` (image/video
293
+ scenes, optional narration billed now and a music bed; HTTP 202, poll with `retrieve`), `regenerate_scene(render_id,
294
+ scene_id, body)` for `POST /v1/studio/renders/{id}/scenes/{scene_id}/regenerate` (a new render with one scene
295
+ replaced) and `list(before=None, limit=None, root_render_id=None)` for `GET /v1/studio/renders` (no polling, no URLs).
296
+ - `retrieve(render_id, expand_assets=True)` sends `?expand=assets`; timeline renders carry `timeline`,
297
+ `parent_render_id`, `root_render_id` and `stage`.
298
+ - Generated types for `StudioRenderList`, `StudioTimelineCreate`, `StudioTimelineAccepted`, `StudioSceneRegenerate`,
299
+ `StudioSceneRegenerateAccepted`, `StudioTimeline`; operations `listRenders`, `createTimeline`, `regenerateRenderScene`.
300
+
301
+ ## [0.27.0] - 2026-10-11
302
+
303
+ - `client.cards` — typed card data from `/v1/cards/*`. Every JSON method returns `{"block": ..., "provenance": ...}`
304
+ where `block` is already a valid `@codai/cards` block: `cards.weather(lat, lng, name=None)` (MET Norway, free),
305
+ `cards.places(q=None, lat=None, lng=None, radius=None, type=None, limit=None, lang=None, region=None,
306
+ open_now=None)` (Google Places, billed $0.032 per call, 200/day), `cards.geocode(address)` (map block + raw
307
+ `results`, billed $0.005), `cards.fx(source=None, from_=None, to=None, amount=None, quote=None, symbols=None)`
308
+ (BNR / ECB, free), `cards.unfurl(url)` (links preview block, 500/day) and `cards.static_map(center=None,
309
+ markers=None, ...)` returning `CardsStaticMap(data, content_type, attribution)` for the PNG (billed $0.002, counted
310
+ in the 500/day maps limit).
311
+ - Generated types for `CardDataResponse`; operations `cardsWeather`, `cardsPlaces`, `cardsGeocode`,
312
+ `cardsStaticMap`, `cardsFx`, `cardsUnfurl`.
313
+
314
+ ## [0.26.0] - 2026-10-11
315
+
316
+ - `schedules.pause/resume` (vacation mode with optional backfill), `schedules.templates/render_template`, and
317
+ `client.heartbeats` (`list/create/update/delete`) for dead-man-switch monitors of crons codai does not run.
318
+
319
+ ## [0.25.0] - 2026-10-11
320
+
321
+ - `client.schedules` — the codai Scheduler: `list`, `create`, `preview` (cron or plain words in English or Romanian →
322
+ next runs + DST warning), `get`, `update`, `delete`, `run_now`, `runs`, `recent_runs`, `decide` (approve / skip a run
323
+ waiting for you) and `stream`; `schedules.hosts.list/update` (devices as hosts: os, kind, tags); and the host executor
324
+ protocol `schedules.executor.claim/heartbeat/result/approve_command`.
325
+ - `transcripts.publish_live` mapped to `publishTranscriptLive` in the operation table.
326
+
327
+ ## [0.24.0] - 2026-10-10
328
+
329
+ - `client.settings` — account-level settings shared by every device of the account (per user, not per key):
330
+ `client.settings.get()` for `GET /v1/settings` (full object with defaults, `version` 0 / `updatedAt` None until
331
+ the first write) and `client.settings.update(partial, version=None)` for `PATCH /v1/settings` (deep merge, strict
332
+ validation; a stale `version` raises a 409 `CodaiError` with code `version_conflict`).
333
+ `settings_conflict_current(err)` returns the current state carried by that 409.
334
+ - Generated types for `SettingsResponse`, `UserSettings`, `TranscriptsSettings`, `SettingsPatchRequest`,
335
+ `SettingsConflict`; operations `getSettings`, `patchSettings`.
336
+
337
+ ## [0.23.0] - 2026-10-10
338
+
339
+ ### Added
340
+
341
+ - `client.documents` — codai Studio documents (`codai-docs`): `create(body)` builds a deck (PPTX), doc (DOCX) or sheet
342
+ (XLSX) from a JSON `spec` (free; `file` is an asset dict with `kind: "document"`), `retrieve(id)` returns it with the
343
+ stored `spec`, `preview(id)` queues a `codai-docs-preview` preview (tagged PDF + page PNGs + contact sheet, HTTP 202),
344
+ `retrieve_preview(preview_id)` polls it and `video(id, body)` turns a deck into an MP4 queued as a `codai-render`
345
+ render (optional narration). Pollers `create_preview_and_wait(id, poll_interval=, timeout=)` returns the succeeded
346
+ preview and `video_and_wait(id, body, poll_interval=, timeout=)` returns a `RenderResult`; both raise
347
+ `RuntimeError` on `failed` and `TimeoutError` on timeout.
348
+ - Generated types: `DocumentCreateRequest`, `Document`, `DocumentPreview`, `DocumentPreviewAccepted`,
349
+ `DocumentVideoRequest`, `DocumentVideoAccepted`, `DeckSpec`, `DocSpec`, `SheetSpec` and their parts; `AssetKind`
350
+ gains `document` and `preview`.
351
+
352
+ ## [0.22.0] - 2026-10-10
353
+
354
+ ### Added
355
+
356
+ - `client.sfx` — the codai Studio sound-effects library (`codai-sfx`, curated CC0 sounds + a Freesound CC0-only
357
+ fallback): `client.sfx.search(q=, category=, tags=, max_duration=, limit=)` for `GET /v1/studio/sfx` returns
358
+ `SfxSearchResult` (`data`, `source`, `fallback_unavailable` from `x-codai-sfx-fallback: unavailable`; `tags` accepts
359
+ a list) and `client.sfx.import_(sfx_id)` for `POST /v1/studio/sfx/{sfx_id}/import` returns the asset dict
360
+ (`kind: "sfx"`, usable as `asset:<asset_id>` in renders). The method is `import_` because `import` is a Python
361
+ keyword. Both are free.
362
+ - Generated types: `SfxDto`, `SfxCategory`, `SfxList`, `searchSfxResponse`, `importSfxResponse`; `AssetKind` gains
363
+ `sfx`.
364
+
365
+ ## [0.21.0] - 2026-10-10
366
+
367
+ ### Added
368
+
369
+ - `client.audio.speech_json(body)` for `POST /v1/audio/speech` with `timestamps` and/or `store`: returns
370
+ `SpeechJsonResult` with per-word timings (`words`), decoded `audio` or the stored studio `asset` (kind `speech`,
371
+ usable as `asset:<id>` in renders), `duration_seconds`, `cost_micro_usd` and `timestamps_unavailable`
372
+ (`x-codai-timestamps: unavailable`). Defaults to `codai-speech` and `timestamps: True`.
373
+ - Generated types: speech models `codai-speech` (Chirp 3 HD, 30 native Romanian voices) and
374
+ `codai-speech-expressive` (Gemini 2.5 Flash TTS, honours `instructions`); request fields `language`, `timestamps`,
375
+ `store`; new TypedDicts `SpeechJson`, `SpeechWord`, `MusicTrackMeta`; music request fields `lyrics`,
376
+ `instrumental`, `bpm`, `duration_seconds`, `language` and the `codai-music` / `codai-music-pro` /
377
+ `codai-music-legacy` model enum.
378
+ - `MusicResult.tracks` (per-item `lyrics`, `caption`, `lyrics_match`, `warnings`) and `MusicResult.warnings`
379
+ (e.g. `lyrics_mismatch` for best-effort non-English lyrics).
380
+
381
+ ### Changed
382
+
383
+ - `codai-music` is now Lyria 3 Clip (30 s MP3 with vocals); Lyria 2 stays available as `codai-music-legacy`.
384
+ - `audio.speech()` / `speech_detailed()` no longer send the Azure default `voice: "alloy"` for `codai-speech*` models.
385
+ - `music.generate()` client timeout floor raised from 180 s to 300 s (Lyria 3 Pro songs).
386
+
387
+ ## [0.20.0] - 2026-10-10
388
+
389
+ ### Added
390
+
391
+ - `client.renders` for codai Studio renders (`codai-render`, HyperFrames HTML composition → MP4, up to 180 s at
392
+ 1080p): `create(body)` (`POST /v1/studio/renders`, HTTP 202), `retrieve(id)` (`GET /v1/studio/renders/{id}`, status
393
+ - `progress` + `qa`) and `render(body, poll_interval=, timeout=)`, which polls until `succeeded` and returns a
394
+ `RenderResult` (`asset`, `poster_url`, `qa`, `cost_micro_usd`); raises `RuntimeError` on `failed` and
395
+ `TimeoutError` on timeout.
396
+ - Regenerated `_types.py` (`StudioRender*` schemas, the `createRender` / `getRender` operations, the
397
+ `asset_not_found`, `too_many_renders` and `renders_unavailable` error codes).
398
+
399
+ ## [0.19.0] - 2026-10-10
400
+
401
+ ### Added
402
+
403
+ - `client.assets` for codai Studio assets (`/v1/assets`, owner-scoped, kept 90 days): `list(kind=, before=, limit=)`,
404
+ `iterate()` (follows `next_before`), `retrieve(id)`, `content_url(id)` (fresh 1 h signed URL), `content_path(id)`
405
+ (authenticated `GET /v1/assets/{id}/content` redirect), `content(id)` (bytes) and `delete(id)`.
406
+ - `response_format: "url"` on `images.generate` / `images.edit` and `music.generate` bodies, and
407
+ `response_format="url"` on `videos.retrieve` / `videos.generate`: results are stored as assets and returned in
408
+ `ImagesResult.assets` / `MusicResult.assets` / `VideoResult.asset` (signed URLs) instead of bytes. The default
409
+ `b64_json` behaviour is unchanged.
410
+ - Regenerated `_types.py` (`AssetDto`, `AssetKind`, `AssetList`, `AssetDeleted`, `MediaResponseFormat`, the four asset
411
+ operations, `assets_unavailable` error code).
412
+
413
+ ## [0.18.0] - 2026-10-09
414
+
415
+ ### Added
416
+
417
+ - codai Studio media (US-only, English prompts): `music.generate(body)` -> `MusicResult` (`POST /v1/audio/music`,
418
+ Lyria 2, 30 s WAV clips, timeout floor 180 s) and `videos.create` / `.retrieve(id)` /
419
+ `.generate(body, poll_interval=5.0, timeout=300.0)` -> `VideoResult` (`POST /v1/videos` + `GET /v1/videos/{id}`,
420
+ Veo 3.1 Fast; raises `RuntimeError` on `failed`/`filtered`, `TimeoutError` on timeout).
421
+
422
+ ## [0.17.0] - 2026-10-08
423
+
424
+ ### Added
425
+
426
+ - `client.tools.ground(body)` — `POST /v1/tools/ground`: locate a UI element described in words on an inline
427
+ screenshot (codai's self-hosted GUI grounding model).
428
+
429
+ ## [0.16.1] - 2026-10-07
430
+
431
+ ### Added
432
+
433
+ - Web search result `source` gains `ocw` (MIT OpenCourseWare course records for education queries that name MIT / OCW).
434
+
435
+ ## [0.16.0] - 2026-10-06
436
+
437
+ ### Added
438
+
439
+ - `transcripts.export(id, "pdf" | "docx")` returns `{bytes, filename, content_type}`; text formats keep returning
440
+ `{text, ...}`.
441
+ - `transcripts.shares.create(id, expires_in_days=None)` / `.list(id)` / `.revoke(id, share_id)` - read-only share
442
+ links (`https://hub.codai.ro/t/<token>`; the token is returned once).
443
+ - `transcripts.public_view(token)` - the read-only view behind a share link.
444
+ - Types regenerated from the gateway spec (`TranscriptShare*`, `PublicTranscript`).
445
+
446
+ ## [0.15.3] - 2026-10-06
447
+
448
+ ### Added
449
+
450
+ - `TranscriptDetail` types gain `processing` (pending job: `kind`, `attempts`, `max_attempts`, `last_error`,
451
+ `next_attempt_at`) - regenerated from the gateway spec.
452
+
453
+ ## [0.15.2] - 2026-10-05
454
+
455
+ ### Added
456
+
457
+ - `POST /v1/tools/search` types: request `depth: "fast" | "deep"` (deep = more sources, ~12 s budget); `mode`
458
+ also accepts `fast` / `deep` as an alias (means `codai` with that depth).
459
+
460
+ ## [0.15.1] - 2026-10-05
461
+
462
+ ### Added
463
+
464
+ - `POST /v1/tools/search` types: request `fallback: "auto" | "off"` (automatic Grounding with Google Search when
465
+ codai results are weak, returned as a separate `google` block + `fallback: { used, reason }`), new intents
466
+ `academic` / `definition` / `education`, and new result sources (`mdn`, `mslearn`, `hn`, `osv`, `endoflife`,
467
+ `openalex`, `crossref`, `arxiv`, `europepmc`, `doaj`, `core`, `wiktionary`, `openstax`, `dexonline`).
468
+
469
+ ## [0.15.0] - 2026-10-05
470
+
471
+ ### Added
472
+
473
+ - `client.usage.get(query)` → `GET /v1/usage`: your own requests, errors, tasks, tokens and cost grouped by
474
+ `day` | `key` | `device` | `app` | `platform` | `model` | `client` | `tag`, filtered by `key`, `device`, `app`,
475
+ `platform`, `model`, `tag`; `from` / `to` are inclusive `YYYY-MM-DD` (default last 30 days, max 400).
476
+ `cost_micro_usd` is USD micros as a string. Unknown query keys raise `ValueError`.
477
+ - `client.keys.list(include_revoked=False)` → `GET /v1/keys`: your API keys with `kind`, `device_id`, `platform`,
478
+ `client_version`, `tags` and `current`.
479
+ - `client.keys.set_tags(id, tags)` → `PATCH /v1/keys/{id}`: replace a key's tags (≤ 16, each ≤ 32 chars).
480
+ - Generated TypedDicts `UsageReport`, `UsageRow`, `ApiKeyInfo` (+ `getUsage` / `listKeys` / `setKeyTags` aliases).
481
+
482
+ ## [0.14.0] - 2026-10-05
483
+
484
+ ### Added
485
+
486
+ - `client.transcripts` — Transcripts API: `list` (folder / tag / pinned / status, cursor), `create`, `get`,
487
+ `update`, `delete`, `finalize`, `retranscribe`, `reanalyze`, `add_bookmark`, `rename_speaker`,
488
+ `edit_utterance`, `audio`, `export` (md / txt / srt / vtt / json → `{text, filename, content_type}`),
489
+ `accept_prompt`, `usage`, `search` (lexical / hybrid), `ask`, `stream` (SSE generator), plus
490
+ `transcripts.folders.*` and `transcripts.vocabulary.get|put`.
491
+ - `client.transcripts.upload_audio(data, track=, transcript_id=, chunk_size=, upload_id=, on_progress=)` —
492
+ resumable tus 1.0 upload (`transcripts.uploads.capabilities|create|offset|append`).
493
+ - `PromptSource` gains `transcript`.
494
+
495
+ ## [0.13.1] - 2026-10-05
496
+
497
+ ### Added
498
+
499
+ - `POST /v1/tools/search` types: request `mode: "codai" | "google"` (default `codai`) and the
500
+ `ToolSearchGoogleResponse` shape (Grounding with Google Search: `answer`, `sources`, `queries`,
501
+ `search_suggestions_html`, `attribution`), plus the `releases` / `registry` / `engine` result sources.
502
+
503
+ ## [0.13.0] - 2026-10-05
504
+
505
+ ### Added
506
+
507
+ - Pending prompts track delivery: statuses `sent`, `answered`, `verified` and fields `sent_at`, `answered_at`,
508
+ `verified_at`, `match_ref`, `match_turn_id`, `match_score`, `base_sha`, `head_sha`, `report`, `report_at`.
509
+ - `client.prompts.match(id, body)` → `POST /v1/prompts/:id/match`; `client.prompts.verify(id, body)` →
510
+ `POST /v1/prompts/:id/verify` (report: done / partial / not started, what changed, what remains);
511
+ `client.prompts.verify_many(body)` → `POST /v1/prompts/verify`; `client.prompts.stream()` → `GET /v1/prompts/stream`
512
+ (SSE generator of `event: prompt` upserts / deletes).
513
+
514
+ ## [0.12.0] - 2026-10-05
515
+
516
+ ### Added
517
+
518
+ - `client.audio.speech_stream(body)` → `POST /v1/audio/speech/stream` (`codai-tts-ro`, Azure neural TTS with
519
+ visemes): generator of `{"type": "start"|"audio"|"viseme"|"word"|"done"|"error", ...}`; `audio` events carry the
520
+ decoded `audio_bytes`. `createSpeechStream` in the parity table.
521
+ - `client.system_one.batch(items, questions, timeout_ms=None, fallback=None)` — the same questions about up to 16
522
+ states in one call; `system_one.create` documents the new `items`, `timeout_ms` and `fallback` fields and the
523
+ `fallback_used` response flag.
524
+ - Ephemeral token scope `live` (realtime + audio) accepted by `tokens.create`.
525
+ - `_types.py` regenerated (`SpeechStreamRequest`, `SpeechStreamEvent`, batch fields on `SystemOneRequest` /
526
+ `SystemOneResponse`).
527
+
528
+ ### Not included
529
+
530
+ - No realtime (WebSocket) helper: the SDK is zero-dependency and the standard library has no WebSocket client, and
531
+ `/v1/realtime` is outside the OpenAPI parity contract. Use `tokens.create("live")` and any WebSocket client against
532
+ `wss://ai.codai.ro/v1/realtime?model=codai-transcribe-live&language=ro&key=<token>` (protocol in the docs,
533
+ gateway → Embeddings, audio & realtime).
534
+
535
+ ## [0.11.0] - 2026-10-02
536
+
537
+ ### Added
538
+
539
+ - `client.devices.revoke_self_key()` — `POST /v1/keys/self/revoke`: the calling API key revokes itself (sign out).
540
+
541
+ ## [0.10.0] - 2026-09-29
542
+
543
+ ### Added
544
+
545
+ - `client.prompts.{list, create, get, update, delete, launch}` — pending prompts (`/v1/prompts/*`):
546
+ ready-to-paste prompts left for a future chat, with `goal_key` supersede, `not_before` reminders
547
+ (`prompt.due` notifications) and `launch` into a new session on a device. `launch` is never retried.
548
+ `_types.py` regenerated (`Prompt`, `PromptCreate`, `PromptPatch`, `PromptCreated`, …).
549
+
550
+ ## [0.9.0] - 2026-09-29
551
+
552
+ ### Added
553
+
554
+ - `client.sessions.dispatch(session_id, *, device_id=..., text=..., mode="send"|"open", turn_id=..., control_id=...)`
555
+ keyword form. `mode="open"` hands the session off — the target only opens it (no user turn), and
556
+ `text`/`turn_id` are dropped. The positional `body` dict still works (keywords override its keys); a
557
+ `send` without `text` now raises `ValueError` client-side instead of a server `400`.
558
+ - Regenerated `src/codai/_types.py`: `SessionDispatchRequest.mode` (`text` now optional),
559
+ `SessionDispatchResponse.mode`, `SessionControl.mode` (`'open'` on handoff controls) and
560
+ `DeviceListItem.online` (seen in the last 2 minutes).
561
+ - `client.images.edit({"prompt": ..., "image": {"b64_json": ..., "mime_type": ...} | [...]})` →
562
+ `POST /v1/images/edits` (JSON body, 1–14 input images; `mask` unsupported). `editImage` in the
563
+ parity table; `EditImageBody` / `CoreImageEditRequest` types.
564
+ - `ImagesResult.mime_types` and `ImagesResult.data_region` (`x-codai-data-region`, `eu` | `global`).
565
+ - `generate()` types gain `reference_images`, `aspect_ratio`, `image_size` and the
566
+ `codai-image-fast` / `codai-image-pro` model ids.
567
+
568
+ ### Changed
569
+
570
+ - `codai-image` is now `gemini-3.1-flash-image` on the Vertex EU endpoint (2.5 is shut down on
571
+ 2026-10-02 and served as `codai-image`). `cost_micro_usd` is usage-based (≈ $0.074 per default
572
+ 1K image) instead of a flat $0.04. The image request floor timeout is 150 s.
573
+
574
+ ## [0.8.0] - 2026-09-29
575
+
576
+ ### Added
577
+
578
+ - `client.images.generate({"prompt": ..., "n"?, "size"?, "negative_prompt"?, "model"?})` →
579
+ `POST /v1/images/generations` (base64 only). Backed by `gemini-2.5-flash-image`; `model`
580
+ defaults to `codai-image` (`imagen-4` / `imagen-4-fast` are legacy aliases). Flat $0.04 per
581
+ returned image, reported as `cost_micro_usd`.
582
+ - EXPERIMENTAL, unaudited (HIDE protocol) agent-output signatures — provenance only, never
583
+ authentication:
584
+ - `EnvironmentTask.output_signature: Optional[EnvironmentTaskOutputSignature]` plus the
585
+ `AgentOutputKey` / `AgentOutputVerifyRequest` / `AgentOutputVerifyResult` types
586
+ (regenerated `src/codai/_types.py`).
587
+ - `client.verify.agent_output_key()` → `GET /v1/verify/agent-output/key` and
588
+ `client.verify.agent_output(payload, signature)` → `POST /v1/verify/agent-output`
589
+ (public; never retried, 503 `signing_disabled` when signing is off).
590
+
591
+ ## [0.7.0] - 2026-09-27
592
+
593
+ ### Added
594
+
595
+ - Resolve pricing experiment (regenerated `src/codai/_resolve_types.py`):
596
+ - `ResolveQuote` `intro_price_micro_eur` / `list_price_micro_eur` — present while the €1
597
+ intro price applies to your first paid accept.
598
+ - `ResolveAccepted` `paid_with` (`ResolvePaidWith`: `intro` | `credit` | `wallet` | `free`)
599
+ and `held_micro_eur`; `ResolveJob.paid_with` once accepted.
600
+ - `ResolveInsufficientFundsError.bundle_url` (`Optional[str]`) — fix-bundle checkout
601
+ (10 t7 fixes for €49), set only for `t7` jobs; also appended to the error message.
602
+ - `client.resolve.balance()` — `GET /v1/resolve/balance` (operation `getResolveBalance`) →
603
+ `ResolveBalance {fix_credits, intro_available, wallet_micro_eur}`.
604
+
605
+ ## [0.6.0] - 2026-09-27
606
+
607
+ ### Added
608
+
609
+ - Signed Resolve attestations: `ResolveAttestation.signature` (DSSE envelope, `None` for
610
+ pre-signing attestations) and `ResolveAttestationEnvelope` / `ResolveKeys` / `ResolveKey`
611
+ types (regenerated `codai._resolve_types`).
612
+ - `client.resolve.keys()` — `GET /v1/resolve/keys` (operation `getResolveKeys`).
613
+ - `client.resolve.verify_attestation(att, keys=None)` and offline
614
+ `verify_attestation_with_keys(att, keys)` → `{"ok": True, "keyid"}` or
615
+ `{"ok": False, "reason": "UNSIGNED" | "BAD_SIGNATURE" | "STATEMENT_MISMATCH" | "BAD_PAYLOAD_TYPE"}`.
616
+ The statement check is stdlib; the ECDSA signature check needs the optional
617
+ `cryptography` package (lazy import, clear `ImportError` otherwise).
618
+ `attestation_statement(att)` exposes the recomputed statement.
619
+
620
+ ## [0.5.0] - 2026-09-27
621
+
622
+ ### Added
623
+
624
+ - Resolve job webhooks: `callback_url` on `ResolveIntake`, one-time `callback_secret` on
625
+ `ResolveQuote` / `ResolveDeclined`, and `ResolveWebhookEvent` / `ResolveWebhookEventName` /
626
+ `ResolveWebhookJob` TypedDicts (regenerated `_resolve_types.py`).
627
+ - `verify_resolve_webhook(secret, raw_body, signature_header, tolerance_seconds=300, now=None)`
628
+ — constant-time (`hmac.compare_digest`) check of `x-codai-signature: t=<unix>,v1=<hex>`
629
+ with a timestamp window. Exported from `codai` and `codai.resources`.
630
+
631
+ ## [0.4.0] - 2026-09-27
632
+
633
+ ### Added
634
+
635
+ - `client.resolve` — codai Resolve (pay per verified fix, `https://resolve.codai.ro`,
636
+ override with `resolve_base_url=`): `submit`, `get`, `accept`, `attestation`,
637
+ `health`, `wait_for`. `ResolveError` / `ResolveInsufficientFundsError` (402 carries
638
+ `topup_url`, `balance`, `required`). `RESOLVE_OPERATION_METHODS` maps every
639
+ `resolve.yaml` operationId; `_resolve_types.py` is generated from it.
640
+
641
+ ## [0.3.1] - 2026-09-26
642
+
643
+ ### Changed
644
+
645
+ - Regenerated `_types.py`: project prebuild pool fields, environment `prebuild`
646
+ and `prebuild_claimed` on create (CE-0072).
647
+
648
+ ## [0.3.0] - 2026-09-25
649
+
650
+ ### Added
651
+
652
+ - Projects + environments resources (26 operations), restoring full parity with the gateway
653
+ OpenAPI (88 operations): `projects.{list,create,get,update,delete}`,
654
+ `environments.{list,create,enroll,get,update,start,stop,archive,destroy,enroll_token}`,
655
+ `environments.members.{list,add,set_ssh_key,set_role,remove}`,
656
+ `environments.ports.{list,set,remove}` and `environments.secrets.{list,set,remove}`.
657
+ `enroll`, `create` and `enroll_token` are never retried (the enrolment token is single-use).
658
+ - `_types.py` regenerated: `Project*` and `Environment*` TypedDicts.
659
+ - `system_one.create({"state", "questions"})` — typed decisions from codai-s1 (`POST /v1/systemone`,
660
+ Choice / Score / Noul with calibrated probabilities). Raises on 503 `s1_unavailable`; keep a fallback.
661
+ - `triggers.*`, `notifications.*`, `environments.members.ssh_cert`, `environments.ssh_ca`,
662
+ `environments.tasks.{list,create,get}` — parity with the TypeScript SDK.
663
+
664
+ ## [0.2.1] - 2026-09-24
665
+
666
+ ### Added
667
+
668
+ - `WalletLedgerEntry.source` literal gains `compute_charge` — the debit written every 10 minutes
669
+ for a running managed cloud environment (F2). Additive; regenerated from the gateway OpenAPI.
670
+
671
+ ## [0.2.0] - 2026-09-23
672
+
673
+ ### Added
674
+
675
+ - Full gateway coverage: one method per `operationId` in `apps/docs/openapi/en/gateway.yaml`
676
+ (62 operations), grouped like the TypeScript SDK — `client.chat.completions`, `messages`,
677
+ `responses`, `embeddings`, `audio`, `tokens`, `models`, `health`, `agents` (+ `agents.runs`),
678
+ `tools`, `tasks`, `sessions` (+ `events`, `controls`, `lease`, `shares`), `devices`, `hosts`,
679
+ `orgs` (+ `members`), `account`, `receipt`, `feedback`, `phone_models`.
680
+ - `OPERATION_METHODS` (operationId → dotted method path) and `tests/test_parity.py`, which
681
+ parses the OpenAPI spec and fails on a missing, stale or non-callable mapping and when the
682
+ Python table diverges from the TypeScript one.
683
+ - Generated `codai._types` (`TypedDict`s for every schema plus `<operationId>Body` /
684
+ `<operationId>Response`) from `scripts/gen-types.py`; the parity test fails when it is stale.
685
+ - SSE streams: `chat.completions.stream()` / `messages.stream()` / `responses.stream()` return
686
+ iterables with `.text()`, raw chunks/events and a `.final` aggregate (content, tool calls,
687
+ usage, finish reason, routing headers); `agents.runs.stream()`, `sessions.stream()` and
688
+ `hosts.stream()` are generators of `{"event", "data"}` frames.
689
+ - `ext={...}` extension bag on every method covering all documented `X-Codai-*` request
690
+ headers (`effort`, `thinking`, `thinking_budget`, `cache`, `no_task`, `task_id`, `device`,
691
+ `compact`, `best_of`, `share_token`, …) plus raw `headers`; client-wide `defaults`, `device`,
692
+ `device_name`, `device_platform`, `client` constructor options.
693
+ - `CodaiError.code`, `.request_id`, `.retry_after` parsed from the gateway error envelope.
694
+ - Dev extras `pip install -e ".[dev]"` (pyyaml + pytest); an `http.server` stub test suite.
695
+
696
+ ### Changed
697
+
698
+ - `__version__` now reports the real version (`0.2.0`; it said `0.1.0` in 0.1.1).
699
+ - `chat_stream()` is built on `chat.completions.stream()`; 429/5xx retries add jitter.
700
+ - Runtime stays zero-dependency (stdlib `urllib`), Python 3.9+.
701
+
702
+ ### Backward compatibility
703
+
704
+ - Every 0.1.x method keeps its name, signature and return shape: `chat()`, `chat_stream()`,
705
+ `agents_run()`, `feedback()`, `models()`, `mint_token()`, `embeddings()`, `transcribe()`,
706
+ `speech()`. `chat`, `embeddings`, `models` and `feedback` are callable resource groups, so
707
+ `client.chat([...])` and `client.chat.completions.create({...})` are the same call.
708
+
709
+ ## [0.1.1] - 2026-09-13
710
+
711
+ ### Changed
712
+
713
+ - Repository moved to https://github.com/codai-ro/codai-sdk-python (project URLs updated). No code changes.
714
+
715
+ ## [0.1.0] - 2026-06-23
716
+
717
+ ### Added
718
+
719
+ - Initial public release.
720
+ - `Codai.chat()` — OpenAI-compatible completions with codai extensions
721
+ (`session_id`, `agent_mode`, `compact`, `best_of`).
722
+ - `Codai.chat_stream()` — streaming via generator.
723
+ - `Codai.agents_run()` — server-side agent loop.
724
+ - `Codai.feedback()` — thumbs rating on a completed request.
725
+ - `Codai.models()` — list models available to the key.
726
+ - Zero dependencies (stdlib only), Python 3.9+.
727
+
728
+ [Unreleased]: https://github.com/codai-ro/codai-sdk-python/compare/v0.2.0...HEAD
729
+ [0.2.0]: https://github.com/codai-ro/codai-sdk-python/compare/v0.1.1...v0.2.0
730
+ [0.1.1]: https://github.com/codai-ro/codai-sdk-python/compare/v0.1.0...v0.1.1
731
+ [0.1.0]: https://github.com/codai-ro/codai-sdk-python/releases/tag/v0.1.0