codai-sdk 0.2.1__tar.gz → 0.4.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 (42) hide show
  1. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/CHANGELOG.md +33 -0
  2. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/PKG-INFO +28 -1
  3. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/README.md +27 -0
  4. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/pyproject.toml +1 -1
  5. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/scripts/gen-types.py +39 -16
  6. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/__init__.py +31 -2
  7. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/_operations.py +56 -0
  8. codai_sdk-0.4.0/src/codai/_resolve_types.py +174 -0
  9. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/_types.py +1053 -3
  10. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/resources/__init__.py +24 -0
  11. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/resources/core.py +13 -0
  12. codai_sdk-0.4.0/src/codai/resources/environments.py +183 -0
  13. codai_sdk-0.4.0/src/codai/resources/resolve.py +148 -0
  14. codai_sdk-0.4.0/src/codai/resources/triggers.py +115 -0
  15. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/tests/test_parity.py +4 -3
  16. codai_sdk-0.4.0/tests/test_resolve.py +95 -0
  17. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/tests/test_resources.py +173 -2
  18. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  19. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  20. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/.github/dependabot.yml +0 -0
  21. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/.github/pull_request_template.md +0 -0
  22. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/.github/workflows/publish.yml +0 -0
  23. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/.gitignore +0 -0
  24. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/CODE_OF_CONDUCT.md +0 -0
  25. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/CONTRIBUTING.md +0 -0
  26. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/LICENSE +0 -0
  27. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/SECURITY.md +0 -0
  28. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/examples/agent.py +0 -0
  29. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/examples/chat.py +0 -0
  30. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/examples/feedback.py +0 -0
  31. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/examples/streaming.py +0 -0
  32. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/scripts/smoke_local.py +0 -0
  33. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/_http.py +0 -0
  34. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/resources/_base.py +0 -0
  35. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/resources/agents.py +0 -0
  36. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/resources/chat.py +0 -0
  37. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/resources/messages.py +0 -0
  38. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/resources/platform.py +0 -0
  39. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/resources/responses.py +0 -0
  40. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/src/codai/resources/sessions.py +0 -0
  41. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/tests/conftest.py +0 -0
  42. {codai_sdk-0.2.1 → codai_sdk-0.4.0}/tests/test_http.py +0 -0
@@ -6,6 +6,39 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.0] - 2026-09-27
10
+
11
+ ### Added
12
+
13
+ - `client.resolve` — codai Resolve (pay per verified fix, `https://resolve.codai.ro`,
14
+ override with `resolve_base_url=`): `submit`, `get`, `accept`, `attestation`,
15
+ `health`, `wait_for`. `ResolveError` / `ResolveInsufficientFundsError` (402 carries
16
+ `topup_url`, `balance`, `required`). `RESOLVE_OPERATION_METHODS` maps every
17
+ `resolve.yaml` operationId; `_resolve_types.py` is generated from it.
18
+
19
+ ## [0.3.1] - 2026-09-26
20
+
21
+ ### Changed
22
+
23
+ - Regenerated `_types.py`: project prebuild pool fields, environment `prebuild`
24
+ and `prebuild_claimed` on create (CE-0072).
25
+
26
+ ## [0.3.0] - 2026-09-25
27
+
28
+ ### Added
29
+
30
+ - Projects + environments resources (26 operations), restoring full parity with the gateway
31
+ OpenAPI (88 operations): `projects.{list,create,get,update,delete}`,
32
+ `environments.{list,create,enroll,get,update,start,stop,archive,destroy,enroll_token}`,
33
+ `environments.members.{list,add,set_ssh_key,set_role,remove}`,
34
+ `environments.ports.{list,set,remove}` and `environments.secrets.{list,set,remove}`.
35
+ `enroll`, `create` and `enroll_token` are never retried (the enrolment token is single-use).
36
+ - `_types.py` regenerated: `Project*` and `Environment*` TypedDicts.
37
+ - `system_one.create({"state", "questions"})` — typed decisions from codai-s1 (`POST /v1/systemone`,
38
+ Choice / Score / Noul with calibrated probabilities). Raises on 503 `s1_unavailable`; keep a fallback.
39
+ - `triggers.*`, `notifications.*`, `environments.members.ssh_cert`, `environments.ssh_ca`,
40
+ `environments.tasks.{list,create,get}` — parity with the TypeScript SDK.
41
+
9
42
  ## [0.2.1] - 2026-09-24
10
43
 
11
44
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: codai-sdk
3
- Version: 0.2.1
3
+ Version: 0.4.0
4
4
  Summary: Official Python SDK for the codai AI gateway — chat, messages, responses, streaming, agents, sessions, hosts, tasks, account.
5
5
  Project-URL: Homepage, https://codai.ro
6
6
  Project-URL: Repository, https://github.com/codai-ro/codai-sdk-python
@@ -147,6 +147,33 @@ Typed request/response shapes live in `codai._types` (generated from the
147
147
  OpenAPI spec — `TypedDict`s such as `ChatCompletionRequest`, `Task`,
148
148
  `SessionEvent`, `CreateSessionShareResponse`).
149
149
 
150
+ ## codai Resolve (0.4.0)
151
+
152
+ Pay per verified fix: submit a public GitHub issue, get a flat quote, accept it
153
+ (held on your wallet), and pay only when the fix passes your tests. First 3 fixes
154
+ are free. Lives on `https://resolve.codai.ro` (same key; `resolve_base_url=` to override).
155
+
156
+ ```python
157
+ from codai import Codai, ResolveInsufficientFundsError
158
+
159
+ client = Codai(api_key="codai_...")
160
+ job = client.resolve.submit({
161
+ "repo_url": "https://github.com/owner/repo",
162
+ "issue_url": "https://github.com/owner/repo/issues/42",
163
+ })
164
+ if job["status"] == "quoted":
165
+ try:
166
+ client.resolve.accept(job["id"])
167
+ except ResolveInsufficientFundsError as e:
168
+ print("top up first:", e.topup_url)
169
+ raise
170
+ done = client.resolve.wait_for(job["id"])
171
+ print(done["status"], done.get("attestation_url"))
172
+ ```
173
+
174
+ Failed attempts and infrastructure errors refund the hold in full. Types:
175
+ `codai._resolve_types` (generated from `resolve.yaml`).
176
+
150
177
  ## Development
151
178
 
152
179
  ```bash
@@ -123,6 +123,33 @@ Typed request/response shapes live in `codai._types` (generated from the
123
123
  OpenAPI spec — `TypedDict`s such as `ChatCompletionRequest`, `Task`,
124
124
  `SessionEvent`, `CreateSessionShareResponse`).
125
125
 
126
+ ## codai Resolve (0.4.0)
127
+
128
+ Pay per verified fix: submit a public GitHub issue, get a flat quote, accept it
129
+ (held on your wallet), and pay only when the fix passes your tests. First 3 fixes
130
+ are free. Lives on `https://resolve.codai.ro` (same key; `resolve_base_url=` to override).
131
+
132
+ ```python
133
+ from codai import Codai, ResolveInsufficientFundsError
134
+
135
+ client = Codai(api_key="codai_...")
136
+ job = client.resolve.submit({
137
+ "repo_url": "https://github.com/owner/repo",
138
+ "issue_url": "https://github.com/owner/repo/issues/42",
139
+ })
140
+ if job["status"] == "quoted":
141
+ try:
142
+ client.resolve.accept(job["id"])
143
+ except ResolveInsufficientFundsError as e:
144
+ print("top up first:", e.topup_url)
145
+ raise
146
+ done = client.resolve.wait_for(job["id"])
147
+ print(done["status"], done.get("attestation_url"))
148
+ ```
149
+
150
+ Failed attempts and infrastructure errors refund the hold in full. Types:
151
+ `codai._resolve_types` (generated from `resolve.yaml`).
152
+
126
153
  ## Development
127
154
 
128
155
  ```bash
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codai-sdk"
7
- version = "0.2.1"
7
+ version = "0.4.0"
8
8
  description = "Official Python SDK for the codai AI gateway — chat, messages, responses, streaming, agents, sessions, hosts, tasks, account."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env python3
2
- """Generate ``src/codai/_types.py`` from ``apps/docs/openapi/en/gateway.yaml``.
2
+ """Generate ``src/codai/_types.py`` from ``apps/docs/openapi/en/gateway.yaml``
3
+ (and ``src/codai/_resolve_types.py`` from ``resolve.yaml`` with ``--spec resolve``).
3
4
 
4
5
  TypedDicts for every ``components.schemas`` entry plus a ``<operationId>Body`` /
5
6
  ``<operationId>Response`` alias for each operation's JSON request / 2xx body.
6
7
  Deterministic: the same spec always produces byte-identical output, so a second
7
8
  run is a no-op (the parity CI step diffs the committed file against a fresh run).
8
9
 
9
- .\\.venv\\Scripts\\python scripts/gen-types.py # write
10
- .\\.venv\\Scripts\\python scripts/gen-types.py --check # exit 1 when stale
10
+ .\\.venv\\Scripts\\python scripts/gen-types.py # write
11
+ .\\.venv\\Scripts\\python scripts/gen-types.py --check # exit 1 when stale
12
+ .\\.venv\\Scripts\\python scripts/gen-types.py --spec resolve # Resolve API types
11
13
 
12
14
  Dev-only dependency: pyyaml (``pip install -e ".[dev]"``). The runtime SDK stays
13
15
  zero-dependency — this script is never imported by the package.
@@ -25,15 +27,24 @@ import yaml
25
27
 
26
28
  HERE = Path(__file__).resolve().parent
27
29
  PKG = HERE.parent
28
- SPEC = PKG.parents[1] / "apps" / "docs" / "openapi" / "en" / "gateway.yaml"
30
+ OPENAPI = PKG.parents[1] / "apps" / "docs" / "openapi" / "en"
31
+ SPEC = OPENAPI / "gateway.yaml"
29
32
  OUT = PKG / "src" / "codai" / "_types.py"
33
+ RESOLVE_SPEC = OPENAPI / "resolve.yaml"
34
+ RESOLVE_OUT = PKG / "src" / "codai" / "_resolve_types.py"
35
+
36
+ # --spec name → (spec file, output file, API label, regenerate command)
37
+ TARGETS: Dict[str, Tuple[Path, Path, str, str]] = {
38
+ "gateway": (SPEC, OUT, "gateway", "python scripts/gen-types.py"),
39
+ "resolve": (RESOLVE_SPEC, RESOLVE_OUT, "Resolve", "python scripts/gen-types.py --spec resolve"),
40
+ }
30
41
 
31
42
  HTTP_METHODS = ("get", "post", "put", "patch", "delete")
32
43
 
33
- HEADER = '''"""Typed dictionaries for the codai gateway API.
44
+ HEADER = '''"""Typed dictionaries for the codai {api} API.
34
45
 
35
- generated — do not edit. Source: apps/docs/openapi/en/gateway.yaml.
36
- Regenerate with ``python scripts/gen-types.py`` (see that script's docstring).
46
+ generated — do not edit. Source: apps/docs/openapi/en/{source}.
47
+ Regenerate with ``{regen}`` (see that script's docstring).
37
48
 
38
49
  Every ``components.schemas`` entry is a ``TypedDict`` (``total=False`` when the
39
50
  schema has optional fields; a ``_<Name>Required`` base carries the required
@@ -76,8 +87,15 @@ def union(parts: List[str]) -> str:
76
87
 
77
88
 
78
89
  class Generator:
79
- def __init__(self, spec: Dict[str, Any]) -> None:
90
+ def __init__(
91
+ self,
92
+ spec: Dict[str, Any],
93
+ api: str = "gateway",
94
+ source: str = "gateway.yaml",
95
+ regen: str = "python scripts/gen-types.py",
96
+ ) -> None:
80
97
  self.spec = spec
98
+ self.header = HEADER.format(api=api, source=source, regen=regen)
81
99
  self.schemas: Dict[str, Any] = spec["components"]["schemas"]
82
100
  self.blocks: List[Tuple[str, str]] = []
83
101
  self.emitted: Dict[str, str] = {}
@@ -295,7 +313,7 @@ class Generator:
295
313
  for name in self.schemas: # YAML order
296
314
  self.ensure_schema(name)
297
315
  self.emit_operations()
298
- out = [HEADER]
316
+ out = [self.header]
299
317
  for _, code in self.blocks:
300
318
  out.append(code)
301
319
  out.append("\n\n")
@@ -307,18 +325,23 @@ class Generator:
307
325
 
308
326
 
309
327
  def main(argv: List[str]) -> int:
310
- with SPEC.open("r", encoding="utf-8") as fh:
328
+ name = argv[argv.index("--spec") + 1] if "--spec" in argv else "gateway"
329
+ if name not in TARGETS:
330
+ print(f"unknown --spec {name!r}; one of {', '.join(TARGETS)}", file=sys.stderr)
331
+ return 2
332
+ spec_path, out, api, regen = TARGETS[name]
333
+ with spec_path.open("r", encoding="utf-8") as fh:
311
334
  spec = yaml.safe_load(fh)
312
- text = Generator(spec).render()
335
+ text = Generator(spec, api=api, source=spec_path.name, regen=regen).render()
313
336
  if "--check" in argv:
314
- current = OUT.read_text(encoding="utf-8") if OUT.exists() else ""
337
+ current = out.read_text(encoding="utf-8") if out.exists() else ""
315
338
  if current != text:
316
- print(f"STALE: {OUT} differs from a fresh generation", file=sys.stderr)
339
+ print(f"STALE: {out} differs from a fresh generation", file=sys.stderr)
317
340
  return 1
318
- print(f"OK: {OUT} is up to date")
341
+ print(f"OK: {out} is up to date")
319
342
  return 0
320
- OUT.write_text(text, encoding="utf-8", newline="\n")
321
- print(f"wrote {OUT} ({text.count(chr(10))} lines, {len(spec['components']['schemas'])} schemas)")
343
+ out.write_text(text, encoding="utf-8", newline="\n")
344
+ print(f"wrote {out} ({text.count(chr(10))} lines, {len(spec['components']['schemas'])} schemas)")
322
345
  return 0
323
346
 
324
347
 
@@ -32,8 +32,9 @@ from __future__ import annotations
32
32
  from typing import Any, Dict, Generator, List, Optional
33
33
 
34
34
  from ._http import CodaiError, HttpClient, RequestExtensions, SseFrame, extension_headers, frame_json, read_sse
35
- from ._operations import OPERATION_METHODS, STREAMING_OPERATIONS
35
+ from ._operations import OPERATION_METHODS, RESOLVE_OPERATION_METHODS, STREAMING_OPERATIONS
36
36
  from .resources import (
37
+ RESOLVE_TERMINAL_STATUSES,
37
38
  Account,
38
39
  AgentRunResult,
39
40
  Agents,
@@ -43,20 +44,28 @@ from .resources import (
43
44
  ChatStream,
44
45
  Devices,
45
46
  Embeddings,
47
+ Environments,
46
48
  EphemeralToken,
47
49
  Feedback,
48
50
  Health,
49
51
  Hosts,
50
52
  Messages,
51
53
  Models,
54
+ Notifications,
52
55
  Orgs,
53
56
  PhoneModels,
57
+ Projects,
54
58
  Receipts,
59
+ Resolve,
60
+ ResolveError,
61
+ ResolveInsufficientFundsError,
55
62
  Responses,
56
63
  Sessions,
64
+ SystemOne,
57
65
  Tasks,
58
66
  Tokens,
59
67
  Tools,
68
+ Triggers,
60
69
  )
61
70
 
62
71
  __all__ = [
@@ -70,12 +79,16 @@ __all__ = [
70
79
  "RequestExtensions",
71
80
  "SseFrame",
72
81
  "OPERATION_METHODS",
82
+ "RESOLVE_OPERATION_METHODS",
83
+ "RESOLVE_TERMINAL_STATUSES",
84
+ "ResolveError",
85
+ "ResolveInsufficientFundsError",
73
86
  "STREAMING_OPERATIONS",
74
87
  "extension_headers",
75
88
  "frame_json",
76
89
  "read_sse",
77
90
  ]
78
- __version__ = "0.2.1"
91
+ __version__ = "0.4.0"
79
92
 
80
93
 
81
94
  class _CallableGroup:
@@ -108,6 +121,7 @@ class Codai:
108
121
  client: Optional[str] = None,
109
122
  defaults: Optional[RequestExtensions] = None,
110
123
  opener: Any = None,
124
+ resolve_base_url: str = "https://resolve.codai.ro",
111
125
  ) -> None:
112
126
  if not api_key:
113
127
  raise ValueError("Codai: api_key is required")
@@ -134,6 +148,7 @@ class Codai:
134
148
  self.messages = Messages(http)
135
149
  self.responses = Responses(http)
136
150
  self.audio = Audio(http)
151
+ self.system_one = SystemOne(http)
137
152
  self.tokens = Tokens(http)
138
153
  self.health = Health(http)
139
154
  self.agents = Agents(http)
@@ -143,9 +158,23 @@ class Codai:
143
158
  self.devices = Devices(http)
144
159
  self.hosts = Hosts(http)
145
160
  self.orgs = Orgs(http)
161
+ self.projects = Projects(http)
162
+ self.environments = Environments(http)
146
163
  self.account = Account(http)
147
164
  self.receipt = Receipts(http)
148
165
  self.phone_models = PhoneModels(http)
166
+ self.triggers = Triggers(http)
167
+ self.notifications = Notifications(http)
168
+ # codai Resolve lives on its own host; same key, no X-Codai-* defaults.
169
+ self.resolve = Resolve(
170
+ HttpClient(
171
+ api_key=api_key,
172
+ base_url=resolve_base_url,
173
+ timeout=timeout,
174
+ max_retries=max_retries,
175
+ opener=opener,
176
+ )
177
+ )
149
178
 
150
179
  self.chat: Any = _CallableGroup(Chat(http), self._legacy_chat)
151
180
  self.embeddings: Any = _CallableGroup(Embeddings(http), self._legacy_embeddings)
@@ -16,6 +16,7 @@ OPERATION_METHODS: Dict[str, str] = {
16
16
  "createMessage": "messages.create",
17
17
  # Embeddings & Audio
18
18
  "createEmbeddings": "embeddings.create",
19
+ "createSystemOne": "system_one.create",
19
20
  "createTranscription": "audio.transcribe",
20
21
  "createSpeech": "audio.speech",
21
22
  "createEphemeralToken": "tokens.create",
@@ -78,6 +79,52 @@ OPERATION_METHODS: Dict[str, str] = {
78
79
  "listOrgMembers": "orgs.members.list",
79
80
  "addOrgMember": "orgs.members.add",
80
81
  "removeOrgMember": "orgs.members.remove",
82
+ # Projects
83
+ "listProjects": "projects.list",
84
+ "createProject": "projects.create",
85
+ "getProject": "projects.get",
86
+ "updateProject": "projects.update",
87
+ "deleteProject": "projects.delete",
88
+ # Environments
89
+ "listEnvironments": "environments.list",
90
+ "createEnvironment": "environments.create",
91
+ "enrollEnvironment": "environments.enroll",
92
+ "getEnvironment": "environments.get",
93
+ "updateEnvironment": "environments.update",
94
+ "startEnvironment": "environments.start",
95
+ "stopEnvironment": "environments.stop",
96
+ "archiveEnvironment": "environments.archive",
97
+ "destroyEnvironment": "environments.destroy",
98
+ "createEnvironmentEnrollToken": "environments.enroll_token",
99
+ "listEnvironmentMembers": "environments.members.list",
100
+ "addEnvironmentMember": "environments.members.add",
101
+ "setMyEnvironmentSshKey": "environments.members.set_ssh_key",
102
+ "setEnvironmentMemberRole": "environments.members.set_role",
103
+ "removeEnvironmentMember": "environments.members.remove",
104
+ "listEnvironmentPorts": "environments.ports.list",
105
+ "setEnvironmentPort": "environments.ports.set",
106
+ "removeEnvironmentPort": "environments.ports.remove",
107
+ "listEnvironmentSecrets": "environments.secrets.list",
108
+ "setEnvironmentSecret": "environments.secrets.set",
109
+ "removeEnvironmentSecret": "environments.secrets.remove",
110
+ "issueMyEnvironmentSshCert": "environments.members.ssh_cert",
111
+ "getEnvironmentSshCa": "environments.ssh_ca",
112
+ "listEnvironmentTasks": "environments.tasks.list",
113
+ "createEnvironmentTask": "environments.tasks.create",
114
+ "getEnvironmentTask": "environments.tasks.get",
115
+ "listTriggers": "triggers.list",
116
+ "createTrigger": "triggers.create",
117
+ "updateTrigger": "triggers.update",
118
+ "deleteTrigger": "triggers.delete",
119
+ "rotateTriggerSecret": "triggers.rotate_secret",
120
+ "setTriggerSecret": "triggers.set_secret",
121
+ "testTrigger": "triggers.test",
122
+ "listTriggerEvents": "triggers.events",
123
+ "receiveTriggerHook": "triggers.fire_hook",
124
+ "listNotifications": "notifications.list",
125
+ "markNotificationsRead": "notifications.mark_read",
126
+ "deleteNotification": "notifications.delete",
127
+ "deleteNotifications": "notifications.delete_many",
81
128
  # Account
82
129
  "getAccount": "account.get",
83
130
  "patchAccount": "account.update",
@@ -88,3 +135,12 @@ OPERATION_METHODS: Dict[str, str] = {
88
135
 
89
136
  # Operations whose SDK method is a generator (SSE-only endpoints).
90
137
  STREAMING_OPERATIONS: FrozenSet[str] = frozenset({"streamAgentRun", "streamSession", "streamHost"})
138
+
139
+ # codai Resolve (``apps/docs/openapi/en/resolve.yaml``) → ``client.resolve.*``.
140
+ RESOLVE_OPERATION_METHODS: Dict[str, str] = {
141
+ "resolveHealth": "resolve.health",
142
+ "createResolveJob": "resolve.submit",
143
+ "getResolveJob": "resolve.get",
144
+ "acceptResolveJob": "resolve.accept",
145
+ "getResolveAttestation": "resolve.attestation",
146
+ }
@@ -0,0 +1,174 @@
1
+ """Typed dictionaries for the codai Resolve API.
2
+
3
+ generated — do not edit. Source: apps/docs/openapi/en/resolve.yaml.
4
+ Regenerate with ``python scripts/gen-types.py --spec resolve`` (see that script's docstring).
5
+
6
+ Every ``components.schemas`` entry is a ``TypedDict`` (``total=False`` when the
7
+ schema has optional fields; a ``_<Name>Required`` base carries the required
8
+ keys of mixed schemas). Non-object schemas (enums, unions, arrays) are type
9
+ aliases. ``<operationId>Body`` / ``<operationId>Response`` name each
10
+ operation's JSON request body and first 2xx JSON response.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Any, Dict, List, Literal, Optional, TypedDict, Union
16
+
17
+ class ResolveHealth(TypedDict):
18
+ ok: bool
19
+ service: str
20
+
21
+
22
+ class ResolveValidationDetails(TypedDict):
23
+ """Zod `flatten()` output returned when the intake body fails schema validation."""
24
+ formErrors: List[str]
25
+ fieldErrors: Dict[str, List[str]]
26
+
27
+
28
+ class ResolveError(TypedDict):
29
+ """Flat error envelope used by every Resolve endpoint. Not the gateway `{ error: { code, message } }` shape."""
30
+ error: Union[str, ResolveValidationDetails]
31
+
32
+
33
+ class _ResolveIntakeRequired(TypedDict):
34
+ repo_url: str
35
+
36
+
37
+ class ResolveIntake(_ResolveIntakeRequired, total=False):
38
+ """Body of `POST /v1/resolve/jobs`. Provide `issue_url` or `issue_text` (at least one)."""
39
+ issue_url: str
40
+ issue_text: str
41
+ test_command: str
42
+
43
+
44
+ # `free` (first 3 resolves per user), `t7` ($7), `t19` ($19), `t49` ($49). Prices are in USD micros on the wire.
45
+ ResolveTier = Literal['free', 't7', 't19', 't49']
46
+
47
+
48
+ # Lifecycle: `triage → quoted → accepted → running → resolved | failed`, plus `declined`
49
+ ResolveJobStatus = Literal['triage', 'quoted', 'declined', 'accepted', 'running', 'resolved', 'failed', 'error']
50
+
51
+
52
+ class ResolveQuote(TypedDict):
53
+ """`201` body — the job was quoted (`quoted`) or auto-accepted on the free tier (`accepted`)."""
54
+ id: str
55
+ status: Literal['quoted', 'accepted']
56
+ tier: ResolveTier
57
+ quote_micro_usd: int
58
+ triage_reason: str
59
+ free_tier_remaining: int
60
+
61
+
62
+ class ResolveDeclined(TypedDict):
63
+ """`200` body — triage declined the issue. The job is persisted with `status: declined` and is never billed."""
64
+ id: str
65
+ status: str
66
+ decline_reason: str
67
+
68
+
69
+ class ResolveAccepted(TypedDict):
70
+ """Accepted"""
71
+ id: str
72
+ status: str
73
+
74
+
75
+ class ResolveInsufficientFunds(TypedDict):
76
+ """`402` body of `POST /v1/resolve/jobs/{id}/accept` when the wallet cannot hold the quote."""
77
+ error: str
78
+ balance: int
79
+ required: int
80
+ currency: str
81
+ topup_url: str
82
+
83
+
84
+ class ResolveJob(TypedDict):
85
+ """Full job view returned by `GET /v1/resolve/jobs/{id}`. Nullable fields are `null` until the corresponding stag…"""
86
+ id: str
87
+ status: ResolveJobStatus
88
+ repo_url: str
89
+ tier: Optional[ResolveTier]
90
+ quote_micro_usd: Optional[int]
91
+ triage_reason: Optional[str]
92
+ decline_reason: Optional[str]
93
+ fail_reason: Optional[str]
94
+ cost_micro_usd: int
95
+ attestation_id: Optional[str]
96
+ attestation_url: Optional[str]
97
+ created_at: str
98
+ completed_at: Optional[str]
99
+
100
+
101
+ class ResolveReproTest(TypedDict):
102
+ """Repro test written by the agent when you did not supply `test_command`."""
103
+ path: str
104
+ content: Optional[str]
105
+
106
+
107
+ class ResolveAttestation(TypedDict):
108
+ """Public execution proof. Everything needed to reproduce the verification locally."""
109
+ id: str
110
+ kind: str
111
+ repo_url: str
112
+ base_commit: str
113
+ patch: str
114
+ repro_test: Optional[ResolveReproTest]
115
+ test_command: str
116
+ regression_command: Optional[str]
117
+ test_output_before: str
118
+ test_output_after: str
119
+ regression_output: Optional[str]
120
+ runner_image_digest: str
121
+ started_at: str
122
+ verified_at: str
123
+
124
+
125
+ # GET /health — 200 response
126
+ ResolveHealthResponse = ResolveHealth
127
+
128
+
129
+ # POST /v1/resolve/jobs — request body
130
+ CreateResolveJobBody = ResolveIntake
131
+
132
+
133
+ # POST /v1/resolve/jobs — 200 response
134
+ CreateResolveJobResponse = ResolveDeclined
135
+
136
+
137
+ # POST /v1/resolve/jobs — 201 response
138
+ CreateResolveJobResponse201 = ResolveQuote
139
+
140
+
141
+ # GET /v1/resolve/jobs/{id} — 200 response
142
+ GetResolveJobResponse = ResolveJob
143
+
144
+
145
+ # POST /v1/resolve/jobs/{id}/accept — 200 response
146
+ AcceptResolveJobResponse = ResolveAccepted
147
+
148
+
149
+ # GET /v1/resolve/attestations/{id} — 200 response
150
+ GetResolveAttestationResponse = ResolveAttestation
151
+
152
+
153
+ __all__ = [
154
+ 'AcceptResolveJobResponse',
155
+ 'CreateResolveJobBody',
156
+ 'CreateResolveJobResponse',
157
+ 'CreateResolveJobResponse201',
158
+ 'GetResolveAttestationResponse',
159
+ 'GetResolveJobResponse',
160
+ 'ResolveAccepted',
161
+ 'ResolveAttestation',
162
+ 'ResolveDeclined',
163
+ 'ResolveError',
164
+ 'ResolveHealth',
165
+ 'ResolveHealthResponse',
166
+ 'ResolveInsufficientFunds',
167
+ 'ResolveIntake',
168
+ 'ResolveJob',
169
+ 'ResolveJobStatus',
170
+ 'ResolveQuote',
171
+ 'ResolveReproTest',
172
+ 'ResolveTier',
173
+ 'ResolveValidationDetails',
174
+ ]