fix(ui): stabilize company chat result topology

This commit is contained in:
LZH-YS1998
2026-07-14 20:31:10 +08:00
parent 0a6246aff9
commit 5938b4c215
25 changed files with 1917 additions and 381 deletions
+65 -2
View File
@@ -144,6 +144,7 @@ from opc.layer2_organization.work_item_identity import (
projection_id_for_task,
projection_id_for_work_item,
rework_projection_id_for_gate,
result_delivery_identity_payload_for_task,
turn_type_for_task,
turn_type_for_work_item,
work_item_identity_payload,
@@ -9076,6 +9077,48 @@ class OPCEngine:
in {"suspending", "suspended"}
)
@staticmethod
def _ensure_result_delivery_identity_for_commit(
task: Task,
result: TaskResult,
) -> dict[str, str]:
"""Reuse the runtime's immutable delivery id or mint one once.
A runtime-generated canonical turn is not guaranteed to be copied back
into mutable Task metadata. The TaskResult artifact is therefore the
hand-off boundary between runtime persistence and the engine mirrors.
External/pause results without a canonical turn receive a per-result
execution seed here. Runtime/provider *session* ids are deliberately
excluded because those sessions survive resume and can produce several
legitimate deliveries for the same task and retry count.
The generated identity is written back to ``TaskResult.artifacts``
before the task result is persisted. Replaying that durable result is
therefore stable instead of minting a second UI row after a restart.
"""
artifacts = dict(result.artifacts or {})
canonical_turn_id = str(
artifacts.get("canonical_turn_id")
or artifacts.get("conversation_turn_id")
or ""
).strip()
persisted_delivery_id = str(artifacts.get("result_delivery_id") or "").strip()
execution_id = str(artifacts.get("result_execution_id") or "").strip()
if not persisted_delivery_id and not canonical_turn_id and not execution_id:
execution_id = uuid.uuid4().hex
identity = result_delivery_identity_payload_for_task(
task,
canonical_turn_id=canonical_turn_id,
execution_id=execution_id,
result_delivery_id=persisted_delivery_id,
)
result.artifacts = {
**artifacts,
**({"result_execution_id": execution_id} if execution_id else {}),
**identity,
}
return identity
async def _execute_registered_task_attempt(self, task: Task) -> TaskResult:
try:
result = await self._run_task_once(task)
@@ -9115,6 +9158,7 @@ class OPCEngine:
"company runtime was suspended before result commit"
)
self._apply_runtime_state_to_task(task, result)
result_identity = self._ensure_result_delivery_identity_for_commit(task, result)
task.status = result.status
task.result = {"content": result.content, "artifacts": result.artifacts}
await self.store.save_task(task)
@@ -9131,6 +9175,8 @@ class OPCEngine:
"status": result.status.value,
"employee_id": str(assignment.get("employee_id", "")).strip(),
"role_id": str(assignment.get("role_id") or task.assigned_to or "").strip(),
"child_session_id": str(task.session_id),
**result_identity,
**work_item_identity_payload_for_task(task),
},
)
@@ -9141,7 +9187,7 @@ class OPCEngine:
and task.session_id != task.parent_session_id
):
assignment = dict(task.metadata.get("employee_assignment", {}) or {})
await self.memory.record_assistant_turn(
child_result_message = await self.memory.record_assistant_turn(
session_id=task.session_id,
content=result.content,
project_id=task.project_id,
@@ -9152,6 +9198,8 @@ class OPCEngine:
"status": result.status.value,
"employee_id": str(assignment.get("employee_id", "")).strip(),
"role_id": str(assignment.get("role_id") or task.assigned_to or "").strip(),
"child_session_id": str(task.session_id),
**result_identity,
**work_item_identity_payload_for_task(task),
},
)
@@ -9161,6 +9209,11 @@ class OPCEngine:
task=task,
result_content=result.content,
artifacts=result.artifacts,
result_delivery_id=result_identity.get("result_delivery_id", ""),
source_result_message_id=str(
getattr(child_result_message, "message_id", "") or ""
),
canonical_turn_id=result_identity.get("canonical_turn_id", ""),
)
if result.status == TaskStatus.DONE:
await self._record_task_mode_external_result_reply(task, result.content)
@@ -9197,6 +9250,7 @@ class OPCEngine:
"company runtime was suspended before retry result commit"
)
self._apply_runtime_state_to_task(task, result)
result_identity = self._ensure_result_delivery_identity_for_commit(task, result)
task.status = result.status
task.result = {"content": result.content, "artifacts": result.artifacts}
await self.store.save_task(task)
@@ -9214,6 +9268,8 @@ class OPCEngine:
"retry_count": task.retry_count,
"employee_id": str(assignment.get("employee_id", "")).strip(),
"role_id": str(assignment.get("role_id") or task.assigned_to or "").strip(),
"child_session_id": str(task.session_id),
**result_identity,
**work_item_identity_payload_for_task(task),
},
)
@@ -9224,7 +9280,7 @@ class OPCEngine:
and task.session_id != task.parent_session_id
):
assignment = dict(task.metadata.get("employee_assignment", {}) or {})
await self.memory.record_assistant_turn(
child_result_message = await self.memory.record_assistant_turn(
session_id=task.session_id,
content=result.content,
project_id=task.project_id,
@@ -9236,6 +9292,8 @@ class OPCEngine:
"retry_count": task.retry_count,
"employee_id": str(assignment.get("employee_id", "")).strip(),
"role_id": str(assignment.get("role_id") or task.assigned_to or "").strip(),
"child_session_id": str(task.session_id),
**result_identity,
**work_item_identity_payload_for_task(task),
},
)
@@ -9245,6 +9303,11 @@ class OPCEngine:
task=task,
result_content=result.content,
artifacts=result.artifacts,
result_delivery_id=result_identity.get("result_delivery_id", ""),
source_result_message_id=str(
getattr(child_result_message, "message_id", "") or ""
),
canonical_turn_id=result_identity.get("canonical_turn_id", ""),
)
if result.status == TaskStatus.DONE:
await self._record_task_mode_external_result_reply(task, result.content)
@@ -7,6 +7,8 @@ from typing import Any, Mapping
WORK_ITEM_PROJECTION_ID_KEY = "work_item_projection_id"
WORK_ITEM_TURN_TYPE_KEY = "work_item_turn_type"
RESULT_DELIVERY_ID_KEY = "result_delivery_id"
SOURCE_TASK_ID_KEY = "source_task_id"
GATE_REWORK_PROJECTION_ID_KEY = "rework_projection_id"
GATE_TARGET_PROJECTION_ID_KEY = "target_projection_id"
GATE_TARGET_PROJECTION_IDS_KEY = "target_projection_ids"
@@ -259,6 +261,94 @@ def work_item_identity_payload_for_task(
)
def canonical_result_turn_id_for_task(
task: Any,
*,
canonical_turn_id: str = "",
) -> str:
"""Return the canonical conversation turn owning a task result.
Runtime events also carry iteration-scoped ``turn_id`` values. Result
surfaces must never use those values as their logical delivery identity,
so this helper only falls back to the task's canonical runtime fields.
"""
explicit = _clean(canonical_turn_id)
if explicit:
return explicit
metadata = dict(getattr(task, "metadata", {}) or {}) if task is not None else {}
runtime_metadata = dict(metadata.get("runtime_v2", {}) or {})
for source in (metadata, runtime_metadata):
for key in (
"canonical_turn_id",
"conversation_turn_id",
"current_turn_id",
"runtime_v2_current_turn_id",
):
value = _clean(source.get(key))
if value:
return value
return ""
def result_delivery_id_for_task(
task: Any,
*,
canonical_turn_id: str = "",
execution_id: str = "",
result_delivery_id: str = "",
) -> str:
"""Build one stable identity shared by every surface of a task result.
The attempt suffix keeps a failed result and its retry distinct while the
canonical turn (when available) links the runtime final, child result and
parent mirror without inspecting their display text.
"""
explicit = _clean(result_delivery_id)
if explicit:
return explicit
task_id = _clean(getattr(task, "id", ""))
turn_id = canonical_result_turn_id_for_task(
task,
canonical_turn_id=canonical_turn_id,
)
execution = _clean(execution_id)
if not task_id or (not turn_id and not execution):
return ""
try:
attempt = max(0, int(getattr(task, "retry_count", 0) or 0))
except (TypeError, ValueError):
attempt = 0
identity_kind = "turn" if turn_id else "execution"
identity_value = turn_id or execution
return f"result:task:{task_id}:{identity_kind}:{identity_value}:attempt:{attempt}"
def result_delivery_identity_payload_for_task(
task: Any,
*,
canonical_turn_id: str = "",
execution_id: str = "",
result_delivery_id: str = "",
) -> dict[str, str]:
"""Return structured lineage shared by persisted result projections."""
delivery_id = result_delivery_id_for_task(
task,
canonical_turn_id=canonical_turn_id,
execution_id=execution_id,
result_delivery_id=result_delivery_id,
)
source_task_id = _clean(getattr(task, "id", ""))
canonical_id = canonical_result_turn_id_for_task(
task,
canonical_turn_id=canonical_turn_id,
)
return {
**({RESULT_DELIVERY_ID_KEY: delivery_id} if delivery_id else {}),
**({SOURCE_TASK_ID_KEY: source_task_id} if source_task_id else {}),
**({"canonical_turn_id": canonical_id} if canonical_id else {}),
}
def migrate_work_item_projection_metadata(
metadata: Mapping[str, Any] | None,
*,
+23
View File
@@ -19,6 +19,7 @@ from opc.core.models import OPCEvent, PermissionResolution, Task, TaskResult, Ta
from opc.layer2_organization.collaboration_policy import ownership_guard_violation
from opc.layer2_organization.work_item_identity import (
projection_id_for_task,
result_delivery_identity_payload_for_task,
turn_type_for_task,
work_item_identity_payload_for_task,
)
@@ -703,6 +704,14 @@ class NativeRuntimeV2:
runtime_notes=runtime_notes,
)
if verification_gate is not None:
verification_gate.artifacts = {
**dict(verification_gate.artifacts or {}),
"runtime_session_id": runtime_session_id,
**result_delivery_identity_payload_for_task(
task,
canonical_turn_id=conversation_turn_id,
),
}
await self._save_runtime_session(
runtime_session_id,
task,
@@ -728,6 +737,10 @@ class NativeRuntimeV2:
artifacts = {
**aggregated_artifacts,
"runtime_session_id": runtime_session_id,
**result_delivery_identity_payload_for_task(
task,
canonical_turn_id=conversation_turn_id,
),
"permission_requests": self._permission_requests_from_results([]),
"active_subagents": active_subagents,
"compaction_boundaries": list(compaction_boundaries),
@@ -2799,6 +2812,16 @@ class NativeRuntimeV2:
"runtime_session_id": runtime_session_id,
"source_kind": source_kind,
}
if not tool_calls:
metadata.update(
result_delivery_identity_payload_for_task(
task,
canonical_turn_id=canonical_turn_id,
)
)
metadata.update(work_item_identity_payload_for_task(task))
if task.session_id:
metadata["child_session_id"] = str(task.session_id)
if is_company_mode:
metadata["execution_mode"] = "company_mode"
metadata["company_runtime_raw_turn"] = True
+9
View File
@@ -713,6 +713,9 @@ class MemoryManager:
task: Any,
result_content: str,
artifacts: dict[str, Any] | None = None,
result_delivery_id: str = "",
source_result_message_id: str = "",
canonical_turn_id: str = "",
) -> None:
if not self.store:
return
@@ -728,9 +731,13 @@ class MemoryManager:
metadata={
"kind": "child_result",
"child_session_id": child_session_id,
"source_task_id": str(getattr(task, "id", "") or ""),
"task_title": getattr(task, "title", ""),
"employee_id": str(assignment.get("employee_id", "")).strip(),
"role_id": str(assignment.get("role_id") or getattr(task, "assigned_to", "") or "").strip(),
**({"result_delivery_id": str(result_delivery_id).strip()} if str(result_delivery_id).strip() else {}),
**({"source_result_message_id": str(source_result_message_id).strip()} if str(source_result_message_id).strip() else {}),
**({"canonical_turn_id": str(canonical_turn_id).strip()} if str(canonical_turn_id).strip() else {}),
**work_item_identity_payload_for_task(task),
},
)
@@ -747,6 +754,8 @@ class MemoryManager:
"agent_id": getattr(task, "assigned_to", ""),
"summary": summary,
"artifacts": self._compact_artifacts(artifacts or {}),
**({"result_delivery_id": str(result_delivery_id).strip()} if str(result_delivery_id).strip() else {}),
**({"source_result_message_id": str(source_result_message_id).strip()} if str(source_result_message_id).strip() else {}),
},
)
+171 -84
View File
@@ -575,23 +575,43 @@ class ChatStore:
@staticmethod
def _strip_narrative_title_prefix(content: str) -> str:
trimmed = str(content or "").strip()
markdown_title = re.match(r"^\*\*(.{8,160}?)\*\*:\s+([\s\S]+)$", trimmed)
if markdown_title:
# Only discard an explicit, anchored narrative wrapper. The former
# fallback used the first ``": "`` anywhere in the first 160
# characters, so ordinary Markdown such as ``**Work Item 1: ...**``
# was progressively truncated every time a sync was merged. Peeling
# explicit nested wrappers to a fixed point keeps this comparison
# helper idempotent without interpreting body punctuation as structure.
while True:
markdown_title = re.match(r"^\*\*(.{8,160}?)\*\*:\s+([\s\S]+)$", trimmed)
if not markdown_title:
return trimmed
body = markdown_title.group(2).strip()
if len(body) >= 80:
return body
colon_index = trimmed.find(": ")
if colon_index < 8 or colon_index > 160:
return trimmed
prefix = trimmed[:colon_index].replace("*", "").strip()
body = trimmed[colon_index + 2 :].strip()
if len(body) < 80:
return trimmed
if not re.search(r"[A-Za-z\u4e00-\u9fff]", prefix):
return trimmed
if re.match(r"^(https?|file)$", prefix, flags=re.IGNORECASE):
return trimmed
return body
if len(body) < 80 or body == trimmed:
return trimmed
trimmed = body
@classmethod
def _select_duplicate_display_content(
cls,
preferred: dict[str, Any],
secondary: dict[str, Any],
) -> str:
"""Choose an exact input body; never persist the comparison key itself."""
preferred_content = str(preferred.get("content", "") or "")
secondary_content = str(secondary.get("content", "") or "")
preferred_key = cls._normalize_duplicate_content(preferred_content)
if not preferred_key or preferred_key != cls._normalize_duplicate_content(secondary_content):
return preferred_content
# If one real surface is already the comparison body while the other
# carries an explicit narrative wrapper/footer, reuse that real body.
# This preserves the historical visible answer without manufacturing
# display text from a lossy canonicalization function.
if (
secondary_content.strip() == preferred_key
and preferred_content.strip() != preferred_key
):
return secondary_content
return preferred_content
@staticmethod
def _message_timestamp(message: dict[str, Any]) -> float:
@@ -663,6 +683,9 @@ class ChatStore:
normalized = str(value or "").strip()
if normalized:
keys.add(normalized)
result_delivery_id = str(metadata.get("result_delivery_id", "") or "").strip()
if result_delivery_id:
keys.add(f"result_delivery:{result_delivery_id}")
return keys
@classmethod
@@ -681,10 +704,15 @@ class ChatStore:
cls,
existing: dict[str, Any],
candidate: dict[str, Any],
*,
prefer_candidate: bool = False,
) -> dict[str, Any]:
preferred = existing
secondary = candidate
if cls._message_preference_score(candidate) > cls._message_preference_score(existing):
if (
prefer_candidate
or cls._message_preference_score(candidate) > cls._message_preference_score(existing)
):
preferred = candidate
secondary = existing
@@ -694,12 +722,7 @@ class ChatStore:
secondary_meta = dict(secondary.get("metadata", {}) or {})
preferred_meta = dict(preferred.get("metadata", {}) or {})
merged["metadata"] = {**secondary_meta, **preferred_meta}
normalized_content = cls._normalize_duplicate_content(preferred.get("content", ""))
if (
normalized_content
and normalized_content == cls._normalize_duplicate_content(secondary.get("content", ""))
):
merged["content"] = normalized_content
merged["content"] = cls._select_duplicate_display_content(preferred, secondary)
shared_ids = cls._message_identity_keys(existing) & cls._message_identity_keys(candidate)
canonical_id = ""
@@ -742,7 +765,10 @@ class ChatStore:
return (
str(existing.get("sender", "") or "") == str(candidate.get("sender", "") or "")
and str(existing.get("sender_name", "") or "") == str(candidate.get("sender_name", "") or "")
and cls._normalize_duplicate_content(existing.get("content", "")) == cls._normalize_duplicate_content(candidate.get("content", ""))
# Display content is persisted data, not a duplicate-comparison
# key. Exact comparison lets authoritative transcript backfill
# repair a legacy row whose body was destructively normalized.
and str(existing.get("content", "") or "") == str(candidate.get("content", "") or "")
and cls._message_timestamp(existing) == cls._message_timestamp(candidate)
and str(existing.get("reply_to_id", "") or "") == str(candidate.get("reply_to_id", "") or "")
and list(existing.get("mentions", []) or []) == list(candidate.get("mentions", []) or [])
@@ -791,6 +817,55 @@ class ChatStore:
return None
return await self._message_scope(str(message_id).strip())
async def _persist_merged_message(
self,
existing: dict[str, Any],
merged: dict[str, Any],
*,
channel_id: str,
project_id: str,
preserve_timestamp: bool = False,
) -> tuple[dict[str, Any], bool]:
"""Persist a duplicate merge while retaining the mounted cache row.
Semantic transcript duplicates can have a different source message id.
Keeping the existing cache identity (and, for semantic replacement, its
timestamp) upgrades the row in place instead of moving it in the UI.
"""
persisted_id = str(existing.get("message_id", "") or existing.get("id", "") or "")
existing_timestamp = self._message_timestamp(existing)
merged_timestamp = (
existing_timestamp
if preserve_timestamp and existing_timestamp
else self._message_timestamp(merged) or time.time()
)
persisted = {
**merged,
"message_id": persisted_id,
"channel_id": channel_id,
"timestamp": merged_timestamp,
"created_at": merged_timestamp,
}
if self._message_persisted_equal(existing, persisted):
return persisted, False
await self._db.execute(
"UPDATE messages SET sender = ?, sender_name = ?, content = ?, timestamp = ?, "
"reply_to_id = ?, mentions = ?, metadata = ? WHERE message_id = ? AND channel_id = ? AND project_id = ?",
(
persisted["sender"],
persisted["sender_name"],
persisted["content"],
merged_timestamp,
persisted.get("reply_to_id"),
json.dumps(persisted.get("mentions", [])),
json.dumps(persisted.get("metadata", {})),
persisted_id,
channel_id,
project_id,
),
)
return persisted, True
async def _merge_into_same_scope_row(
self,
message_id: str,
@@ -798,13 +873,14 @@ class ChatStore:
channel_id: str,
project_id: str,
candidate: dict[str, Any],
) -> dict[str, Any] | None:
prefer_candidate: bool = False,
) -> tuple[dict[str, Any], bool] | None:
"""Merge ``candidate`` into an already-persisted row with the same id/scope.
Returns the merged row when the update happened (or nothing changed), or
None when the row could not be loaded. Backfill and the live insert path
can race on the same message id; the duplicate must merge in place, never
be re-inserted under a scoped alias id in the same channel.
Returns ``(merged row, materially changed)`` or ``None`` when the row
could not be loaded. Backfill and the live insert path can race on the
same message id; the duplicate must merge in place, never be re-inserted
under a scoped alias id in the same channel.
"""
cursor = await self._db.execute(
"SELECT message_id, channel_id, sender, sender_name, content, "
@@ -816,29 +892,17 @@ class ChatStore:
if row is None:
return None
existing = self._row_to_message_dict(row)
merged = self._merge_duplicate_messages(existing, candidate)
if self._message_persisted_equal(existing, merged):
return merged
merged_timestamp = self._message_timestamp(merged) or time.time()
await self._db.execute(
"UPDATE messages SET sender = ?, sender_name = ?, content = ?, timestamp = ?, "
"reply_to_id = ?, mentions = ?, metadata = ? WHERE message_id = ? AND channel_id = ? AND project_id = ?",
(
merged["sender"],
merged["sender_name"],
merged["content"],
merged_timestamp,
merged.get("reply_to_id"),
json.dumps(merged.get("mentions", [])),
json.dumps(merged.get("metadata", {})),
message_id,
channel_id,
project_id,
),
merged = self._merge_duplicate_messages(
existing,
candidate,
prefer_candidate=prefer_candidate,
)
return await self._persist_merged_message(
existing,
merged,
channel_id=channel_id,
project_id=project_id,
)
merged["timestamp"] = merged_timestamp
merged["created_at"] = merged_timestamp
return merged
async def _allocate_scoped_message_id(
self,
@@ -1581,35 +1645,24 @@ class ChatStore:
existing_index = existing_positions.get(mid)
if existing_index is not None:
existing_match = existing_messages[existing_index]
merged_existing = self._merge_duplicate_messages(existing_match, normalized_message)
if not self._message_persisted_equal(existing_match, merged_existing):
merged_timestamp = self._message_timestamp(merged_existing) or time.time()
await self._db.execute(
"UPDATE messages SET sender = ?, sender_name = ?, content = ?, timestamp = ?, "
"reply_to_id = ?, mentions = ?, metadata = ? WHERE message_id = ? AND channel_id = ? AND project_id = ?",
(
merged_existing["sender"],
merged_existing["sender_name"],
merged_existing["content"],
merged_timestamp,
merged_existing.get("reply_to_id"),
json.dumps(merged_existing.get("mentions", [])),
json.dumps(merged_existing.get("metadata", {})),
mid,
channel_id,
project_id,
),
)
semantic_index.replace(existing_index, {
**merged_existing,
"created_at": merged_timestamp,
})
inserted_messages.append({
**merged_existing,
"channel_id": channel_id,
"timestamp": merged_timestamp,
"created_at": merged_timestamp,
})
# This candidate came directly from the durable transcript.
# For the same identity its original display body is
# authoritative, while metadata unique to the cache is
# still retained by the merge.
merged_existing = self._merge_duplicate_messages(
existing_match,
normalized_message,
prefer_candidate=True,
)
persisted, did_change = await self._persist_merged_message(
existing_match,
merged_existing,
channel_id=channel_id,
project_id=project_id,
)
if did_change:
semantic_index.replace(existing_index, persisted)
inserted_messages.append(persisted)
changed_existing = True
continue
@@ -1623,10 +1676,15 @@ class ChatStore:
channel_id=channel_id,
project_id=project_id,
candidate=normalized_message,
prefer_candidate=True,
)
if merged is not None:
persisted, did_change = merged
existing_ids.add(mid)
existing_positions[mid] = semantic_index.append(merged)
existing_positions[mid] = semantic_index.append(persisted)
if did_change:
inserted_messages.append(persisted)
changed_existing = True
continue
if existing_scope and existing_scope != (channel_id, project_id):
metadata = dict(normalized_message.get("metadata", {}) or {})
@@ -1644,9 +1702,33 @@ class ChatStore:
excluded_message_ids=consumed_existing_ids,
)
if duplicate_index is not None:
consumed_existing_ids.add(
existing_messages[duplicate_index]["message_id"]
existing_duplicate = existing_messages[duplicate_index]
existing_id = str(existing_duplicate["message_id"])
# Prefer the authoritative transcript on an equal score, but
# do not replace a stronger canonical result surface with a
# lower-priority mirror. The existing cache id and timestamp
# remain stable either way.
prefer_candidate = (
self._message_preference_score(normalized_message)
>= self._message_preference_score(existing_duplicate)
)
merged_duplicate = self._merge_duplicate_messages(
existing_duplicate,
normalized_message,
prefer_candidate=prefer_candidate,
)
persisted, did_change = await self._persist_merged_message(
existing_duplicate,
merged_duplicate,
channel_id=channel_id,
project_id=project_id,
preserve_timestamp=True,
)
if did_change:
semantic_index.replace(duplicate_index, persisted)
inserted_messages.append(persisted)
changed_existing = True
consumed_existing_ids.add(existing_id)
continue
try:
@@ -1674,11 +1756,16 @@ class ChatStore:
channel_id=channel_id,
project_id=project_id,
candidate=normalized_message,
prefer_candidate=True,
)
if merged is not None:
persisted, did_change = merged
merged_id = normalized_message["message_id"]
existing_ids.add(merged_id)
existing_positions[merged_id] = semantic_index.append(merged)
existing_positions[merged_id] = semantic_index.append(persisted)
if did_change:
inserted_messages.append(persisted)
changed_existing = True
continue
metadata = dict(normalized_message.get("metadata", {}) or {})
metadata.setdefault("ui_message_id", normalized_message["message_id"])
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="data:," />
<title>OpenOPC Pixel Office</title>
<script type="module" crossorigin src="./assets/index-Cson66Y4.js"></script>
<script type="module" crossorigin src="./assets/index-CTK-c7_K.js"></script>
<link rel="modulepreload" crossorigin href="./assets/phaser-DFK5Ua9d.js">
<link rel="stylesheet" crossorigin href="./assets/index-DEvLDWDw.css">
</head>
@@ -127,16 +127,37 @@ assert.match(
'runtime events carrying a non-empty tool_name must update stable displayTool (empty tool_name keeps the sticky last command)',
)
// 8. Assistant streaming drafts must disappear at real terminal boundaries.
// 8. A streaming draft is one mounted logical turn. Completion/checkpoint
// events cannot remove it before the matching persisted final is merged.
assert.match(
src,
/evt\.type === 'turn_completed' \|\| evt\.type === 'turn_failed' \|\| evt\.type === 'checkpoint_saved'/,
'runtime terminal/checkpoint events must clear task-mode Live Reply drafts',
/const turnId = resolveCanonicalTurnId\(data\) \|\| undefined/,
'runtime deltas must use the shared canonical-turn resolver',
)
assert.match(
src,
/detailHasFinalForDraft[\s\S]*runtime_v2_assistant[\s\S]*ss\.clearDraft\(detailTaskId\)/,
'session_detail backfill of the final runtime assistant turn must clear matching Live Reply drafts',
/const startsNewCanonicalTurn = evt\.type === 'turn_started'[\s\S]*turnId !== activeDraftTurnId[\s\S]*evt\.type === 'turn_failed' \|\| startsNewCanonicalTurn/,
'only failure or a genuinely new canonical turn may clear an uncommitted draft',
)
assert.doesNotMatch(
src,
/evt\.type === 'turn_completed'[^\n]*clearDraft|evt\.type === 'checkpoint_saved'[^\n]*clearDraft/,
'turn completion and checkpoint persistence must not clear a draft ahead of its final message',
)
assert.match(
src,
/detailHasFinalForDraft[\s\S]*terminalAssistantTurnId\(message\) === draftTurnId[\s\S]*ss\.clearDraft\(detailTaskId\)/,
'session_detail may clear only after merging the matching terminal assistant turn',
)
assert.match(
src,
/cs\.addMessageFromBackend\(mapped\)[\s\S]*terminalTurnId = terminalAssistantTurnId\(mapped\)[\s\S]*terminalTurnId === activeDraftTurnId[\s\S]*clearDraft\(taskId\)/,
'session_message/chat_new_message must merge a matching terminal final before clearing its draft',
)
assert.doesNotMatch(
src,
/mapped\.sender !== 'user'\)\s*\{\s*sessionStoreRef\.current\?\.clearDraft/,
'an unrelated non-user session message must never clear an active draft',
)
// 9. Summary/full pagination and transport-local failures have independent
+27 -19
View File
@@ -24,6 +24,7 @@ import { companyRuntimeControlPatchForBoardStatus } from './lib/sessionRuntime'
import { getExecutionTurnId } from './lib/workItemRuntimeIds'
import { normalizeSessionCompanyProfile, normalizeSessionExecMode } from './lib/sessionIdentity'
import { extractSessionRecruitmentByRole, sessionChannelId } from './lib/sessionRecruitment'
import { resolveCanonicalTurnId, terminalAssistantTurnId } from './lib/turnIdentity'
import { unassignAgent } from './game/map/OfficeStore'
import type { AgentAnimStatus, EmployeeAssignment, KanbanPhase, KanbanTask, RoleAggregatedStatus, RoleWorkItemSummary, Session, TaskPreferredAgent } from './types/kanban'
@@ -919,9 +920,11 @@ export default function App() {
const taskId = typeof data.task_id === 'string' ? data.task_id : ''
if (taskId) {
const isDeltaEvent = evt.type === 'assistant_delta' || evt.type === 'thinking_delta'
const bufferedDraftTurnId = pendingDeltaFlushRef.current.get(taskId)?.draftTurnId
// Store-write ordering guarantee: everything buffered for this
// task lands before a non-delta event (turn boundaries call
// clearDraft; the draft must be flushed first, not after).
// task lands before a non-delta event. A matching terminal
// message replaces the draft later; flushing here must not make
// a completion boundary remove the visible turn prematurely.
if (!isDeltaEvent) flushPendingDeltas(taskId)
const ss = sessionStoreRef.current
const bs = boardStoreRef.current
@@ -931,13 +934,10 @@ export default function App() {
: ''
const executionMode = typeof data.execution_mode === 'string' ? data.execution_mode : ''
const isTaskModeRuntime = executionMode === 'task_mode' || projectionId === 'task_mode_execution'
const turnId = typeof data.turn_id === 'string' && data.turn_id
? data.turn_id
: typeof data.canonical_turn_id === 'string' && data.canonical_turn_id
? data.canonical_turn_id
: typeof data.execution_turn_id === 'string' && data.execution_turn_id
? data.execution_turn_id
: undefined
// Drafts represent one logical assistant turn. The shared
// resolver deliberately prefers conversation identity over the
// iteration-scoped runtime turn id.
const turnId = resolveCanonicalTurnId(data) || undefined
const marksCompanyRuntime =
!!projectionId && projectionId !== 'task_mode_execution' && !isTaskModeRuntime
if (marksCompanyRuntime && !isDeltaEvent && existingSession?.isCompanyRuntime !== true) {
@@ -993,7 +993,14 @@ export default function App() {
entry.kanbanPatch = { ...entry.kanbanPatch, ...kanbanPatch }
scheduleDeltaFlush()
} else {
if (evt.type === 'turn_started' || evt.type === 'turn_completed' || evt.type === 'turn_failed' || evt.type === 'checkpoint_saved') {
const activeDraftTurnId = String(
bufferedDraftTurnId ?? existingSession?.draftTurnId ?? '',
).trim()
const startsNewCanonicalTurn = evt.type === 'turn_started'
&& !!turnId
&& !!activeDraftTurnId
&& turnId !== activeDraftTurnId
if (evt.type === 'turn_failed' || startsNewCanonicalTurn) {
ss?.clearDraft(taskId)
}
ss?.updateSession(taskId, runtimePartial)
@@ -1084,14 +1091,9 @@ export default function App() {
payload.client_history_page === true,
)
const draftTurnId = String(existingSession?.draftTurnId ?? '').trim()
const detailHasFinalForDraft = !!draftTurnId && detailMessages.some((message) => {
if (message.sender === 'user') return false
const metadata = message.metadata ?? {}
const transcriptKind = String(metadata.transcript_kind ?? metadata.kind ?? '').trim()
if (transcriptKind !== 'runtime_v2_assistant') return false
const messageTurnId = String(metadata.canonical_turn_id ?? metadata.turn_id ?? '').trim()
return messageTurnId === draftTurnId
})
const detailHasFinalForDraft = !!draftTurnId
&& !!cs
&& detailMessages.some(message => terminalAssistantTurnId(message) === draftTurnId)
if (detailHasFinalForDraft) {
ss.clearDraft(detailTaskId)
}
@@ -1672,8 +1674,14 @@ export default function App() {
console.debug('[onSessionMessage]', mapped.sender, mapped.channelId, mapped.content?.slice(0, 60))
cs.addMessageFromBackend(mapped)
const taskId = mapped.channelId.startsWith('session:') ? mapped.channelId.slice('session:'.length) : ''
const terminalTurnId = terminalAssistantTurnId(mapped)
const activeDraftTurnId = String(
sessionStoreRef.current?.sessions.find(session => session.taskId === taskId)?.draftTurnId ?? '',
).trim()
if (taskId && mapped.sender !== 'user') {
sessionStoreRef.current?.clearDraft(taskId)
if (terminalTurnId && terminalTurnId === activeDraftTurnId) {
sessionStoreRef.current?.clearDraft(taskId)
}
// Force refresh — session messages are critical content that must sync
scheduleSessionDetailRefresh(taskId, 'full', true)
}
@@ -241,6 +241,153 @@ assert.equal(repeatedNativeCompanySync.length, 1)
assert.equal(repeatedNativeCompanySync[0].metadata?.ui_timeline_id, 'message:native-raw-1')
assert.equal(repeatedNativeCompanySync[0].timestamp, 5)
const structuredDeliveryMerge = __chatStoreTestUtils.dedupeMessages([
{
...nativeCompanyRawTurn,
content: 'Raw runtime wording before the canonical result wrapper.',
metadata: {
...nativeCompanyRawTurn.metadata,
result_delivery_id: 'result:company-turn-1:attempt:0',
},
},
{
...companyRoleResult,
content: 'Canonical committed wording may differ without creating another row.',
metadata: {
...companyRoleResult.metadata,
result_delivery_id: 'result:company-turn-1:attempt:0',
},
},
])
assert.equal(structuredDeliveryMerge.length, 1)
assert.equal(structuredDeliveryMerge[0].id, 'role-result-1')
assert.equal(
structuredDeliveryMerge[0].content,
'Canonical committed wording may differ without creating another row.',
)
const deliveryMirrorWithLongerWrapper = __chatStoreTestUtils.dedupeMessages([
{
...nativeCompanyRawTurn,
content: 'Runtime wrapper that must not outrank the committed surface.\n\nCanonical result body.',
metadata: {
...nativeCompanyRawTurn.metadata,
result_delivery_id: 'result:company-turn-wrapper:attempt:0',
},
},
{
...companyRoleResult,
content: 'Canonical result body.',
metadata: {
...companyRoleResult.metadata,
result_delivery_id: 'result:company-turn-wrapper:attempt:0',
},
},
])
assert.equal(deliveryMirrorWithLongerWrapper.length, 1)
assert.equal(
deliveryMirrorWithLongerWrapper[0].content,
'Canonical result body.',
'delivery mirrors follow surface authority; suffix repair only applies to one persistent message id',
)
const ctoDispatchContent = `Both work items have been successfully dispatched to my senior engineer. Here's the status:
## Dispatch Summary
**Work Item 1: OpenOPC Source Code Architecture Deep-Dive Analysis**
- ID: \`1ed5f5f1-ac41-49a1-b1fa-23bbc9adab82\`
- Owner: senior_engineer
- Scope: \`openopc-source-analysis\`
- Output: \`/data2/bjdwhzzh/project-hku/OpenOPC_workplace/0009/openopc-architecture-analysis.md\`
- Covers: Layered architecture, the work-item state machine, collaboration policy, and seat executor mechanisms.
**Work Item 2: External Multi-Agent Frameworks Architecture Research**
- ID: \`d0307208-6b95-44c1-9b51-6bf073bbdcef\`
- Owner: senior_engineer
Both work items are independent and can execute in parallel.`
const ctoCompanyFinal: ChatMessage = {
id: 'runtime-v2-company-assistant-final:cto-turn-9',
channelId: 'session:cto-work-item',
sender: 'cto',
senderName: 'CTO',
content: ctoDispatchContent,
timestamp: 20,
mentions: [],
metadata: {
source: 'engine',
transcript_kind: 'runtime_v2_company_assistant',
canonical_turn_id: 'cto-turn-9',
ui_message_id: 'runtime-v2-company-assistant-final:cto-turn-9',
},
}
const repeatedCtoSnapshot = __chatStoreTestUtils.dedupeMessages([
ctoCompanyFinal,
{ ...ctoCompanyFinal, metadata: { ...ctoCompanyFinal.metadata } },
])
assert.equal(repeatedCtoSnapshot.length, 1)
assert.equal(
repeatedCtoSnapshot[0].content,
ctoDispatchContent,
'comparison normalization must never peel Work Item, ID, or Owner fields from rendered content',
)
let repeatedCtoMerge = [ctoCompanyFinal]
for (let replay = 0; replay < 6; replay += 1) {
repeatedCtoMerge = __chatStoreTestUtils.mergeMessagesIntoExisting(
repeatedCtoMerge,
[{ ...ctoCompanyFinal, metadata: { ...ctoCompanyFinal.metadata } }],
)
assert.equal(repeatedCtoMerge.length, 1)
assert.equal(
repeatedCtoMerge[0].content,
ctoDispatchContent,
`identical MERGE replay ${replay + 1} must preserve the complete source text`,
)
}
const truncatedCtoContent = ctoDispatchContent.slice(
ctoDispatchContent.indexOf('OpenOPC Source Code Architecture Deep-Dive Analysis**'),
)
const truncatedCtoFinal: ChatMessage = {
...ctoCompanyFinal,
content: truncatedCtoContent,
}
let interleavedCtoReplay = [ctoCompanyFinal]
for (const replayedMessage of [
truncatedCtoFinal,
ctoCompanyFinal,
truncatedCtoFinal,
ctoCompanyFinal,
]) {
interleavedCtoReplay = __chatStoreTestUtils.mergeMessagesIntoExisting(
interleavedCtoReplay,
[{ ...replayedMessage, metadata: { ...replayedMessage.metadata } }],
)
assert.equal(interleavedCtoReplay.length, 1)
assert.equal(
interleavedCtoReplay[0].content,
ctoDispatchContent,
'a same-identity truncated cache replay must neither replace nor duplicate the complete message',
)
}
const repairedCtoReplay = __chatStoreTestUtils.mergeMessagesIntoExisting(
[truncatedCtoFinal],
[ctoCompanyFinal],
)
assert.equal(repairedCtoReplay.length, 1)
assert.equal(repairedCtoReplay[0].content, ctoDispatchContent)
assert.equal(
__chatStoreTestUtils.mergeMessagesIntoExisting(repairedCtoReplay, [truncatedCtoFinal])[0].content,
ctoDispatchContent,
'once a complete same-identity source arrives, later truncated replays must not regress it',
)
const mountedHighPriorityResult: ChatMessage = {
...companyRoleResult,
id: 'mounted-high-result',
@@ -23,8 +23,8 @@ function messageMetadata(message: ChatMessage): Record<string, unknown> {
return (message.metadata ?? {}) as Record<string, unknown>
}
function normalizeMessageContent(content: string): string {
const normalized = String(content ?? '')
function normalizeMessageFormatting(content: string): string {
return String(content ?? '')
.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
.split('\n')
@@ -32,6 +32,10 @@ function normalizeMessageContent(content: string): string {
.join('\n')
.replace(/\n{3,}/g, '\n\n')
.trim()
}
function normalizeMessageContent(content: string): string {
const normalized = normalizeMessageFormatting(content)
const titleStripped = stripNarrativeTitlePrefix(normalized)
const paragraphs = titleStripped.split(/\n{2,}/).map(part => part.trim()).filter(Boolean)
if (paragraphs.length > 1 && /^Verification:\s/i.test(paragraphs[paragraphs.length - 1])) {
@@ -41,21 +45,40 @@ function normalizeMessageContent(content: string): string {
}
function stripNarrativeTitlePrefix(content: string): string {
const trimmed = String(content || '').trim()
const markdownTitle = trimmed.match(/^\*\*(.{8,160}?)\*\*:\s+([\s\S]+)$/)
if (markdownTitle) {
let trimmed = String(content || '').trim()
// Only an explicit, first-line "**Title**: body" wrapper is removable.
// Searching for an arbitrary `: ` in the first N characters is destructive:
// ordinary Markdown such as "**Work Item 1: ...**" and list fields such as
// "- ID: ..." would be peeled one layer at a time on repeated syncs.
for (;;) {
const markdownTitle = trimmed.match(/^\*\*([^\r\n]{8,160}?)\*\*:(?:[ \t]+|\r?\n+)([\s\S]+)$/)
if (!markdownTitle) return trimmed
const body = markdownTitle[2].trim()
if (body.length >= 80) return body
if (body.length < 80 || body === trimmed) return trimmed
trimmed = body
}
const colonIndex = trimmed.indexOf(': ')
if (colonIndex < 8 || colonIndex > 160) return trimmed
}
const prefix = trimmed.slice(0, colonIndex).replace(/\*/g, '').trim()
const body = trimmed.slice(colonIndex + 2).trim()
if (body.length < 80) return trimmed
if (!/[A-Za-z\u4e00-\u9fff]/.test(prefix)) return trimmed
if (/^(https?|file)$/i.test(prefix)) return trimmed
return body
function losslessIdentityContent(
existing: ChatMessage,
candidate: ChatMessage,
preferred: ChatMessage,
sharesConcreteIdentity: boolean,
): string {
if (!sharesConcreteIdentity) return preferred.content
const existingComparable = normalizeMessageFormatting(existing.content)
const candidateComparable = normalizeMessageFormatting(candidate.content)
if (!existingComparable || !candidateComparable || existingComparable === candidateComparable) {
return preferred.content
}
// A cache/detail replay can contain a prefix-truncated copy of the same
// persistent message. Preserve the lossless source regardless of arrival
// order; unrelated edits still follow the normal preference rules.
if (existingComparable.endsWith(candidateComparable)) return existing.content
if (candidateComparable.endsWith(existingComparable)) return candidate.content
return preferred.content
}
function messageIdentityKeys(message: ChatMessage): Set<string> {
@@ -66,6 +89,9 @@ function messageIdentityKeys(message: ChatMessage): Set<string> {
for (const value of [
message.id,
typeof metadata.ui_message_id === 'string' ? metadata.ui_message_id : '',
typeof metadata.result_delivery_id === 'string' && metadata.result_delivery_id.trim()
? `delivery:${metadata.result_delivery_id.trim()}`
: '',
checkpointType && checkpointId ? `checkpoint:${checkpointType}:${checkpointId}` : '',
]) {
const normalized = String(value ?? '').trim()
@@ -81,7 +107,7 @@ function scopedMessageIdentityKeys(message: ChatMessage): Set<string> {
}
function isDerivedIdentityKey(value: string): boolean {
return value.startsWith('checkpoint:')
return value.startsWith('checkpoint:') || value.startsWith('delivery:')
}
function messageTimestamp(message: ChatMessage): number {
@@ -229,18 +255,17 @@ function mergeDuplicateMessages(
const existingIds = messageIdentityKeys(existing)
const candidateIds = messageIdentityKeys(candidate)
let sharesConcreteIdentity = false
let canonicalId = ''
for (const id of existingIds) {
if (candidateIds.has(id) && !isDerivedIdentityKey(id)) {
canonicalId = id
break
if (!candidateIds.has(id)) continue
if (!isDerivedIdentityKey(id)) {
sharesConcreteIdentity = true
if (!canonicalId) canonicalId = id
}
}
const normalizedContent = normalizeMessageContent(preferred.content)
const content = normalizedContent && normalizedContent === normalizeMessageContent(secondary.content)
? normalizedContent
: preferred.content
const content = losslessIdentityContent(existing, candidate, preferred, sharesConcreteIdentity)
const existingCheckpointId = String(messageMetadata(existing).checkpoint_id ?? '').trim()
const candidateCheckpointId = String(messageMetadata(candidate).checkpoint_id ?? '').trim()
@@ -17,6 +17,16 @@ assert.match(
assert.match(messageListSource, /scrollPolicy = 'follow'/, 'main transcript behavior should default to follow mode')
assert.doesNotMatch(messageListSource, /useVirtualizer/, 'chat transcript must use stable normal DOM rows')
assert.doesNotMatch(messageListSource, /PROGRAMMATIC_SCROLL_GRACE_MS/, 'scroll behavior must not regress to timer-based intent guessing')
assert.doesNotMatch(
messageListSource,
/seenNarrativeMessages|seenProjectUpdates/,
'MessageList must not independently remove durable rows using rendered content',
)
assert.match(
messageListSource,
/terminalAssistantTurnId\(message\)/,
'draft suppression must use the shared committed-turn resolver',
)
const progressWithoutServerId = {
type: 'status_change' as const,
@@ -147,7 +157,16 @@ const companyTurnKeys = [
metadata: { kind: 'company_role_result', canonical_turn_id: sharedCompanyTurn },
}),
]
assert.equal(new Set(companyTurnKeys).size, companyTurnKeys.length, 'distinct company rows sharing a turn need unique DOM keys')
assert.equal(
companyTurnKeys[1],
companyTurnKeys[2],
'company draft/final surfaces for one canonical turn must reuse the same DOM slot',
)
assert.notEqual(
companyTurnKeys[2],
companyTurnKeys[3],
'a separately committed role result keeps its result-delivery identity',
)
const narrativeItems = buildNarrativeMessageItems([
baseMessage('m1', '[Company:cto::execute::abc] starting Research source reliability', 1000),
@@ -163,35 +182,63 @@ assert.equal(narrativeItems[0].kind === 'ops-bundle' ? narrativeItems[0].events.
assert.equal(narrativeItems[1].kind, 'message')
assert.equal(narrativeItems[2].kind, 'ops-bundle')
const longResult = 'Completed the focused recheck and produced the QA artifact with caveats for downstream aggregation.'
const dedupedProjectUpdates = buildNarrativeMessageItems([
baseMessage('u1', prefixedPayload, 2000, 'qa_analyst'),
baseMessage('u2', `**Report #1: Recheck remediated screen**: ${prefixedPayload}`, 2000, 'qa_analyst'),
], { isCompanyRuntime: true, detailMode: 'summary' })
assert.equal(dedupedProjectUpdates.length, 1)
assert.equal(
dedupedProjectUpdates.length,
2,
'renderer must preserve distinct project-update rows; upstream identity owns consolidation',
)
assert.equal(dedupedProjectUpdates[0].kind, 'message')
assert.equal(dedupedProjectUpdates[0].kind === 'message' ? dedupedProjectUpdates[0].msg.id : '', 'u1')
const longResult = 'Completed the focused recheck and produced the QA artifact with caveats for downstream aggregation.'
const dedupedNarrativeMessages = buildNarrativeMessageItems([
const identicalDurableNarratives = buildNarrativeMessageItems([
baseMessage('same-content-1', longResult, 2500, 'qa_analyst'),
baseMessage('same-content-2', longResult, 2500, 'qa_analyst'),
], { isCompanyRuntime: true, detailMode: 'summary' })
assert.deepEqual(
identicalDurableNarratives.map(item => item.kind === 'message' ? item.msg.id : item.id),
['same-content-1', 'same-content-2'],
'distinct stable identities must survive even when sender, timestamp, and content are identical',
)
const ambiguousNarrativeMessages = buildNarrativeMessageItems([
baseMessage('n1', longResult, 3000, 'qa_analyst'),
baseMessage('n2', `Recheck remediated ten-bagger candidate screen: ${longResult}`, 3000, 'qa_analyst'),
], { isCompanyRuntime: true, detailMode: 'summary' })
assert.equal(dedupedNarrativeMessages.length, 1)
assert.equal(dedupedNarrativeMessages[0].kind === 'message' ? dedupedNarrativeMessages[0].msg.id : '', 'n1')
assert.equal(
ambiguousNarrativeMessages.length,
2,
'plain narrative prefixes are content and must not be stripped to guess message identity',
)
const duplicatedResultSurface = buildNarrativeMessageItems([
{
...baseMessage('r1', longResult, 4000, 'chao'),
metadata: { source: 'engine', transcript_kind: 'child_task_result' },
metadata: {
source: 'engine',
transcript_kind: 'child_task_result',
result_delivery_id: 'delivery-r1',
},
},
{
...baseMessage('r2', `Deliver final result to user: ${longResult}`, 4500, 'system'),
senderName: 'Company Member',
metadata: { source: 'runtime_event', kind: 'worker_notification', notification_kind: 'task_complete' },
metadata: {
source: 'engine',
transcript_kind: 'child_result',
result_delivery_id: 'delivery-r1',
},
},
], { isCompanyRuntime: true, detailMode: 'summary' })
assert.equal(duplicatedResultSurface.length, 1)
assert.equal(duplicatedResultSurface[0].kind === 'message' ? duplicatedResultSurface[0].msg.id : '', 'r1')
assert.equal(
duplicatedResultSurface.length,
2,
'MessageList must not run a second result consolidator after the store/company projection',
)
const fullItems = buildNarrativeMessageItems([
baseMessage('m1', '[Company:cto::execute::abc] starting Research source reliability', 1000),
@@ -4,6 +4,7 @@ import type { ProgressEntry, RoleWorkItemSummary, Session, WorkItemProgressEntry
import { progressEntryKey } from '../lib/progressEntryKey'
import { stableMessageTimelineKey } from '../lib/messageTimelineIdentity'
import { isMessageVisibleAtDetailLevel, resultSurfaceDedupeKey } from '../lib/workItemSessions'
import { resolveCanonicalTurnId, terminalAssistantTurnId } from '../lib/turnIdentity'
import { IconCopy, IconCheck, IconChat, IconSparkle, IconShield, IconActivity, IconChevron } from './SvgIcons'
import { AgentProgressBlock, AgentProgressEntryCard, INLINE_PROGRESS_ENTRY_TYPES } from './AgentProgressBlock'
import { MarkdownBody } from './MarkdownBody'
@@ -345,13 +346,6 @@ export function messageTimelineKey(message: ChatMessage): string {
return stableMessageTimelineKey(message)
}
function terminalAssistantTurnId(message: ChatMessage): string {
const timelineKey = messageTimelineKey(message)
return timelineKey.startsWith('turn:assistant:')
? timelineKey.slice('turn:assistant:'.length)
: ''
}
function formatTime(ts: number) {
return new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false })
}
@@ -400,44 +394,6 @@ function compactWhitespace(value: string): string {
return value.replace(/\s+/g, ' ').trim()
}
function stripNarrativeTitlePrefix(content: string): string {
const trimmed = String(content || '').trim()
const markdownTitle = trimmed.match(/^\*\*(.{8,160}?)\*\*:\s+([\s\S]+)$/)
if (markdownTitle) {
const body = markdownTitle[2].trim()
if (body.length >= 80) return body
}
const colonIndex = trimmed.indexOf(': ')
if (colonIndex < 8 || colonIndex > 160) return trimmed
const prefix = trimmed.slice(0, colonIndex).replace(/\*/g, '').trim()
const body = trimmed.slice(colonIndex + 2).trim()
if (body.length < 80) return trimmed
if (!/[A-Za-z\u4e00-\u9fff]/.test(prefix)) return trimmed
if (/^(https?|file)$/i.test(prefix)) return trimmed
return body
}
function isResultSurfaceMessage(message: ChatMessage): boolean {
const transcriptKind = String(message.metadata?.transcript_kind ?? message.metadata?.kind ?? '').trim()
if ([
'runtime_v2_assistant',
'runtime_v2_company_assistant',
'top_level_reply',
'company_role_result',
'company_role_result_retry',
'child_task_result',
'child_task_result_retry',
'child_result',
].includes(transcriptKind)) {
return true
}
if (String(message.metadata?.kind ?? '').trim() === 'worker_notification') {
return true
}
return false
}
function parseJsonObjectText(content: string): Record<string, unknown> | null {
const trimmed = String(content || '').trim()
if (!trimmed) return null
@@ -830,8 +786,6 @@ export function buildNarrativeMessageItems(
const items: TimelineItem[] = []
let bundle: SystemOpsBundleEvent[] = []
let bundleSortOrder = 0
const seenProjectUpdates = new Set<string>()
const seenNarrativeMessages = new Set<string>()
const flushBundle = () => {
if (bundle.length === 0) return
@@ -854,33 +808,6 @@ export function buildNarrativeMessageItems(
bundle.push(ops)
return
}
const projectUpdate = detailMode === 'summary' ? parseProjectUpdatePayload(msg.content) : null
if (projectUpdate) {
const dedupeKey = [
msg.sender,
Math.round(msg.timestamp / 1000),
projectUpdate.kind,
compactWhitespace(projectUpdate.summary || projectUpdate.acceptanceSummary || '').slice(0, 500),
projectUpdate.verdict ?? '',
projectUpdate.deliverables.map(item => `${item.name}:${item.path}`).join('|').slice(0, 800),
].join('\u0001')
if (seenProjectUpdates.has(dedupeKey)) return
seenProjectUpdates.add(dedupeKey)
}
if (detailMode === 'summary' && !isCheckpointCardMetadata(msg.metadata)) {
const canonicalContent = compactWhitespace(stripNarrativeTitlePrefix(msg.content)).slice(0, 1200)
if (canonicalContent) {
const dedupeKey = isResultSurfaceMessage(msg)
? ['result', canonicalContent].join('\u0001')
: [
msg.sender,
Math.round(msg.timestamp / 1000),
canonicalContent,
].join('\u0001')
if (seenNarrativeMessages.has(dedupeKey)) return
seenNarrativeMessages.add(dedupeKey)
}
}
flushBundle()
items.push({
kind: 'message',
@@ -1331,7 +1258,7 @@ export const MessageList = React.memo(function MessageList({
for (const message of timelineMessages) {
const thinking = String(message.metadata?.runtime_thinking ?? '').trim()
if (!thinking) continue
const turnId = String(message.metadata?.canonical_turn_id ?? message.metadata?.turn_id ?? '').trim()
const turnId = resolveCanonicalTurnId(message.metadata)
if (turnId && thinkingProgressTurnIds.has(turnId)) continue
entries.push({
type: 'thinking' as const,
@@ -1,4 +1,103 @@
import type { ChatMessage } from '../types/chat'
import { resolveCanonicalTurnId, terminalAssistantTurnId } from './turnIdentity'
const COMMITTED_RESULT_SURFACE_KINDS = new Set([
'child_task_result',
'child_task_result_retry',
'company_role_result',
'company_role_result_retry',
'child_result',
'top_level_reply',
])
const RUNTIME_RESULT_SURFACE_KINDS = new Set([
'runtime_v2_assistant',
'runtime_v2_company_assistant',
])
function metadataValue(metadata: Record<string, unknown>, ...keys: string[]): string {
for (const key of keys) {
const value = String(metadata[key] ?? '').trim()
if (value) return value
}
return ''
}
function resultGenerationSuffix(metadata: Record<string, unknown>, kind: string): string {
const explicitAttempt = metadataValue(
metadata,
'result_attempt',
'attempt',
'attempt_index',
'retry_count',
'retryCount',
)
const attempt = explicitAttempt || (kind.endsWith('_retry') ? 'retry' : '')
const revision = metadataValue(
metadata,
'result_revision',
'delivery_revision',
'revision',
)
return [
attempt ? `attempt:${attempt}` : '',
revision ? `revision:${revision}` : '',
].filter(Boolean).join(':')
}
function withResultGeneration(
base: string,
metadata: Record<string, unknown>,
kind: string,
): string {
const suffix = resultGenerationSuffix(metadata, kind)
return suffix ? `${base}:${suffix}` : base
}
/** Stable protocol identity shared by mirrors of one committed result. */
export function stableResultDeliveryKey(message: ChatMessage): string {
const metadata = (message.metadata ?? {}) as Record<string, unknown>
const kind = String(metadata.transcript_kind ?? metadata.kind ?? '').trim()
if (!COMMITTED_RESULT_SURFACE_KINDS.has(kind) && !RUNTIME_RESULT_SURFACE_KINDS.has(kind)) {
return ''
}
const deliveryId = metadataValue(
metadata,
'canonical_delivery_id',
'result_delivery_id',
'delivery_id',
)
if (deliveryId) return `delivery:${deliveryId}`
if (RUNTIME_RESULT_SURFACE_KINDS.has(kind)) {
const turnId = resolveCanonicalTurnId(metadata)
return turnId ? `turn:${turnId}` : ''
}
// A committed result belongs to a task/work-item delivery, not merely to
// the surrounding conversation turn. Parallel roles commonly share that
// turn, so a turn-only fallback would collapse independent deliveries.
const explicitSourceTaskId = metadataValue(metadata, 'source_task_id', 'child_task_id')
const sourceTaskId = explicitSourceTaskId || (
kind === 'top_level_reply'
? ''
: metadataValue(metadata, 'task_id', 'taskId')
)
if (sourceTaskId) {
return withResultGeneration(`source-task:${sourceTaskId}`, metadata, kind)
}
const workItemId = metadataValue(metadata, 'work_item_id', 'work_item_projection_id')
if (workItemId) {
return withResultGeneration(`work-item:${workItemId}`, metadata, kind)
}
const childSessionId = metadataValue(metadata, 'child_session_id')
return childSessionId
? withResultGeneration(`child-session:${childSessionId}`, metadata, kind)
: ''
}
export function stableMessageTimelineKey(message: ChatMessage): string {
const metadata = message.metadata ?? {}
@@ -21,10 +120,19 @@ export function stableMessageTimelineKey(message: ChatMessage): string {
const retainedTimelineId = String(metadata.ui_timeline_id ?? '').trim()
if (retainedTimelineId) return retainedTimelineId
const turnId = String(metadata.canonical_turn_id ?? metadata.turn_id ?? '').trim()
if (!isUserTurn && turnId && transcriptKind === 'runtime_v2_assistant') {
// Only a streamed runtime terminal owns the live draft's React slot.
// Committed result surfaces may expose the same conversation turn through
// terminalAssistantTurnId, but their row identity must remain delivery/task
// scoped so parallel role results cannot collide.
const turnId = RUNTIME_RESULT_SURFACE_KINDS.has(transcriptKind)
? terminalAssistantTurnId(message)
: ''
if (!isUserTurn && turnId) {
return `turn:assistant:${turnId}`
}
const resultDeliveryKey = stableResultDeliveryKey(message)
if (!isUserTurn && resultDeliveryKey) return `result:${resultDeliveryKey}`
return `message:${message.id}`
}
@@ -0,0 +1,50 @@
import type { ChatMessage, ChatMessageMeta } from '../types/chat'
const TERMINAL_ASSISTANT_KINDS = new Set([
'runtime_v2_assistant',
'runtime_v2_company_assistant',
'child_task_result',
'child_task_result_retry',
'company_role_result',
'company_role_result_retry',
'child_result',
'top_level_reply',
])
export function resolveCanonicalTurnId(
metadata: ChatMessageMeta | Record<string, unknown> | null | undefined,
): string {
const source = (metadata ?? {}) as Record<string, unknown>
for (const key of [
'canonical_turn_id',
'conversation_turn_id',
'turn_id',
'execution_turn_id',
]) {
const value = String(source[key] ?? '').trim()
if (value) return value
}
return ''
}
export function terminalAssistantTurnId(message: ChatMessage): string {
if (message.sender === 'user') return ''
const metadata = (message.metadata ?? {}) as Record<string, unknown>
const kind = String(metadata.transcript_kind ?? metadata.kind ?? '').trim()
if (!TERMINAL_ASSISTANT_KINDS.has(kind)) return ''
if (kind === 'runtime_v2_company_assistant') {
// Company tool-call iterations intentionally share this transcript kind
// and canonical turn with the final answer. Treating every iteration as
// terminal hides the live draft during detail refresh, then grows it back
// on the next delta. Only the actual final surface may replace the draft.
const uiMessageId = String(metadata.ui_message_id ?? '').trim()
const isFinal = metadata.company_final_turn === true
|| !!String(metadata.result_delivery_id ?? '').trim()
|| uiMessageId.startsWith('runtime-v2-company-assistant-final:')
// Snapshot translation retains final-vs-intermediate as visibility even
// when reading records created before structured delivery ids existed.
|| String(metadata.detail_visibility ?? '').trim() === 'summary'
if (!isFinal) return ''
}
return resolveCanonicalTurnId(metadata)
}
@@ -2,8 +2,10 @@ import assert from 'node:assert/strict'
import type { ChatMessage } from '../types/chat'
import type { Session } from '../types/kanban'
import { mapBackendSession, mergeSessionDetailHasMore } from './collabSync'
import { stableMessageTimelineKey, stableResultDeliveryKey } from './messageTimelineIdentity'
import { canonicalizeSessionExecutionIdentity } from './sessionIdentity'
import { deriveCompanyRuntimeDisplayStatus, getConversationHeaderSession, getConversationSessionView, getWorkItemChildSessions, getWorkItemRoleSessions, mergeConversationMessages, projectSessionConversation, selectCompanySummaryMessages } from './workItemSessions'
import { resolveCanonicalTurnId, terminalAssistantTurnId } from './turnIdentity'
import { deriveCompanyRuntimeDisplayStatus, getConversationHeaderSession, getConversationSessionView, getWorkItemChildSessions, getWorkItemRoleSessions, mergeConversationMessages, projectSessionConversation, resultSurfaceDedupeKey, selectCompanySummaryMessages } from './workItemSessions'
function makeSession(overrides: Partial<Session> & Pick<Session, 'taskId' | 'channelId' | 'title' | 'status' | 'columnId' | 'assigneeIds' | 'priority' | 'tags' | 'progressLog' | 'createdAt' | 'updatedAt' | 'messageCount'>): Session {
return {
@@ -338,12 +340,380 @@ assert.deepEqual(
[
'parent-user',
'canonical-role-result',
'summary-company-final',
'summary-terminal-b',
'child-checkpoint',
'child-checkpoint-response',
].sort(),
)
const companyRuntimeTurn = resultMessage(
'company-runtime-turn',
'session:company-child',
'A company runtime terminal surface.',
{
source: 'engine',
transcript_kind: 'runtime_v2_company_assistant',
canonical_turn_id: 'company-turn-0009',
result_delivery_id: 'company-delivery-0009',
},
'assistant',
)
assert.equal(
stableMessageTimelineKey(companyRuntimeTurn),
'turn:assistant:company-turn-0009',
)
const conversationOnlyRuntimeTurn = resultMessage(
'company-runtime-conversation-turn',
'session:company-child',
'A terminal surface with only its conversation identity.',
{
source: 'engine',
transcript_kind: 'runtime_v2_company_assistant',
conversation_turn_id: 'conversation-turn-only',
turn_id: 'iteration-turn-must-not-win',
execution_turn_id: 'execution-turn-must-not-win',
detail_visibility: 'summary',
},
'assistant',
)
assert.equal(
resolveCanonicalTurnId(conversationOnlyRuntimeTurn.metadata),
'conversation-turn-only',
)
assert.equal(terminalAssistantTurnId(conversationOnlyRuntimeTurn), 'conversation-turn-only')
assert.equal(
terminalAssistantTurnId({
...conversationOnlyRuntimeTurn,
metadata: {
...conversationOnlyRuntimeTurn.metadata,
detail_visibility: 'full',
},
}),
'',
'a company tool-call iteration must not suppress the active draft',
)
assert.equal(
stableMessageTimelineKey(conversationOnlyRuntimeTurn),
'turn:assistant:conversation-turn-only',
)
assert.equal(
terminalAssistantTurnId({
...conversationOnlyRuntimeTurn,
metadata: {
...conversationOnlyRuntimeTurn.metadata,
transcript_kind: 'company_role_result',
},
}),
'conversation-turn-only',
'committed result kinds must participate in terminal-turn matching',
)
assert.equal(
stableMessageTimelineKey({
...conversationOnlyRuntimeTurn,
metadata: {
...conversationOnlyRuntimeTurn.metadata,
transcript_kind: 'company_role_result',
source_task_id: 'committed-source-task',
},
}),
'result:source-task:committed-source-task',
'a committed terminal may expose its turn for matching without taking the runtime draft key',
)
const project0009CtoResult = [
'Both work items have been successfully dispatched to my senior engineer. Here\'s the status:',
'',
'## Dispatch Summary',
'',
'**Work Item 1: OpenOPC Source Code Architecture Deep-Dive Analysis**',
'- ID: `1ed5f5f1-ac41-49a1-b1fa-23bbc9adab82`',
'- Owner: senior_engineer',
'- Scope: `openopc-source-analysis`',
'- Output: `/data2/bjdwhzzh/project-hku/OpenOPC_workplace/0009/openopc-architecture-analysis.md`',
'- Covers: Layered architecture, collaboration policy, seat executor pattern, and self-evolution mechanisms.',
'',
'**Work Item 2: External Multi-Agent Frameworks Architecture Research**',
'- ID: `d0307208-6b95-44c1-9b51-6bf073bbdcef`',
'- Owner: senior_engineer',
'- Scope: `external-frameworks-research`',
'- Output: `/data2/bjdwhzzh/project-hku/OpenOPC_workplace/0009/external-frameworks-analysis.md`',
'',
'Both are independent and can execute in parallel. The runtime will monitor their completion.',
].join('\n')
const project0009WorkItemSuffix = project0009CtoResult.slice(
project0009CtoResult.indexOf('OpenOPC Source Code Architecture Deep-Dive Analysis'),
)
const project0009IdSuffix = project0009CtoResult.slice(
project0009CtoResult.indexOf('`1ed5f5f1-ac41-49a1-b1fa-23bbc9adab82`'),
)
// Content fallback must not treat arbitrary Markdown colons as removable
// narrative wrappers. The old normalization repeatedly turned the full 0009
// result into these two shorter variants, which changed the rendered height.
const fallback0009Full = resultMessage(
'0009-fallback-full',
'session:company-root',
project0009CtoResult,
{ source: 'engine', transcript_kind: 'child_result' },
'assistant',
)
const fallback0009WorkItem = resultMessage(
'0009-fallback-work-item',
'session:company-child',
project0009WorkItemSuffix,
{ source: 'engine', transcript_kind: 'runtime_v2_assistant' },
'assistant',
)
const fallback0009Id = resultMessage(
'0009-fallback-id',
'session:company-child',
project0009IdSuffix,
{ source: 'engine', transcript_kind: 'runtime_v2_assistant' },
'assistant',
)
assert.notEqual(resultSurfaceDedupeKey(fallback0009Full), resultSurfaceDedupeKey(fallback0009WorkItem))
assert.notEqual(resultSurfaceDedupeKey(fallback0009WorkItem), resultSurfaceDedupeKey(fallback0009Id))
const committed0009Parent = {
...resultMessage(
'0009-parent-result',
'session:company-root',
project0009CtoResult,
{
source: 'engine',
transcript_kind: 'child_result',
source_task_id: 'cto-task-0009',
},
'assistant',
),
timestamp: 2_000,
}
const committed0009Child = {
...resultMessage(
'0009-child-result',
'session:company-child',
project0009CtoResult,
{
source: 'engine',
transcript_kind: 'child_task_result',
task_id: 'cto-task-0009',
},
'assistant',
),
timestamp: 2_010,
}
const raw0009WorkItem = {
...fallback0009WorkItem,
metadata: {
...fallback0009WorkItem.metadata,
detail_visibility: 'summary' as const,
canonical_turn_id: 'cto-turn-0009',
},
timestamp: 2_020,
}
const raw0009Id = {
...fallback0009Id,
metadata: {
...fallback0009Id.metadata,
detail_visibility: 'summary' as const,
canonical_turn_id: 'cto-turn-0009',
},
timestamp: 2_030,
}
for (const messages of [
[committed0009Parent, committed0009Child, raw0009WorkItem, raw0009Id],
[raw0009Id, raw0009WorkItem, committed0009Child, committed0009Parent],
]) {
const summary = selectCompanySummaryMessages(messages, 'session:company-root')
assert.equal(summary.length, 1)
assert.equal(summary[0]?.id, '0009-child-result')
assert.equal(summary[0]?.content, project0009CtoResult)
assert.equal(stableMessageTimelineKey(summary[0]!), 'result:source-task:cto-task-0009')
}
const sameRoleText = 'The role completed its independent architecture analysis and committed the result.'
const multiRoleSummary = selectCompanySummaryMessages([
resultMessage(
'0009-cto-role-result',
'session:company-cto',
sameRoleText,
{
source: 'engine',
transcript_kind: 'company_role_result',
work_item_projection_id: '0009-cto-work-item',
},
'assistant',
),
resultMessage(
'0009-coo-role-result',
'session:company-coo',
sameRoleText,
{
source: 'engine',
transcript_kind: 'company_role_result',
work_item_projection_id: '0009-coo-work-item',
},
'assistant',
),
], 'session:company-root')
assert.deepEqual(
multiRoleSummary.map(message => stableMessageTimelineKey(message)).sort(),
[
'result:work-item:0009-coo-work-item',
'result:work-item:0009-cto-work-item',
],
)
const sharedConversationTurn = 'shared-company-conversation-turn'
const parallelRoleResults = [
resultMessage(
'parallel-cto-result',
'session:company-cto',
'CTO completed the architecture assessment.',
{
source: 'engine',
transcript_kind: 'company_role_result',
canonical_turn_id: sharedConversationTurn,
work_item_projection_id: 'architecture-assessment',
},
'assistant',
),
resultMessage(
'parallel-coo-result',
'session:company-coo',
'COO completed the feature assessment.',
{
source: 'engine',
transcript_kind: 'company_role_result',
canonical_turn_id: sharedConversationTurn,
work_item_projection_id: 'feature-assessment',
},
'assistant',
),
]
assert.deepEqual(
parallelRoleResults.map(resultSurfaceDedupeKey),
[
'result:work-item:architecture-assessment',
'result:work-item:feature-assessment',
],
'parallel committed roles must use work-item identity before a shared conversation turn',
)
assert.equal(
selectCompanySummaryMessages(parallelRoleResults, 'session:company-root').length,
2,
)
const versionedSourceResult = resultMessage(
'versioned-source-result',
'session:company-child',
'Versioned result.',
{
source: 'engine',
transcript_kind: 'company_role_result_retry',
source_task_id: 'source-task-versioned',
retry_count: 2,
delivery_revision: 4,
} as ChatMessage['metadata'],
'assistant',
)
assert.equal(
stableResultDeliveryKey(versionedSourceResult),
'source-task:source-task-versioned:attempt:2:revision:4',
)
const fullEqualPriorityResult = {
...resultMessage(
'full-equal-priority',
'session:company-child-a',
'The complete authoritative body includes every required architectural conclusion and its supporting rationale.',
{
source: 'engine',
transcript_kind: 'company_role_result',
result_delivery_id: 'deterministic-delivery',
},
'assistant',
),
timestamp: 2_100,
}
const truncatedEqualPriorityResult = {
...resultMessage(
'truncated-equal-priority',
'session:company-child-b',
'supporting rationale.',
{
source: 'engine',
transcript_kind: 'company_role_result',
result_delivery_id: 'deterministic-delivery',
},
'assistant',
),
timestamp: 2_200,
}
for (const groups of [
[[fullEqualPriorityResult], [truncatedEqualPriorityResult]],
[[truncatedEqualPriorityResult], [fullEqualPriorityResult]],
]) {
const merged = mergeConversationMessages(groups)
assert.equal(merged.length, 1)
assert.equal(merged[0]?.id, 'full-equal-priority')
assert.equal(merged[0]?.content, fullEqualPriorityResult.content)
assert.equal(merged[0]?.timestamp, 2_100)
assert.equal(
stableMessageTimelineKey(merged[0]!),
'result:delivery:deterministic-delivery',
)
const replayed = mergeConversationMessages([
merged,
[truncatedEqualPriorityResult],
])
assert.equal(replayed.length, 1)
assert.equal(replayed[0]?.id, 'full-equal-priority')
assert.equal(replayed[0]?.content, fullEqualPriorityResult.content)
}
const equalLengthStableWinner = {
...resultMessage(
'z-stable-winner',
'session:company-child-a',
'BBBB',
{
source: 'engine',
transcript_kind: 'company_role_result',
result_delivery_id: 'equal-length-delivery',
},
'assistant',
),
timestamp: 3_200,
}
const equalLengthLoser = {
...resultMessage(
'a-stable-loser',
'session:company-child-b',
'AAAA',
{
source: 'engine',
transcript_kind: 'company_role_result',
result_delivery_id: 'equal-length-delivery',
},
'assistant',
),
timestamp: 3_100,
}
for (const groups of [
[[equalLengthStableWinner], [equalLengthLoser]],
[[equalLengthLoser], [equalLengthStableWinner]],
]) {
const firstMerge = mergeConversationMessages(groups)
assert.equal(firstMerge[0]?.id, 'z-stable-winner')
assert.equal(firstMerge[0]?.content, 'BBBB')
assert.equal(firstMerge[0]?.timestamp, 3_100)
const replayed = mergeConversationMessages([firstMerge, [equalLengthLoser]])
assert.equal(replayed[0]?.id, 'z-stable-winner')
assert.equal(replayed[0]?.content, 'BBBB')
}
assert.equal(companyHeaderView?.status, 'running')
assert.equal(companyHeaderView?.contextTokens, 0)
assert.equal(companyHeaderView?.contextWindow, 128000)
@@ -2,7 +2,7 @@ import type { ChatMessage } from '../types/chat'
import type { ProgressEntry, Session } from '../types/kanban'
import { getContextUsageMetrics } from './contextUsage'
import { isSessionWorking } from './sessionRuntime'
import { stableMessageTimelineKey } from './messageTimelineIdentity'
import { stableMessageTimelineKey, stableResultDeliveryKey } from './messageTimelineIdentity'
const CONTEXT_TOKENS_RE = /(\d[\d,]*)\s*\/\s*(\d[\d,]*)\s+tokens/i
const USED_PCT_RE = /(\d{1,3})%\s*used/i
@@ -20,22 +20,32 @@ function compactWhitespace(value: string): string {
return value.replace(/\s+/g, ' ').trim()
}
function stripNarrativeTitlePrefix(content: string): string {
const trimmed = String(content || '').trim()
const markdownTitle = trimmed.match(/^\*\*(.{8,160}?)\*\*:\s+([\s\S]+)$/)
if (markdownTitle) {
const body = markdownTitle[2].trim()
if (body.length >= 80) return body
}
const colonIndex = trimmed.indexOf(': ')
if (colonIndex < 8 || colonIndex > 160) return trimmed
function normalizeResultContentFallback(content: string): string {
let normalized = String(content || '')
.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
.split('\n')
.map(line => line.trimEnd())
.join('\n')
.replace(/\n{3,}/g, '\n\n')
.trim()
const prefix = trimmed.slice(0, colonIndex).replace(/\*/g, '').trim()
const body = trimmed.slice(colonIndex + 2).trim()
if (body.length < 80) return trimmed
if (!/[A-Za-z\u4e00-\u9fff]/.test(prefix)) return trimmed
if (/^(https?|file)$/i.test(prefix)) return trimmed
return body
// Some result mirrors add one explicit Markdown narrative label. Only
// remove that anchored wrapper; a colon later in Markdown (for example
// "**Work Item 1: ..." or "- ID: ...") is message content, not a title.
for (;;) {
const markdownTitle = normalized.match(/^\*\*([^\r\n]{8,160}?)\*\*:(?:[ \t]+|\r?\n+)([\s\S]+)$/)
if (!markdownTitle) break
const body = markdownTitle[2].trim()
if (body.length < 80 || body === normalized) break
normalized = body
}
const paragraphs = normalized.split(/\n{2,}/).map(part => part.trim()).filter(Boolean)
if (paragraphs.length > 1 && /^Verification:\s/i.test(paragraphs[paragraphs.length - 1])) {
normalized = paragraphs.slice(0, -1).join('\n\n').trim()
}
return compactWhitespace(normalized).slice(0, 2000)
}
function resultSurfacePriority(message: ChatMessage): number {
@@ -67,9 +77,52 @@ function resultSurfacePriority(message: ChatMessage): number {
return 0
}
function normalizedResultContentLength(message: ChatMessage): number {
return String(message.content ?? '')
.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
.split('\n')
.map(line => line.trimEnd())
.join('\n')
.trim()
.length
}
function resultAuthorityScore(message: ChatMessage): number {
const metadata = (message.metadata ?? {}) as Record<string, unknown>
let score = 0
if (String(metadata.source ?? '').trim() === 'engine') score += 4
if (String(metadata.source_result_message_id ?? '').trim()) score += 2
if (metadata.authoritative_output === true || metadata.canonical_result === true) score += 1
return score
}
function compareResultSurfacePreference(left: ChatMessage, right: ChatMessage): number {
const numericComparisons: Array<[number, number]> = [
[resultSurfacePriority(left), resultSurfacePriority(right)],
[resultAuthorityScore(left), resultAuthorityScore(right)],
[normalizedResultContentLength(left), normalizedResultContentLength(right)],
]
for (const [leftValue, rightValue] of numericComparisons) {
if (leftValue !== rightValue) return leftValue > rightValue ? 1 : -1
}
const idComparison = left.id.localeCompare(right.id)
if (idComparison !== 0) return idComparison
const contentComparison = left.content.localeCompare(right.content)
if (contentComparison !== 0) return contentComparison
// Result chronology is later rewritten to the earliest underlying delivery.
// Timestamp is therefore safe only after immutable identity/content have
// tied; it must never be able to flip the selected surface on replay.
if (left.timestamp === right.timestamp) return 0
return left.timestamp > right.timestamp ? 1 : -1
}
export function resultSurfaceDedupeKey(message: ChatMessage): string {
if (resultSurfacePriority(message) <= 0) return ''
const content = compactWhitespace(stripNarrativeTitlePrefix(message.content)).slice(0, 2000)
const deliveryKey = stableResultDeliveryKey(message)
if (deliveryKey) return `result:${deliveryKey}`
const content = normalizeResultContentFallback(message.content)
return content ? `result:${content}` : ''
}
@@ -492,7 +545,7 @@ export function mergeConversationMessages(messageGroups: ChatMessage[][]): ChatM
const existingIndex = resultKeyIndex.get(resultKey)
if (existingIndex !== undefined) {
const existing = merged[existingIndex]
const candidateWins = resultSurfacePriority(message) > resultSurfacePriority(existing)
const candidateWins = compareResultSurfacePreference(message, existing) > 0
const preferred = candidateWins ? message : existing
const secondary = candidateWins ? existing : message
merged[existingIndex] = {
@@ -505,7 +558,7 @@ export function mergeConversationMessages(messageGroups: ChatMessage[][]): ChatM
metadata: {
...(secondary.metadata ?? {}),
...(preferred.metadata ?? {}),
ui_timeline_id: stableMessageTimelineKey(existing),
ui_timeline_id: resultKey || stableMessageTimelineKey(existing),
},
}
continue
@@ -541,22 +594,6 @@ export function selectCompanySummaryMessages(
messages: ChatMessage[],
parentChannelId: string,
): ChatMessage[] {
const terminalAssistantTurn = (message: ChatMessage): string => {
const metadata = (message.metadata ?? {}) as Record<string, unknown>
const kind = String(metadata.transcript_kind ?? metadata.kind ?? '').trim()
if (kind !== 'runtime_v2_assistant' && kind !== 'runtime_v2_company_assistant') return ''
return String(metadata.canonical_turn_id ?? metadata.turn_id ?? '').trim()
}
const parentTerminalTurns = new Set(
messages
.filter(message => (
message.channelId === parentChannelId
&& isMessageVisibleAtDetailLevel(message, 'summary')
))
.map(terminalAssistantTurn)
.filter(Boolean),
)
const childTerminalByTurn = new Map<string, ChatMessage>()
const durableMessages: ChatMessage[] = []
for (const message of messages) {
if (message.channelId === parentChannelId) {
@@ -584,38 +621,8 @@ export function selectCompanySummaryMessages(
'top_level_reply',
].includes(kind)) {
durableMessages.push(message)
continue
}
// Snapshot builder deliberately marks the final runtime surface as
// summary-visible. Preserve that durable contract instead of reusing the
// result-dedupe priority table as a visibility threshold.
const isSummaryTerminal = String(metadata.detail_visibility ?? '').trim() === 'summary'
&& (kind === 'runtime_v2_assistant' || kind === 'runtime_v2_company_assistant')
if (!isSummaryTerminal) continue
const turnId = terminalAssistantTurn(message)
if (!turnId) {
durableMessages.push(message)
continue
}
if (parentTerminalTurns.has(turnId)) continue
const existing = childTerminalByTurn.get(turnId)
if (!existing) {
childTerminalByTurn.set(turnId, message)
continue
}
const existingPriority = resultSurfacePriority(existing)
const candidatePriority = resultSurfacePriority(message)
if (candidatePriority > existingPriority) {
childTerminalByTurn.set(turnId, message)
} else if (
candidatePriority === existingPriority
&& (message.timestamp > existing.timestamp
|| (message.timestamp === existing.timestamp && message.id.localeCompare(existing.id) > 0))
) {
childTerminalByTurn.set(turnId, message)
}
}
durableMessages.push(...childTerminalByTurn.values())
return mergeConversationMessages([durableMessages])
}
@@ -472,7 +472,7 @@ async function runTimelineIdentityCases(page: Page, baseUrl: string): Promise<vo
collapsed: !!committed.querySelector('.msg-collapse-toggle'),
}
})
assert.equal(finalizedDraft.reused, true, 'draft -> runtime_v2_assistant final must reuse the same outer timeline DOM node')
assert.equal(finalizedDraft.reused, true, 'draft -> runtime_v2_company_assistant final must reuse the same outer timeline DOM node')
assert.equal(finalizedDraft.collapsed, false, 'a mounted expanded draft must not auto-collapse when its final arrives')
assert.ok(
finalizedDraft.height >= draftGeometry.height - 4,
@@ -366,7 +366,8 @@ function Fixture() {
mentions: [],
metadata: {
canonical_turn_id: LIVE_TURN_ID,
transcript_kind: 'runtime_v2_assistant',
result_delivery_id: `result:${LIVE_TURN_ID}:attempt:0`,
transcript_kind: 'runtime_v2_company_assistant',
},
},
])
@@ -341,7 +341,13 @@ export interface ChatMessageMeta {
/** UI-only identity retained across semantic result-surface replacement. */
ui_timeline_id?: string
canonical_turn_id?: string
conversation_turn_id?: string
turn_id?: string
execution_turn_id?: string
result_delivery_id?: string
source_result_message_id?: string
source_task_id?: string
child_session_id?: string
execution_mode?: string
transcript_kind?: string
detail_visibility?: DetailVisibility
+40 -26
View File
@@ -192,23 +192,33 @@ def _normalize_duplicate_content(content: Any) -> str:
def _strip_narrative_title_prefix(content: str) -> str:
trimmed = str(content or "").strip()
markdown_title = re.match(r"^\*\*(.{8,160}?)\*\*:\s+([\s\S]+)$", trimmed)
if markdown_title:
# Comparison-only canonicalization may unwrap a deliberate leading title,
# but must never interpret a colon inside ordinary Markdown body text as a
# wrapper boundary. Resolve explicit nested wrappers to a fixed point so
# the comparison key itself is idempotent.
while True:
markdown_title = re.match(r"^\*\*(.{8,160}?)\*\*:\s+([\s\S]+)$", trimmed)
if not markdown_title:
return trimmed
body = markdown_title.group(2).strip()
if len(body) >= 80:
return body
colon_index = trimmed.find(": ")
if colon_index < 8 or colon_index > 160:
return trimmed
prefix = trimmed[:colon_index].replace("*", "").strip()
body = trimmed[colon_index + 2 :].strip()
if len(body) < 80:
return trimmed
if not re.search(r"[A-Za-z\u4e00-\u9fff]", prefix):
return trimmed
if re.match(r"^(https?|file)$", prefix, flags=re.IGNORECASE):
return trimmed
return body
if len(body) < 80 or body == trimmed:
return trimmed
trimmed = body
def _select_duplicate_display_content(
preferred: dict[str, Any],
secondary: dict[str, Any],
) -> str:
"""Return one original surface body, never the lossy comparison key."""
preferred_content = str(preferred.get("content", "") or "")
secondary_content = str(secondary.get("content", "") or "")
preferred_key = _normalize_duplicate_content(preferred_content)
if not preferred_key or preferred_key != _normalize_duplicate_content(secondary_content):
return preferred_content
if secondary_content.strip() == preferred_key and preferred_content.strip() != preferred_key:
return secondary_content
return preferred_content
def _normalize_transcript_detail_level(value: Any) -> TranscriptDetailLevel:
@@ -1132,7 +1142,19 @@ def _transcript_item_to_ui_message(
**({"runtime_thinking": runtime_thinking} if runtime_thinking else {}),
**({
key: message_metadata.get(key)
for key in ("canonical_turn_id", "turn_id")
for key in (
"canonical_turn_id",
"turn_id",
"result_delivery_id",
"source_result_message_id",
"source_task_id",
"child_session_id",
"conversation_turn_id",
"execution_turn_id",
"work_item_projection_id",
"work_item_turn_type",
"runtime_session_id",
)
if message_metadata.get(key)
}),
**ui_meta,
@@ -1193,18 +1215,10 @@ def collapse_adjacent_transcript_duplicates(messages: list[dict[str, Any]]) -> l
):
merged_visibility = "full"
merged_metadata["detail_visibility"] = merged_visibility
preferred_content = str(preferred.get("content", "") or "")
secondary_content = str(secondary.get("content", "") or "")
normalized_content = _normalize_duplicate_content(preferred_content)
merged_content = (
normalized_content
if normalized_content and normalized_content == _normalize_duplicate_content(secondary_content)
else preferred_content
)
collapsed[-1] = {
**secondary,
**preferred,
"content": merged_content,
"content": _select_duplicate_display_content(preferred, secondary),
"metadata": merged_metadata,
}
return collapsed
+47
View File
@@ -37,6 +37,7 @@ from opc.layer2_organization.work_item_identity import (
migrate_work_item_projection_metadata,
projection_id_for_work_item,
rework_projection_id_for_gate,
result_delivery_identity_payload_for_task,
target_projection_id_for_decision,
target_projection_ids_for_decision,
work_item_identity_payload,
@@ -217,6 +218,52 @@ class WorkItemProjectionIdentityTests(unittest.TestCase):
self.assertEqual(payload[WORK_ITEM_PROJECTION_ID_KEY], "task-proj")
self.assertEqual(payload[WORK_ITEM_TURN_TYPE_KEY], "report")
def test_result_delivery_identity_uses_canonical_turn_and_retry_attempt(self) -> None:
task = SimpleNamespace(
id="task-1",
retry_count=2,
metadata={"runtime_v2_current_turn_id": "canonical-turn-1"},
)
payload = result_delivery_identity_payload_for_task(task)
self.assertEqual(
payload["result_delivery_id"],
"result:task:task-1:turn:canonical-turn-1:attempt:2",
)
self.assertEqual(payload["source_task_id"], "task-1")
self.assertEqual(payload["canonical_turn_id"], "canonical-turn-1")
def test_result_delivery_identity_does_not_collide_for_parallel_tasks(self) -> None:
first = SimpleNamespace(id="task-1", retry_count=0, metadata={})
second = SimpleNamespace(id="task-2", retry_count=0, metadata={})
first_payload = result_delivery_identity_payload_for_task(
first,
canonical_turn_id="shared-parent-turn",
)
second_payload = result_delivery_identity_payload_for_task(
second,
canonical_turn_id="shared-parent-turn",
)
self.assertNotEqual(
first_payload["result_delivery_id"],
second_payload["result_delivery_id"],
)
self.assertIn(":task-1:", first_payload["result_delivery_id"])
self.assertIn(":task-2:", second_payload["result_delivery_id"])
def test_result_delivery_identity_requires_an_execution_scope(self) -> None:
task = SimpleNamespace(id="reused-task", retry_count=0, metadata={})
self.assertEqual(result_delivery_identity_payload_for_task(task), {
"source_task_id": "reused-task",
})
first = result_delivery_identity_payload_for_task(task, execution_id="execution-1")
second = result_delivery_identity_payload_for_task(task, execution_id="execution-2")
self.assertNotEqual(first["result_delivery_id"], second["result_delivery_id"])
def test_migrate_projection_metadata_backfills_from_fallbacks_without_overwriting(self) -> None:
migrated, changed = migrate_work_item_projection_metadata(
{},
+153 -1
View File
@@ -67,8 +67,95 @@ class MemoryManagerCompactionTests(unittest.IsolatedAsyncioTestCase):
compactor.maybe_compact_after_message.assert_not_awaited()
async def test_parent_child_result_preserves_structured_source_lineage(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
store = _MemoryStoreStub()
memory = MemoryManager(Path(tmpdir), project_id="0009", store=store)
task = Task(
id="task-cto-result",
title="CTO Result",
assigned_to="cto",
project_id="0009",
session_id="child-session",
parent_session_id="parent-session",
metadata={"work_item_projection_id": "cto::analysis"},
)
await memory.record_child_session_result(
"parent-session",
"child-session",
task=task,
result_content="done",
result_delivery_id=(
"result:task:task-cto-result:turn:canonical-cto-turn:attempt:0"
),
source_result_message_id="source-result-message",
canonical_turn_id="canonical-cto-turn",
)
metadata = store.session_messages[-1].metadata
self.assertEqual(
metadata["result_delivery_id"],
"result:task:task-cto-result:turn:canonical-cto-turn:attempt:0",
)
self.assertEqual(metadata["source_result_message_id"], "source-result-message")
self.assertEqual(metadata["source_task_id"], "task-cto-result")
self.assertEqual(metadata["child_session_id"], "child-session")
self.assertEqual(metadata["canonical_turn_id"], "canonical-cto-turn")
class SharedRoleSessionExecutionTests(unittest.IsolatedAsyncioTestCase):
def test_fallback_delivery_identity_is_per_result_not_per_provider_session(self) -> None:
task = Task(
id="shared-role-task",
title="Shared role",
assigned_to="cto",
retry_count=0,
)
first = TaskResult(
status=TaskStatus.AWAITING_HUMAN,
content="first pause",
artifacts={"provider_session_id": "provider-session-reused"},
)
second = TaskResult(
status=TaskStatus.DONE,
content="second result",
artifacts={"provider_session_id": "provider-session-reused"},
)
first_identity = OPCEngine._ensure_result_delivery_identity_for_commit(task, first)
second_identity = OPCEngine._ensure_result_delivery_identity_for_commit(task, second)
self.assertNotEqual(
first_identity["result_delivery_id"],
second_identity["result_delivery_id"],
)
self.assertNotEqual(
first.artifacts["result_execution_id"],
second.artifacts["result_execution_id"],
)
self.assertNotIn("provider-session-reused", first_identity["result_delivery_id"])
def test_fallback_delivery_identity_is_stable_after_result_persistence(self) -> None:
task = Task(id="external-task", title="External", assigned_to="cto")
result = TaskResult(
status=TaskStatus.DONE,
content="done",
artifacts={"runtime_session_id": "runtime-session-reused"},
)
first_identity = OPCEngine._ensure_result_delivery_identity_for_commit(task, result)
persisted_artifacts = dict(result.artifacts)
reloaded = TaskResult(
status=TaskStatus.DONE,
content="done",
artifacts=persisted_artifacts,
)
replay_identity = OPCEngine._ensure_result_delivery_identity_for_commit(task, reloaded)
self.assertEqual(replay_identity, first_identity)
self.assertEqual(reloaded.artifacts, persisted_artifacts)
async def test_company_shared_role_session_keeps_results_local(self) -> None:
engine = OPCEngine()
engine.store = SimpleNamespace(
@@ -81,7 +168,11 @@ class SharedRoleSessionExecutionTests(unittest.IsolatedAsyncioTestCase):
record_task_completion_async=AsyncMock(),
)
engine._run_task_once = AsyncMock(
return_value=TaskResult(status=TaskStatus.DONE, content="done", artifacts={})
return_value=TaskResult(
status=TaskStatus.DONE,
content="done",
artifacts={"result_delivery_id": "delivery-shared-role"},
)
)
engine._apply_runtime_state_to_task = lambda task, result: None
@@ -105,3 +196,64 @@ class SharedRoleSessionExecutionTests(unittest.IsolatedAsyncioTestCase):
engine.memory.record_assistant_turn.assert_awaited_once()
engine.memory.record_child_session_result.assert_not_awaited()
metadata = engine.memory.record_assistant_turn.await_args.kwargs["metadata"]
self.assertEqual(metadata["result_delivery_id"], "delivery-shared-role")
self.assertEqual(metadata["source_task_id"], "task-cmo-review")
self.assertEqual(metadata["child_session_id"], "app14:role:cmo")
async def test_child_result_and_parent_mirror_share_delivery_identity(self) -> None:
engine = OPCEngine()
engine.store = SimpleNamespace(
get_task=AsyncMock(return_value=None),
save_task=AsyncMock(),
)
engine.memory = SimpleNamespace(
record_assistant_turn=AsyncMock(
return_value=SimpleNamespace(message_id="source-result-message")
),
record_child_session_result=AsyncMock(),
record_task_completion_async=AsyncMock(),
)
engine._run_task_once = AsyncMock(
return_value=TaskResult(
status=TaskStatus.DONE,
content="done",
artifacts={
"canonical_turn_id": "runtime-generated-turn",
"result_delivery_id": (
"result:task:task-cto-result:turn:runtime-generated-turn:attempt:0"
),
"runtime_session_id": "runtime-session-1",
},
)
)
engine._apply_runtime_state_to_task = lambda task, result: None
task = Task(
id="task-cto-result",
title="CTO Result",
assigned_to="cto",
status=TaskStatus.PENDING,
project_id="0009",
session_id="child-session",
parent_session_id="parent-session",
metadata={
"execution_mode": "company_mode",
"work_item_projection_id": "cto::analysis",
},
)
await engine._execute_task(task)
child_metadata = engine.memory.record_assistant_turn.await_args.kwargs["metadata"]
parent_call = engine.memory.record_child_session_result.await_args.kwargs
self.assertEqual(
child_metadata["result_delivery_id"],
"result:task:task-cto-result:turn:runtime-generated-turn:attempt:0",
)
self.assertEqual(
parent_call["result_delivery_id"],
child_metadata["result_delivery_id"],
)
self.assertEqual(parent_call["source_result_message_id"], "source-result-message")
self.assertEqual(parent_call["canonical_turn_id"], "runtime-generated-turn")
+8
View File
@@ -414,6 +414,13 @@ class NativeRuntimeV2Tests(unittest.IsolatedAsyncioTestCase):
# cannot swallow it into the frozen shared-iteration row.
self.assertTrue(metadata["company_final_turn"])
self.assertEqual(metadata["ui_message_id"], "runtime-v2-company-assistant-final:ui-turn:company")
self.assertEqual(
metadata["result_delivery_id"],
"result:task:company-role-task:turn:ui-turn:company:attempt:0",
)
self.assertEqual(metadata["source_task_id"], "company-role-task")
self.assertEqual(metadata["child_session_id"], "sess-company")
self.assertEqual(metadata["work_item_projection_id"], "chao::intake")
self.assertNotIn("visible_speaker", metadata)
await runtime._persist_assistant_turn(
@@ -429,6 +436,7 @@ class NativeRuntimeV2Tests(unittest.IsolatedAsyncioTestCase):
intermediate_metadata = memory.appended_messages[-1]["kwargs"]["metadata"]
self.assertEqual(intermediate_metadata["kind"], "runtime_v2_company_assistant")
self.assertNotIn("company_final_turn", intermediate_metadata)
self.assertNotIn("result_delivery_id", intermediate_metadata)
self.assertEqual(
intermediate_metadata["ui_message_id"],
"runtime-v2-company-assistant:ui-turn:company",
+315 -1
View File
@@ -16,10 +16,34 @@ from opc.plugins.office_ui.chat_store import (
_MessageMatchIndex,
_MessageMatchState,
)
from opc.plugins.office_ui.snapshot_builder import build_transcript_ui_messages
from opc.plugins.office_ui.snapshot_builder import (
build_transcript_ui_messages,
collapse_adjacent_transcript_duplicates,
)
from opc.plugins.office_ui.ws_handler import WSHandler
_WORK_ITEM_RESULT = """Both work items have been successfully dispatched and can execute in parallel.
**Dispatch Summary**
**Work Item 1: OpenOPC Source Code Architecture Deep-Dive Analysis**
- ID: `1ed5f5f1-ac41-49a1-b1fa-23bbc9adab82`
- Owner: senior_engineer
- Scope: `openopc-source-analysis`
- Output: `/workspace/openopc-architecture-analysis.md`
- Covers: Layered architecture, work-item state machines, collaboration policy, and seat executors.
**Work Item 2: External Multi-Agent Frameworks Architecture Research**
- ID: `d0307208-6b95-44c1-9b51-6bf073bbdcef`
- Owner: senior_engineer
- Scope: `external-frameworks-research`
- Output: `/workspace/external-frameworks-analysis.md`
- Covers: Architecture models, coordination, communication, extensibility, and implementation details.
Both are independent and can execute in parallel."""
class TranscriptStorePaginationTests(unittest.IsolatedAsyncioTestCase):
async def test_summary_page_filters_full_detail_rows_before_limit(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
@@ -216,6 +240,43 @@ class TranscriptStorePaginationTests(unittest.IsolatedAsyncioTestCase):
detail_level="summary",
))
def test_renderer_preserves_structured_result_lineage(self) -> None:
lineage = {
"canonical_turn_id": "turn-canonical",
"turn_id": "turn-execution",
"result_delivery_id": "delivery-1",
"source_result_message_id": "source-message-1",
"source_task_id": "source-task-1",
"child_session_id": "child-session-1",
"conversation_turn_id": "conversation-turn-1",
"execution_turn_id": "execution-turn-1",
"work_item_projection_id": "architecture",
"work_item_turn_type": "delivery",
"runtime_session_id": "runtime-session-1",
}
rendered = build_transcript_ui_messages(
[{
"message": SimpleNamespace(
message_id="result-message-1",
role="assistant",
agent_id="cto",
created_at=datetime(2026, 7, 14, 10, 0, 0),
summary_flag=False,
metadata={"kind": "child_result", **lineage},
),
"parts": [SimpleNamespace(
part_type="text",
payload={"text": "Completed architecture analysis."},
)],
}],
channel_id="session:parent-task",
task_id="parent-task",
)
self.assertEqual(len(rendered), 1)
for key, value in lineage.items():
self.assertEqual(rendered[0]["metadata"].get(key), value)
class ChatStorePaginationTests(unittest.TestCase):
@staticmethod
@@ -293,6 +354,129 @@ class ChatStorePaginationTests(unittest.TestCase):
actual = store._dedupe_messages(messages)
self.assertEqual(actual, expected)
def test_work_item_colons_are_not_treated_as_narrative_wrappers(self) -> None:
normalized = ChatStore._normalize_duplicate_content(_WORK_ITEM_RESULT)
self.assertEqual(normalized, _WORK_ITEM_RESULT)
self.assertEqual(ChatStore._normalize_duplicate_content(normalized), normalized)
def test_duplicate_merge_keeps_work_item_display_content_across_replays(self) -> None:
store = ChatStore(None) # type: ignore[arg-type]
current = {
"message_id": "runtime-final",
"channel_id": "session:cto",
"sender": "assistant",
"sender_name": "CTO",
"content": _WORK_ITEM_RESULT,
"created_at": 1.0,
"reply_to_id": None,
"mentions": [],
"metadata": {
"source": "engine",
"transcript_kind": "runtime_v2_company_assistant",
},
}
canonical = {
**current,
"message_id": "child-task-result",
"created_at": 2.0,
"metadata": {
"source": "engine",
"transcript_kind": "child_task_result",
},
}
for _ in range(6):
current = store._merge_duplicate_messages(current, canonical)
self.assertEqual(current["content"], _WORK_ITEM_RESULT)
def test_duplicate_merge_reuses_an_original_unwrapped_surface(self) -> None:
store = ChatStore(None) # type: ignore[arg-type]
body = "Canonical answer " + ("with implementation details. " * 8)
wrapped = {
"message_id": "runtime-final",
"channel_id": "session:answer",
"sender": "assistant",
"sender_name": "OPC",
"content": f"**Top level answer**: {body}",
"created_at": 1.0,
"metadata": {
"source": "engine",
"transcript_kind": "runtime_v2_assistant",
},
}
unwrapped = {
**wrapped,
"message_id": "top-level-reply",
"content": body,
"created_at": 2.0,
"metadata": {
"source": "engine",
"transcript_kind": "top_level_reply",
},
}
merged = store._merge_duplicate_messages(wrapped, unwrapped)
self.assertEqual(merged["content"], unwrapped["content"])
self.assertIn(merged["content"], (wrapped["content"], unwrapped["content"]))
def test_result_delivery_identity_is_namespaced_without_canonical_turn_fallback(self) -> None:
store = ChatStore(None) # type: ignore[arg-type]
existing = {
"message_id": "raw-final",
"channel_id": "session:result",
"sender": "assistant",
"content": "runtime wording",
"created_at": 1.0,
"metadata": {
"result_delivery_id": "delivery-1",
"canonical_turn_id": "turn-shared",
},
}
delivered = {
**existing,
"message_id": "child-task-result",
"content": "canonical result wording",
"metadata": {
"result_delivery_id": "delivery-1",
"canonical_turn_id": "turn-shared",
},
}
unrelated_user = {
**existing,
"message_id": "user-turn",
"sender": "user",
"content": "different user text",
"metadata": {"canonical_turn_id": "turn-shared"},
}
self.assertIn("result_delivery:delivery-1", store._message_identity_keys(existing))
self.assertTrue(store._messages_semantically_match(existing, delivered))
self.assertFalse(store._messages_semantically_match(existing, unrelated_user))
def test_transcript_collapse_keeps_work_item_display_content_across_replays(self) -> None:
runtime = {
"message_id": "runtime-final",
"sender": "assistant",
"sender_name": "CTO",
"content": _WORK_ITEM_RESULT,
"created_at": 1.0,
"metadata": {"transcript_kind": "runtime_v2_company_assistant"},
}
canonical = {
**runtime,
"message_id": "child-task-result",
"created_at": 2.0,
"metadata": {"transcript_kind": "child_task_result"},
}
collapsed = collapse_adjacent_transcript_duplicates([runtime, canonical])
self.assertEqual(len(collapsed), 1)
self.assertEqual(collapsed[0]["content"], _WORK_ITEM_RESULT)
replayed = collapse_adjacent_transcript_duplicates([runtime, collapsed[0]])
self.assertEqual(len(replayed), 1)
self.assertEqual(replayed[0]["content"], _WORK_ITEM_RESULT)
def test_indexed_dedupe_normalizes_long_content_once_per_row(self) -> None:
class CountingChatStore(ChatStore):
normalize_calls = 0
@@ -501,6 +685,136 @@ class ChatStorePaginationTests(unittest.TestCase):
await db.close()
tmpdir.cleanup()
def test_backfill_same_id_repairs_destructively_normalized_content(self) -> None:
asyncio.run(self._exercise_backfill_same_id_repairs_content())
async def _exercise_backfill_same_id_repairs_content(self) -> None:
tmpdir = tempfile.TemporaryDirectory()
db = _SQLiteConnectionAdapter(str(Path(tmpdir.name) / "ui-state.db"))
store = ChatStore(db) # type: ignore[arg-type]
await store.initialize()
channel_id = "session:work-item-repair"
project_id = "test-project"
damaged_content = _WORK_ITEM_RESULT[_WORK_ITEM_RESULT.index("OpenOPC Source"):]
metadata = {
"source": "engine",
"transcript_kind": "child_result",
"legacy_cache_marker": True,
}
authoritative = {
"message_id": "result-message-1",
"sender": "assistant",
"sender_name": "CTO",
"content": _WORK_ITEM_RESULT,
"created_at": 1.0,
"metadata": {
"source": "engine",
"transcript_kind": "child_result",
},
}
try:
await store.insert_message(
channel_id,
"assistant",
"CTO",
damaged_content,
metadata=metadata,
message_id="result-message-1",
project_id=project_id,
created_at=1.0,
)
repaired = await store.backfill_messages(
channel_id,
[authoritative],
project_id=project_id,
)
replayed = await store.backfill_messages(
channel_id,
[authoritative],
project_id=project_id,
)
rows = await store.get_channel_messages(
channel_id,
limit=20,
project_id=project_id,
)
self.assertEqual([message["message_id"] for message in repaired], ["result-message-1"])
self.assertEqual(replayed, [])
self.assertEqual(len(rows), 1)
self.assertEqual(rows[0]["content"], _WORK_ITEM_RESULT)
self.assertTrue(rows[0]["metadata"].get("legacy_cache_marker"))
finally:
await db.close()
tmpdir.cleanup()
def test_backfill_semantic_duplicate_upgrades_existing_row_in_place(self) -> None:
asyncio.run(self._exercise_backfill_semantic_duplicate_upgrade())
async def _exercise_backfill_semantic_duplicate_upgrade(self) -> None:
tmpdir = tempfile.TemporaryDirectory()
db = _SQLiteConnectionAdapter(str(Path(tmpdir.name) / "ui-state.db"))
store = ChatStore(db) # type: ignore[arg-type]
await store.initialize()
channel_id = "session:semantic-result-repair"
project_id = "test-project"
authoritative_content = _WORK_ITEM_RESULT
authoritative = {
"message_id": "source-result-message",
"sender": "cto",
"sender_name": "CTO",
"content": authoritative_content,
"created_at": 2.0,
"metadata": {
"source": "engine",
"transcript_kind": "child_task_result",
},
}
try:
await store.insert_message(
channel_id,
"assistant",
"CTO",
f"**Legacy result**: {_WORK_ITEM_RESULT}",
metadata={
"source": "engine",
"transcript_kind": "runtime_v2_company_assistant",
"legacy_cache_marker": True,
},
message_id="mounted-cache-row",
project_id=project_id,
created_at=1.0,
)
upgraded = await store.backfill_messages(
channel_id,
[authoritative],
project_id=project_id,
)
replayed = await store.backfill_messages(
channel_id,
[authoritative],
project_id=project_id,
)
rows = await store.get_channel_messages(
channel_id,
limit=20,
project_id=project_id,
)
self.assertEqual([message["message_id"] for message in upgraded], ["mounted-cache-row"])
self.assertEqual(replayed, [])
self.assertEqual(len(rows), 1)
self.assertEqual(rows[0]["message_id"], "mounted-cache-row")
self.assertEqual(rows[0]["created_at"], 1.0)
self.assertEqual(rows[0]["content"], authoritative_content)
self.assertEqual(rows[0]["metadata"].get("transcript_kind"), "child_task_result")
self.assertTrue(rows[0]["metadata"].get("legacy_cache_marker"))
finally:
await db.close()
tmpdir.cleanup()
def test_summary_cache_page_filters_before_raw_fetch_limit(self) -> None:
asyncio.run(self._exercise_summary_cache_page())