fix(office-ui): native progress parity, ui_state lock hardening, approval-card idempotency

Native agent progress panel (company mode):
- ws_handler: filter runtime bookkeeping noise (turn/status/member_inbox_updated),
  keep tool_completed as tool_call, thinking summary previews content,
  preserve raw thinking_delta fragments (no strip; skip whitespace-only)
- frontend progressLog: summarize thinking by content preview; merge thinking
  by detail only so the 'Thinking' label never splices into text
- AgentProgressBlock: add bottom "Show more (N earlier steps)" toggle

ui_state.db "database is locked" hardening:
- ws_handler: isolate engine progress/kanban/runtime-event callbacks so UI
  persistence failures never crash work items
- chat_store: busy_timeout, _retry_locked backoff, idempotent insert_message
  (INSERT OR REPLACE), create_channel read-before-write to stop poll writes
- server: flock single-instance guard for `opc ui` per OPC home

Approval card duplicate-click bug:
- EscalationPanel: disable buttons on click with Submitting state and 30s
  reconnect fallback
- ws_handler: stale-escalation branch checks real card status (new
  chat_store.get_checkpoint_message); already-resolved cards get an accurate
  "already handled (decision: X)" reply without being re-marked stale;
  dedup identical helper messages within 120s to stop reply spam

Company mode prompt:
- add soft guidance that the runtime monitors state and re-activates roles,
  so leaders need not poll work items after delegation/review

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
LZH-YS1998
2026-07-07 16:18:01 +08:00
parent ee79331d48
commit 12817a4e60
12 changed files with 516 additions and 157 deletions
@@ -155,7 +155,7 @@ export function AgentProgressBlock({ entries, agentStatus, currentTool, toolElap
const cfg = ENTRY_CONFIG[entry.type] || ENTRY_CONFIG.status_change
return (
<div key={progressEntryKey(entry, i)} className={`ptl-entry${isLast ? ' ptl-entry-last' : ''}`}>
<div key={progressEntryKey(entry, hiddenCount + i)} className={`ptl-entry${isLast ? ' ptl-entry-last' : ''}`}>
<div className="ptl-connector">
<div className="ptl-dot" style={{ color: cfg.color }}>
{cfg.icon}
@@ -183,13 +183,19 @@ export function AgentProgressBlock({ entries, agentStatus, currentTool, toolElap
</div>
)}
{/* ── Collapse button (when expanded) ────────────── */}
{/* ── Expand/collapse toggle (below timeline) ────── */}
{expanded && filteredEntries.length > COLLAPSED_COUNT && (
<button className="ptl-expand" onClick={() => setExpanded(false)}>
<IconChevron down />
<span>Show less</span>
</button>
)}
{!expanded && hiddenCount > 0 && (
<button className="ptl-expand" onClick={() => setExpanded(true)}>
<IconChevron />
<span>Show more ({hiddenCount} earlier step{hiddenCount > 1 ? 's' : ''})</span>
</button>
)}
</div>
)
}