unique-sdk 2026.28.0.dev18__tar.gz → 2026.30.0.dev0__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. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/PKG-INFO +1 -1
  2. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/pyproject.toml +1 -1
  3. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/browser.py +50 -17
  4. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/README.md +0 -0
  5. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/__init__.py +0 -0
  6. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_api_requestor.py +0 -0
  7. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_api_resource.py +0 -0
  8. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_api_version.py +0 -0
  9. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_error.py +0 -0
  10. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_http_client.py +0 -0
  11. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_list_object.py +0 -0
  12. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_object_classes.py +0 -0
  13. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_request_options.py +0 -0
  14. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_unique_object.py +0 -0
  15. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_unique_ql.py +0 -0
  16. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_unique_response.py +0 -0
  17. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_util.py +0 -0
  18. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_version.py +0 -0
  19. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/_webhook.py +0 -0
  20. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/__init__.py +0 -0
  21. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_acronyms.py +0 -0
  22. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_agentic_table.py +0 -0
  23. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_analytics_order.py +0 -0
  24. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_benchmarking.py +0 -0
  25. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_briefing.py +0 -0
  26. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_chat_completion.py +0 -0
  27. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_content.py +0 -0
  28. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_dynamic_frontend.py +0 -0
  29. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_elicitation.py +0 -0
  30. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_embedding.py +0 -0
  31. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_event.py +0 -0
  32. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_folder.py +0 -0
  33. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_group.py +0 -0
  34. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_integrated.py +0 -0
  35. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_llm_models.py +0 -0
  36. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_mcp.py +0 -0
  37. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_message.py +0 -0
  38. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_message_assessment.py +0 -0
  39. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_message_execution.py +0 -0
  40. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_message_log.py +0 -0
  41. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_message_tool.py +0 -0
  42. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_module.py +0 -0
  43. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_scheduled_task.py +0 -0
  44. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_search.py +0 -0
  45. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_search_string.py +0 -0
  46. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_short_term_memory.py +0 -0
  47. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_space.py +0 -0
  48. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_user.py +0 -0
  49. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/api_resources/_web_search.py +0 -0
  50. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/__init__.py +0 -0
  51. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/__main__.py +0 -0
  52. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/cli.py +0 -0
  53. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/__init__.py +0 -0
  54. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/_citation_manifest.py +0 -0
  55. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/cite_file.py +0 -0
  56. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/dynamic_frontend.py +0 -0
  57. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/elicitation.py +0 -0
  58. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/files.py +0 -0
  59. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/folders.py +0 -0
  60. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/mcp.py +0 -0
  61. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/navigation.py +0 -0
  62. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/read.py +0 -0
  63. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
  64. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/search.py +0 -0
  65. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/subagent.py +0 -0
  66. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/web_search.py +0 -0
  67. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/commands/web_search_config.py +0 -0
  68. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/config.py +0 -0
  69. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/formatting.py +0 -0
  70. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/metadata_filter.py +0 -0
  71. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/shell.py +0 -0
  72. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/skills/unique-cli-dynamic-frontend/SKILL.md +0 -0
  73. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
  74. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
  75. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
  76. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
  77. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
  78. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
  79. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +0 -0
  80. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/cli/state.py +0 -0
  81. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/utils/analytics_order_run.py +0 -0
  82. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/utils/benchmarking_run.py +0 -0
  83. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/utils/chat_history.py +0 -0
  84. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/utils/chat_in_space.py +0 -0
  85. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/utils/file_io.py +0 -0
  86. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/utils/sources.py +0 -0
  87. {unique_sdk-2026.28.0.dev18 → unique_sdk-2026.30.0.dev0}/unique_sdk/utils/token.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-sdk
3
- Version: 2026.28.0.dev18
3
+ Version: 2026.30.0.dev0
4
4
  Summary:
5
5
  Author: Martin Fadler, Konstantin Krauss, Andreas Hauri
6
6
  Author-email: Martin Fadler <martin.fadler@unique.ch>, Konstantin Krauss <konstantin@unique.ch>, Andreas Hauri <andreas@unique.ch>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_sdk"
3
- version = "2026.28.0.dev18"
3
+ version = "2026.30.0.dev0"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -10,7 +10,13 @@ Unlike the knowledge-base commands (which hit the platform ``api_base`` via
10
10
  the Swappable Intelligence runner in ``.unique-browser.json``:
11
11
 
12
12
  {"bridgeUrl": "https://gateway.<cluster>/browser-bridge",
13
- "installUrl": "https://..."}
13
+ "installUrl": "https://...",
14
+ "chatId": "chat_...", # optional — the conversation driving this turn
15
+ "spaceId": "..."} # optional — the assistant/space the chat belongs to
16
+
17
+ ``chatId`` / ``spaceId`` are forwarded as ``x-chat-id`` / ``x-space-id`` so the
18
+ bridge can tell the extension which conversation a steering command belongs to,
19
+ letting the side panel mirror that chat.
14
20
 
15
21
  The command therefore issues plain HTTP requests to
16
22
  ``{bridgeUrl}/public/browser/{status,action,control,download}`` with the same
@@ -103,23 +109,41 @@ def _bridge_endpoint(bridge_url: str, endpoint: str) -> str:
103
109
  return f"{bridge_url.rstrip('/')}/{_BRIDGE_API_PREFIX}/{endpoint}"
104
110
 
105
111
 
106
- def _auth_headers(state: ShellState, *, json_body: bool) -> dict[str, str]:
112
+ def _auth_headers(
113
+ state: ShellState,
114
+ *,
115
+ json_body: bool,
116
+ config: dict[str, Any] | None = None,
117
+ ) -> dict[str, str]:
107
118
  """Identity headers mirroring ``unique_sdk`` request headers.
108
119
 
109
120
  On a secured cluster / localhost the gateway injects identity from the
110
121
  request context and ``api_key`` / ``app_id`` may be empty; we still send
111
122
  ``x-user-id`` / ``x-company-id`` so the bridge can key the connection.
123
+
124
+ ``chatId`` / ``spaceId`` from ``.unique-browser.json`` are forwarded as
125
+ ``x-chat-id`` / ``x-space-id`` so the bridge can stamp the relayed
126
+ ``RequestFrame`` with the conversation the agent turn belongs to — the
127
+ extension then mirrors that chat in its side panel. Absent on older
128
+ configs; the bridge treats a missing header as "unknown chat".
112
129
  """
113
130
  headers: dict[str, str] = {"Accept": "application/json"}
114
- config = state.config
115
- if config.api_key:
116
- headers["Authorization"] = f"Bearer {config.api_key}"
117
- if config.user_id:
118
- headers["x-user-id"] = config.user_id
119
- if config.company_id:
120
- headers["x-company-id"] = config.company_id
121
- if config.app_id:
122
- headers["x-app-id"] = config.app_id
131
+ config_state = state.config
132
+ if config_state.api_key:
133
+ headers["Authorization"] = f"Bearer {config_state.api_key}"
134
+ if config_state.user_id:
135
+ headers["x-user-id"] = config_state.user_id
136
+ if config_state.company_id:
137
+ headers["x-company-id"] = config_state.company_id
138
+ if config_state.app_id:
139
+ headers["x-app-id"] = config_state.app_id
140
+ if config:
141
+ chat_id = config.get("chatId")
142
+ if isinstance(chat_id, str) and chat_id:
143
+ headers["x-chat-id"] = chat_id
144
+ space_id = config.get("spaceId")
145
+ if isinstance(space_id, str) and space_id:
146
+ headers["x-space-id"] = space_id
123
147
  if json_body:
124
148
  headers["Content-Type"] = "application/json"
125
149
  return headers
@@ -179,7 +203,7 @@ def cmd_browser_status(state: ShellState, *, config_path: str | None = None) ->
179
203
  try:
180
204
  resp = requests.get(
181
205
  url,
182
- headers=_auth_headers(state, json_body=False),
206
+ headers=_auth_headers(state, json_body=False, config=config),
183
207
  timeout=_ACTION_TIMEOUT_SECONDS,
184
208
  )
185
209
  except requests.RequestException as exc:
@@ -211,7 +235,7 @@ def _post_verb(
211
235
  try:
212
236
  resp = requests.post(
213
237
  url,
214
- headers=_auth_headers(state, json_body=True),
238
+ headers=_auth_headers(state, json_body=True, config=config),
215
239
  data=json.dumps(payload),
216
240
  timeout=_ACTION_TIMEOUT_SECONDS,
217
241
  )
@@ -286,7 +310,7 @@ def cmd_browser_download(
286
310
  try:
287
311
  resp = requests.post(
288
312
  endpoint,
289
- headers=_auth_headers(state, json_body=True),
313
+ headers=_auth_headers(state, json_body=True, config=config),
290
314
  data=json.dumps(payload),
291
315
  timeout=_DOWNLOAD_TIMEOUT_SECONDS,
292
316
  stream=True,
@@ -303,12 +327,17 @@ def cmd_browser_download(
303
327
  dest_path = Path(dest).expanduser()
304
328
 
305
329
  total = 0
330
+ # Track whether we actually opened (and therefore truncated/created) the
331
+ # destination: a failure before open must NOT unlink a pre-existing file
332
+ # that this command never wrote to.
333
+ file_opened = False
306
334
  try:
307
335
  # Parent creation lives inside the try so a permission / filesystem
308
336
  # failure surfaces as an ok:false envelope instead of a traceback.
309
337
  if dest_path.parent and not dest_path.parent.exists():
310
338
  dest_path.parent.mkdir(parents=True, exist_ok=True)
311
339
  with dest_path.open("wb") as handle:
340
+ file_opened = True
312
341
  for chunk in resp.iter_content(chunk_size=_DOWNLOAD_CHUNK_BYTES):
313
342
  if chunk:
314
343
  handle.write(chunk)
@@ -316,14 +345,18 @@ def cmd_browser_download(
316
345
  except requests.RequestException as exc:
317
346
  # Network failures during streaming (timeout, dropped connection,
318
347
  # chunked-encoding error) — drop any truncated bytes so a later step
319
- # can't mistake the partial file for a complete download.
320
- dest_path.unlink(missing_ok=True)
348
+ # can't mistake the partial file for a complete download. Only unlink
349
+ # if we actually opened the file; otherwise leave any pre-existing
350
+ # file untouched.
351
+ if file_opened:
352
+ dest_path.unlink(missing_ok=True)
321
353
  return _err(
322
354
  "browser_bridge_unreachable",
323
355
  f"download stream from the browser bridge failed: {exc}",
324
356
  )
325
357
  except OSError as exc:
326
- dest_path.unlink(missing_ok=True)
358
+ if file_opened:
359
+ dest_path.unlink(missing_ok=True)
327
360
  return _err(
328
361
  "browser_download_write_failed",
329
362
  f"could not prepare or write {dest_path}: {exc}",