unique-sdk 2026.28.0.dev10__tar.gz → 2026.28.0.dev12__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.
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/PKG-INFO +1 -1
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/pyproject.toml +1 -1
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_message.py +5 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/_citation_manifest.py +63 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/mcp.py +107 -2
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/README.md +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/__init__.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_api_requestor.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_api_resource.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_api_version.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_error.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_http_client.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_list_object.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_object_classes.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_request_options.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_unique_object.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_unique_ql.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_unique_response.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_util.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_version.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/_webhook.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/__init__.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_acronyms.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_agentic_table.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_analytics_order.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_benchmarking.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_briefing.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_chat_completion.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_content.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_dynamic_frontend.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_elicitation.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_embedding.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_event.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_folder.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_group.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_integrated.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_llm_models.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_mcp.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_message_assessment.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_message_execution.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_message_log.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_message_tool.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_module.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_scheduled_task.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_search.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_search_string.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_short_term_memory.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_space.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_user.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_web_search.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/__init__.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/__main__.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/cli.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/__init__.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/cite_file.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/dynamic_frontend.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/elicitation.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/files.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/folders.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/navigation.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/read.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/search.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/subagent.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/web_search.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/web_search_config.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/config.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/formatting.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/metadata_filter.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/shell.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/skills/unique-cli-dynamic-frontend/SKILL.md +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/state.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/utils/analytics_order_run.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/utils/benchmarking_run.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/utils/chat_history.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/utils/chat_in_space.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/utils/file_io.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/utils/sources.py +0 -0
- {unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/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.
|
|
3
|
+
Version: 2026.28.0.dev12
|
|
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>
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_message.py
RENAMED
|
@@ -48,6 +48,11 @@ class Message(APIResource["Message"]):
|
|
|
48
48
|
debugInfo: dict[str, Any] | None
|
|
49
49
|
completedAt: datetime | None
|
|
50
50
|
correlation: NotRequired["Message.Correlation | None"]
|
|
51
|
+
segmentKind: NotRequired[
|
|
52
|
+
Literal["PROCESS", "PREFACE", "ELICITATION", "ANSWER"] | None
|
|
53
|
+
]
|
|
54
|
+
responseTurnId: NotRequired[str | None]
|
|
55
|
+
segmentIndex: NotRequired[int | None]
|
|
51
56
|
|
|
52
57
|
class ModifyParams(RequestOptions):
|
|
53
58
|
chatId: str
|
|
@@ -20,6 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import fcntl
|
|
21
21
|
import json
|
|
22
22
|
import os
|
|
23
|
+
import tempfile
|
|
23
24
|
from collections.abc import Generator
|
|
24
25
|
from contextlib import contextmanager
|
|
25
26
|
from pathlib import Path
|
|
@@ -30,6 +31,7 @@ __all__ = [
|
|
|
30
31
|
"_append_turn_refs_manifest_entry",
|
|
31
32
|
"_locked_turn_refs_manifest",
|
|
32
33
|
"_read_turn_refs_manifest",
|
|
34
|
+
"_rewrite_turn_refs_manifest",
|
|
33
35
|
]
|
|
34
36
|
|
|
35
37
|
|
|
@@ -135,6 +137,67 @@ def _append_turn_refs_manifest_entry(
|
|
|
135
137
|
) from exc
|
|
136
138
|
|
|
137
139
|
|
|
140
|
+
def _rewrite_turn_refs_manifest(
|
|
141
|
+
refs_log_path: Path,
|
|
142
|
+
entries: list[dict[str, Any]],
|
|
143
|
+
) -> None:
|
|
144
|
+
"""Atomically replace the manifest with ``entries`` (one JSON object per
|
|
145
|
+
line, in order).
|
|
146
|
+
|
|
147
|
+
Used to backfill a previously-appended entry in place — e.g. when a later
|
|
148
|
+
tool call re-cites a deduped item and extracts ``details`` the first call
|
|
149
|
+
lacked. Must be called while holding ``_locked_turn_refs_manifest`` so the
|
|
150
|
+
read → mutate → rewrite sequence does not race a concurrent append.
|
|
151
|
+
|
|
152
|
+
Crash-safe: the new content is written to a sibling temp file and then
|
|
153
|
+
``os.replace``-d over the manifest, so a failed/partial write never
|
|
154
|
+
truncates or corrupts the live manifest (the runner keeps reading the old
|
|
155
|
+
file until the rename succeeds). Same symlink/regular-file safety discipline
|
|
156
|
+
as the append path — rejects a symlinked dir/file, and the temp file is
|
|
157
|
+
created fresh with ``O_CREAT | O_EXCL | O_NOFOLLOW`` (via ``mkstemp``) so a
|
|
158
|
+
symlink swap between the check and the open still fails closed.
|
|
159
|
+
"""
|
|
160
|
+
_assert_safe_refs_log_path(refs_log_path)
|
|
161
|
+
try:
|
|
162
|
+
refs_log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
163
|
+
except OSError as exc:
|
|
164
|
+
raise UnsafeRefsLogPathError(
|
|
165
|
+
f"failed to create refs log directory: {refs_log_path.parent}"
|
|
166
|
+
) from exc
|
|
167
|
+
_assert_safe_refs_log_path(refs_log_path)
|
|
168
|
+
|
|
169
|
+
# Unique temp name per call in the same directory (so os.replace is an
|
|
170
|
+
# atomic same-filesystem rename). ``mkstemp`` generates a fresh random name
|
|
171
|
+
# and opens it with ``O_CREAT | O_EXCL | O_NOFOLLOW`` (POSIX) at mode 0600 —
|
|
172
|
+
# a leftover temp from an earlier crashed rewrite can never collide and
|
|
173
|
+
# block subsequent backfills.
|
|
174
|
+
try:
|
|
175
|
+
fd, tmp_name = tempfile.mkstemp(
|
|
176
|
+
dir=refs_log_path.parent,
|
|
177
|
+
prefix=f"{refs_log_path.name}.",
|
|
178
|
+
suffix=".tmp",
|
|
179
|
+
)
|
|
180
|
+
except OSError as exc:
|
|
181
|
+
raise UnsafeRefsLogPathError(
|
|
182
|
+
f"failed to open refs log temp file safely: {refs_log_path}"
|
|
183
|
+
) from exc
|
|
184
|
+
tmp_path = Path(tmp_name)
|
|
185
|
+
try:
|
|
186
|
+
with os.fdopen(fd, "w", encoding="utf-8") as fp:
|
|
187
|
+
for entry in entries:
|
|
188
|
+
fp.write(json.dumps(entry, default=str, ensure_ascii=False) + "\n")
|
|
189
|
+
os.replace(tmp_path, refs_log_path)
|
|
190
|
+
except OSError as exc:
|
|
191
|
+
# The live manifest is untouched; drop the partial temp file.
|
|
192
|
+
try:
|
|
193
|
+
os.unlink(tmp_path)
|
|
194
|
+
except OSError:
|
|
195
|
+
pass
|
|
196
|
+
raise UnsafeRefsLogPathError(
|
|
197
|
+
f"failed to write refs log: {refs_log_path}"
|
|
198
|
+
) from exc
|
|
199
|
+
|
|
200
|
+
|
|
138
201
|
@contextmanager
|
|
139
202
|
def _locked_turn_refs_manifest(
|
|
140
203
|
refs_log_path: Path,
|
|
@@ -14,6 +14,7 @@ from unique_sdk.cli.commands._citation_manifest import (
|
|
|
14
14
|
_append_turn_refs_manifest_entry,
|
|
15
15
|
_locked_turn_refs_manifest,
|
|
16
16
|
_read_turn_refs_manifest,
|
|
17
|
+
_rewrite_turn_refs_manifest,
|
|
17
18
|
)
|
|
18
19
|
from unique_sdk.cli.formatting import format_mcp_response
|
|
19
20
|
from unique_sdk.cli.state import ShellState
|
|
@@ -43,6 +44,41 @@ _MCP_MAX_ITEMS_PER_CALL = 8
|
|
|
43
44
|
# Keys an MCP tool's JSON result commonly uses for a record's human title.
|
|
44
45
|
_TITLE_KEYS = ("title", "name", "displayName", "subject", "summary", "key")
|
|
45
46
|
|
|
47
|
+
# Keys an MCP tool's JSON result commonly uses for the optional "details" line
|
|
48
|
+
# (UN-22310) — e.g. a date and an author such as "10/10/2026 - Jamie Dimon".
|
|
49
|
+
# Best-effort: only top-level record keys are inspected for these (nested
|
|
50
|
+
# provenance is too tool-specific to generalize). The values themselves may be
|
|
51
|
+
# nested one level — a date is read as a top-level string, while an author may
|
|
52
|
+
# be a plain string or an object whose name is pulled via `_AUTHOR_NAME_KEYS`
|
|
53
|
+
# (e.g. Atlassian's `{"displayName": "..."}`). The line is omitted when neither
|
|
54
|
+
# a date nor an author is found.
|
|
55
|
+
_DETAILS_DATE_KEYS = (
|
|
56
|
+
"date",
|
|
57
|
+
"updated",
|
|
58
|
+
"updatedAt",
|
|
59
|
+
"updatedDate",
|
|
60
|
+
"lastModified",
|
|
61
|
+
"modified",
|
|
62
|
+
"created",
|
|
63
|
+
"createdAt",
|
|
64
|
+
"createdDate",
|
|
65
|
+
"timestamp",
|
|
66
|
+
)
|
|
67
|
+
_DETAILS_AUTHOR_KEYS = (
|
|
68
|
+
"author",
|
|
69
|
+
"creator",
|
|
70
|
+
"owner",
|
|
71
|
+
"createdBy",
|
|
72
|
+
"updatedBy",
|
|
73
|
+
"by",
|
|
74
|
+
"sender",
|
|
75
|
+
"from",
|
|
76
|
+
)
|
|
77
|
+
# Keys to read a human name out of an author value that is itself an object
|
|
78
|
+
# (e.g. Atlassian's ``{"displayName": "Jamie Dimon"}``).
|
|
79
|
+
_AUTHOR_NAME_KEYS = ("displayName", "name", "fullName", "emailAddress", "email")
|
|
80
|
+
_MCP_DETAILS_CHAR_LIMIT = 200
|
|
81
|
+
|
|
46
82
|
# Canonical ``toolName`` written to both manifests: the **bare advertised tool
|
|
47
83
|
# name** (the payload ``name`` the agent passes to ``unique-cli mcp``).
|
|
48
84
|
# ``unique-cli mcp`` only runs in skills mode, where the agent invokes a tool by
|
|
@@ -83,6 +119,39 @@ def _title_from_json(obj: dict[str, Any]) -> str | None:
|
|
|
83
119
|
return None
|
|
84
120
|
|
|
85
121
|
|
|
122
|
+
def _first_str_by_keys(obj: dict[str, Any], keys: tuple[str, ...]) -> str | None:
|
|
123
|
+
for key in keys:
|
|
124
|
+
value = obj.get(key)
|
|
125
|
+
if isinstance(value, str) and value.strip():
|
|
126
|
+
return value.strip()
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _author_display(obj: dict[str, Any]) -> str | None:
|
|
131
|
+
"""Read an author name from a record, whether the author value is a plain
|
|
132
|
+
string or a nested object (e.g. ``{"displayName": "..."}``)."""
|
|
133
|
+
for key in _DETAILS_AUTHOR_KEYS:
|
|
134
|
+
value = obj.get(key)
|
|
135
|
+
if isinstance(value, str) and value.strip():
|
|
136
|
+
return value.strip()
|
|
137
|
+
if isinstance(value, dict):
|
|
138
|
+
name = _first_str_by_keys(value, _AUTHOR_NAME_KEYS)
|
|
139
|
+
if name:
|
|
140
|
+
return name
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _details_from_json(obj: dict[str, Any]) -> str | None:
|
|
145
|
+
"""Best-effort optional "details" line (UN-22310): a date and/or author
|
|
146
|
+
composed as "<date> - <author>". Returns ``None`` when neither is found."""
|
|
147
|
+
date = _first_str_by_keys(obj, _DETAILS_DATE_KEYS)
|
|
148
|
+
author = _author_display(obj)
|
|
149
|
+
parts = [part for part in (date, author) if part]
|
|
150
|
+
if not parts:
|
|
151
|
+
return None
|
|
152
|
+
return " - ".join(parts)[:_MCP_DETAILS_CHAR_LIMIT]
|
|
153
|
+
|
|
154
|
+
|
|
86
155
|
def _titles_from_json(text: str) -> list[dict[str, Any]]:
|
|
87
156
|
"""Best-effort: pull a human title out of a JSON result (object or list of
|
|
88
157
|
objects), e.g. an Atlassian page/issue returned as JSON-in-text. Returns []
|
|
@@ -98,7 +167,13 @@ def _titles_from_json(text: str) -> list[dict[str, Any]]:
|
|
|
98
167
|
if isinstance(entry, dict):
|
|
99
168
|
title = _title_from_json(entry)
|
|
100
169
|
if title:
|
|
101
|
-
items.append(
|
|
170
|
+
items.append(
|
|
171
|
+
{
|
|
172
|
+
"title": title,
|
|
173
|
+
"snippet": None,
|
|
174
|
+
"details": _details_from_json(entry),
|
|
175
|
+
}
|
|
176
|
+
)
|
|
102
177
|
return items
|
|
103
178
|
|
|
104
179
|
|
|
@@ -189,6 +264,12 @@ def _annotate_mcp_results_for_citations(
|
|
|
189
264
|
numbers_by_key[_item_dedup_key(stored_tool, entry)] = entry[
|
|
190
265
|
"sourceNumber"
|
|
191
266
|
]
|
|
267
|
+
entries_by_number = {
|
|
268
|
+
entry["sourceNumber"]: entry
|
|
269
|
+
for entry in entries
|
|
270
|
+
if isinstance(entry.get("sourceNumber"), int)
|
|
271
|
+
}
|
|
272
|
+
needs_rewrite = False
|
|
192
273
|
for item in items:
|
|
193
274
|
key = _item_dedup_key(tool_name, item)
|
|
194
275
|
source_number = numbers_by_key.get(key)
|
|
@@ -200,6 +281,7 @@ def _annotate_mcp_results_for_citations(
|
|
|
200
281
|
"serverName": server_name,
|
|
201
282
|
"title": item.get("title"),
|
|
202
283
|
"snippet": item.get("snippet"),
|
|
284
|
+
"details": item.get("details"),
|
|
203
285
|
}
|
|
204
286
|
try:
|
|
205
287
|
_append_turn_refs_manifest_entry(refs_log_path, manifest_entry)
|
|
@@ -210,7 +292,25 @@ def _annotate_mcp_results_for_citations(
|
|
|
210
292
|
break
|
|
211
293
|
numbers_by_key[key] = source_number
|
|
212
294
|
entries.append(manifest_entry)
|
|
295
|
+
entries_by_number[source_number] = manifest_entry
|
|
296
|
+
else:
|
|
297
|
+
# Deduped: a prior call already claimed this source number.
|
|
298
|
+
# Backfill ``details`` the first call may have lacked (the
|
|
299
|
+
# runner reads one entry per source number, so enriching it
|
|
300
|
+
# in place is sufficient) — only when newly extracted.
|
|
301
|
+
stored = entries_by_number.get(source_number)
|
|
302
|
+
new_details = item.get("details")
|
|
303
|
+
if stored is not None and new_details and not stored.get("details"):
|
|
304
|
+
stored["details"] = new_details
|
|
305
|
+
needs_rewrite = True
|
|
213
306
|
annotated.append((source_number, item))
|
|
307
|
+
if needs_rewrite:
|
|
308
|
+
try:
|
|
309
|
+
_rewrite_turn_refs_manifest(refs_log_path, entries)
|
|
310
|
+
except (UnsafeRefsLogPathError, OSError) as exc:
|
|
311
|
+
_LOGGER.warning(
|
|
312
|
+
"mcp: failed to backfill refs manifest details: %s", exc
|
|
313
|
+
)
|
|
214
314
|
except (UnsafeRefsLogPathError, OSError) as exc:
|
|
215
315
|
_LOGGER.warning("mcp: failed to append refs manifest: %s", exc)
|
|
216
316
|
return []
|
|
@@ -224,7 +324,12 @@ def _citation_footer(annotated: list[tuple[int, dict[str, Any]]]) -> str:
|
|
|
224
324
|
"""Tell the agent which marker to cite each retrieved item with."""
|
|
225
325
|
if not annotated:
|
|
226
326
|
return ""
|
|
227
|
-
lines = [
|
|
327
|
+
lines = [
|
|
328
|
+
"",
|
|
329
|
+
"Sources — MANDATORY: every fact you take from this result MUST be "
|
|
330
|
+
"cited inline with its [mcpsourceN] marker below, or it will not be "
|
|
331
|
+
"referenced in the answer:",
|
|
332
|
+
]
|
|
228
333
|
for source_number, item in annotated:
|
|
229
334
|
label = item.get("title") or "this MCP tool result"
|
|
230
335
|
lines.append(f" [mcpsource{source_number}] {label}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/__init__.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_acronyms.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_agentic_table.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_benchmarking.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_briefing.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_content.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_elicitation.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_embedding.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_event.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_folder.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_group.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_integrated.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_llm_models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_message_log.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_message_tool.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_module.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_search.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_search_string.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_space.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/api_resources/_web_search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/__init__.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/cite_file.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/elicitation.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/folders.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/navigation.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/scheduled_tasks.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/subagent.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/cli/commands/web_search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/utils/analytics_order_run.py
RENAMED
|
File without changes
|
{unique_sdk-2026.28.0.dev10 → unique_sdk-2026.28.0.dev12}/unique_sdk/utils/benchmarking_run.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|