unique-sdk 2026.32.0.dev6__tar.gz → 2026.32.0.dev8__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 (91) hide show
  1. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/PKG-INFO +1 -1
  2. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/pyproject.toml +1 -1
  3. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_api_requestor.py +8 -8
  4. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_util.py +98 -0
  5. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/web_search.py +39 -2
  6. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/README.md +0 -0
  7. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/__init__.py +0 -0
  8. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_api_resource.py +0 -0
  9. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_api_version.py +0 -0
  10. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_error.py +0 -0
  11. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_http_client.py +0 -0
  12. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_list_object.py +0 -0
  13. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_object_classes.py +0 -0
  14. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_request_options.py +0 -0
  15. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_unique_object.py +0 -0
  16. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_unique_ql.py +0 -0
  17. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_unique_response.py +0 -0
  18. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_version.py +0 -0
  19. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/_webhook.py +0 -0
  20. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/__init__.py +0 -0
  21. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_acronyms.py +0 -0
  22. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_agentic_table.py +0 -0
  23. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_analytics_order.py +0 -0
  24. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_benchmarking.py +0 -0
  25. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_briefing.py +0 -0
  26. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_chat_completion.py +0 -0
  27. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_content.py +0 -0
  28. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_dynamic_frontend.py +0 -0
  29. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_elicitation.py +0 -0
  30. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_embedding.py +0 -0
  31. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_event.py +0 -0
  32. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_folder.py +0 -0
  33. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_group.py +0 -0
  34. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_integrated.py +0 -0
  35. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_llm_models.py +0 -0
  36. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_mcp.py +0 -0
  37. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_message.py +0 -0
  38. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_message_assessment.py +0 -0
  39. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_message_execution.py +0 -0
  40. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_message_log.py +0 -0
  41. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_message_tool.py +0 -0
  42. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_module.py +0 -0
  43. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_scheduled_task.py +0 -0
  44. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_search.py +0 -0
  45. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_search_string.py +0 -0
  46. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_short_term_memory.py +0 -0
  47. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_space.py +0 -0
  48. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_user.py +0 -0
  49. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/api_resources/_web_search.py +0 -0
  50. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/__init__.py +0 -0
  51. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/__main__.py +0 -0
  52. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/cli.py +0 -0
  53. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/__init__.py +0 -0
  54. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/_citation_manifest.py +0 -0
  55. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/agentic_table.py +0 -0
  56. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/browser.py +0 -0
  57. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/cite_file.py +0 -0
  58. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/dynamic_frontend.py +0 -0
  59. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/elicitation.py +0 -0
  60. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/files.py +0 -0
  61. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/folders.py +0 -0
  62. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/mcp.py +0 -0
  63. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/navigation.py +0 -0
  64. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/read.py +0 -0
  65. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
  66. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/search.py +0 -0
  67. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/subagent.py +0 -0
  68. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/commands/web_search_config.py +0 -0
  69. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/config.py +0 -0
  70. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/formatting.py +0 -0
  71. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/identity.py +0 -0
  72. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/metadata_filter.py +0 -0
  73. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/shell.py +0 -0
  74. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-agentic-table/SKILL.md +0 -0
  75. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-dynamic-frontend/SKILL.md +0 -0
  76. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
  77. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
  78. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
  79. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
  80. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
  81. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
  82. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-uploaded-search/SKILL.md +0 -0
  83. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +0 -0
  84. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/cli/state.py +0 -0
  85. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/utils/analytics_order_run.py +0 -0
  86. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/utils/benchmarking_run.py +0 -0
  87. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/utils/chat_history.py +0 -0
  88. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/utils/chat_in_space.py +0 -0
  89. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/utils/file_io.py +0 -0
  90. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/unique_sdk/utils/sources.py +0 -0
  91. {unique_sdk-2026.32.0.dev6 → unique_sdk-2026.32.0.dev8}/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.32.0.dev6
3
+ Version: 2026.32.0.dev8
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.32.0.dev6"
3
+ version = "2026.32.0.dev8"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -194,8 +194,7 @@ class APIRequestor(object):
194
194
  )
195
195
 
196
196
  _util.log_info("Request to Unique", method=method, path=abs_url)
197
- _util.log_debug(
198
- "Request details",
197
+ _util.log_request_details(
199
198
  data=post_data,
200
199
  headers=headers,
201
200
  api_version=self.api_version,
@@ -206,7 +205,7 @@ class APIRequestor(object):
206
205
  )
207
206
 
208
207
  _util.log_info("Unique response", path=abs_url, status=rcode)
209
- _util.log_debug("Unique response body", body=rcontent)
208
+ _util.log_response_body(body=rcontent)
210
209
 
211
210
  if "Request-Id" in rheaders:
212
211
  request_id = rheaders["Request-Id"]
@@ -230,11 +229,11 @@ class APIRequestor(object):
230
229
  )
231
230
 
232
231
  _util.log_info("Async request to Unique", method=method, path=abs_url)
233
- _util.log_debug(
234
- "Async request details",
232
+ _util.log_request_details(
235
233
  data=post_data,
236
234
  headers=headers,
237
235
  api_version=self.api_version,
236
+ message="Async request details",
238
237
  )
239
238
 
240
239
  rcontent, rcode, rheaders = await self._client.request_async(
@@ -242,7 +241,7 @@ class APIRequestor(object):
242
241
  )
243
242
 
244
243
  _util.log_info("Unique response", path=abs_url, status=rcode)
245
- _util.log_debug("Unique response body", body=rcontent)
244
+ _util.log_response_body(body=rcontent)
246
245
 
247
246
  if "Request-Id" in rheaders:
248
247
  request_id = rheaders["Request-Id"]
@@ -364,7 +363,8 @@ class APIRequestor(object):
364
363
  except Exception as e:
365
364
  raise _error.APIError(
366
365
  "Invalid response body from API: %s "
367
- "(HTTP response code was %d)" % (rbody, rcode),
366
+ "(HTTP response code was %d)"
367
+ % (_util.redacted_body_for_error_message(rbody), rcode),
368
368
  cast(bytes, rbody),
369
369
  rcode,
370
370
  rheaders,
@@ -399,7 +399,7 @@ class APIRequestor(object):
399
399
  error_code=status,
400
400
  error_type=error_data.get("type"),
401
401
  error_message=error_data.get("message"),
402
- error_params=error_data.get("params"),
402
+ error_params=_util.redacted_error_params(error_data.get("params")),
403
403
  )
404
404
 
405
405
  error = cause.get("error", {}) if cause else {}
@@ -9,6 +9,7 @@ import random
9
9
  import re
10
10
  import sys
11
11
  import time
12
+ from collections.abc import Mapping
12
13
  from functools import wraps
13
14
  from typing import (
14
15
  Any,
@@ -33,6 +34,8 @@ UNIQUE_LOG = os.environ.get("UNIQUE_LOG")
33
34
 
34
35
  logger: logging.Logger = logging.getLogger("unique")
35
36
 
37
+ _REDACTED = "<redacted>"
38
+
36
39
 
37
40
  def _console_log_level():
38
41
  if unique_sdk.log in ["debug", "info"]:
@@ -42,6 +45,42 @@ def _console_log_level():
42
45
  return None
43
46
 
44
47
 
48
+ def _insecure_log_payloads_enabled() -> bool:
49
+ return os.environ.get("INSECURE_UNIQUE_SDK_LOG_PAYLOADS") == "true"
50
+
51
+
52
+ def _payload_byte_size(value: Any) -> int:
53
+ if value is None:
54
+ return 0
55
+ if isinstance(value, (bytes, bytearray)):
56
+ return len(value)
57
+ if isinstance(value, str):
58
+ return len(value.encode("utf-8"))
59
+ return len(str(value).encode("utf-8"))
60
+
61
+
62
+ def _headers_for_log(headers: Mapping[str, str] | None) -> dict[str, str] | str | None:
63
+ """Return headers for debug logs; Authorization is always redacted."""
64
+ if not _insecure_log_payloads_enabled():
65
+ return _REDACTED
66
+ if headers is None:
67
+ return None
68
+ try:
69
+ sanitized = dict(headers)
70
+ except (TypeError, ValueError):
71
+ return _REDACTED
72
+ for key in list(sanitized):
73
+ if str(key).lower() == "authorization":
74
+ sanitized[key] = _REDACTED
75
+ return sanitized
76
+
77
+
78
+ def _body_for_log(body: Any) -> Any:
79
+ if _insecure_log_payloads_enabled():
80
+ return body
81
+ return _REDACTED
82
+
83
+
45
84
  def log_debug(message, **params):
46
85
  msg = logfmt(dict(message=message, **params))
47
86
  if _console_log_level() == "debug":
@@ -56,6 +95,65 @@ def log_info(message, **params):
56
95
  logger.info(msg)
57
96
 
58
97
 
98
+ def log_request_details(
99
+ *,
100
+ data: Any,
101
+ headers: Mapping[str, str] | None,
102
+ api_version: str | None,
103
+ message: str = "Request details",
104
+ ) -> None:
105
+ if _insecure_log_payloads_enabled():
106
+ log_debug(
107
+ message,
108
+ data=data,
109
+ headers=_headers_for_log(headers),
110
+ api_version=api_version,
111
+ )
112
+ return
113
+ log_debug(
114
+ message,
115
+ data=_REDACTED,
116
+ headers=_REDACTED,
117
+ api_version=api_version,
118
+ payload_bytes=_payload_byte_size(data),
119
+ )
120
+
121
+
122
+ def log_response_body(*, body: Any) -> None:
123
+ if _insecure_log_payloads_enabled():
124
+ log_debug("Unique response body", body=body)
125
+ return
126
+ log_debug(
127
+ "Unique response body",
128
+ body=_body_for_log(body),
129
+ payload_bytes=_payload_byte_size(body),
130
+ )
131
+
132
+
133
+ def redacted_body_for_error_message(body: Any) -> Any:
134
+ """Body representation safe to embed in exception *messages*.
135
+
136
+ Exception messages surface at ERROR (retry logs, tracebacks) regardless
137
+ of LOG_LEVEL, so the raw body must not be embedded unless the insecure
138
+ opt-in is set. The full body stays available programmatically on the
139
+ exception's ``http_body`` attribute.
140
+ """
141
+ if _insecure_log_payloads_enabled():
142
+ return body
143
+ return f"<redacted {_payload_byte_size(body)} bytes>"
144
+
145
+
146
+ def redacted_error_params(params: Any) -> Any:
147
+ """Error ``params`` for the INFO-level 'Unique error received' log.
148
+
149
+ API validation errors commonly echo submitted values in ``params``, so
150
+ they are redacted unless the insecure opt-in is set.
151
+ """
152
+ if params is None or _insecure_log_payloads_enabled():
153
+ return params
154
+ return _REDACTED
155
+
156
+
59
157
  def logfmt(props):
60
158
  def fmt(key, val):
61
159
  # Handle case where val is a bytes or bytesarray
@@ -67,6 +67,29 @@ def _next_source_number(entries: list[dict[str, Any]]) -> int:
67
67
  return max(source_numbers, default=0) + 1
68
68
 
69
69
 
70
+ def _fetch_error_message(content: Any) -> str | None:
71
+ """Extract the failure message when ``content`` is a crawl-error payload.
72
+
73
+ The web-search backend reports per-URL fetch failures in-band: proxy
74
+ crawlers return ``"URL: <url>\\n\\nError: <message>"``, Tavily/Jina a
75
+ bare ``"Error: <message>"``. Returns the message for error payloads and
76
+ ``None`` for real page content.
77
+
78
+ Any leading ``URL: …`` line is skipped without matching it against the
79
+ result's own URL — redirects or normalization can make the reported URL
80
+ differ from the requested one.
81
+ """
82
+ if not isinstance(content, str):
83
+ return None
84
+ stripped = content.strip()
85
+ first_line, _, rest = stripped.partition("\n")
86
+ if first_line.startswith("URL:"):
87
+ stripped = rest.lstrip()
88
+ if stripped.startswith("Error:"):
89
+ return stripped[len("Error:") :].strip() or "unknown crawl error"
90
+ return None
91
+
92
+
70
93
  def _annotate_web_results_for_citations(
71
94
  payload: dict[str, Any],
72
95
  *,
@@ -78,6 +101,14 @@ def _annotate_web_results_for_citations(
78
101
  ``sourceNumber`` across consecutive ``search`` / ``crawl`` calls in
79
102
  the same turn, so the crawled-content row carries the same citation
80
103
  marker the search-snippet row already advertised.
104
+
105
+ Crawl-error payloads (``_fetch_error_message``) are kept out of the
106
+ manifest's ``content`` field and recorded under ``error`` instead: the
107
+ platform grounds the hallucination judge on manifest ``content``, so an
108
+ in-band fetch error stored as content turns a correctly cited,
109
+ snippet-verifiable source into a false "high hallucination" verdict
110
+ (UN-23356). The on-screen result still shows the error text so the
111
+ agent can react to the failed fetch.
81
112
  """
82
113
  refs_log_path = refs_log_path or (Path.cwd() / _WEB_REFS_LOG_RELATIVE_PATH)
83
114
  with _locked_turn_refs_manifest(
@@ -109,13 +140,19 @@ def _annotate_web_results_for_citations(
109
140
 
110
141
  result["sourceNumber"] = source_number
111
142
  result["citation"] = f"websource{source_number}"
143
+ content = result.get("content")
144
+ error = result.get("error")
145
+ fetch_error = _fetch_error_message(content)
146
+ if fetch_error is not None:
147
+ content = None
148
+ error = error or fetch_error
112
149
  manifest_entry = {
113
150
  "sourceNumber": source_number,
114
151
  "url": url,
115
152
  "title": result.get("title"),
116
153
  "snippet": result.get("snippet"),
117
- "content": result.get("content"),
118
- "error": result.get("error"),
154
+ "content": content,
155
+ "error": error,
119
156
  }
120
157
  _append_turn_refs_manifest_entry(refs_log_path, manifest_entry)
121
158
  annotated_results.append(result)