Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/issues/cua-driver-v0-1-5-sync/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Plan

## Source Review

- Compare upstream `trycua/cua` tags `cua-driver-v0.1.4` and
`cua-driver-v0.1.5`.
- Confirm whether `libs/cua-driver/Skills/cua-driver` changed.
- Review local fork differences before applying upstream changes.

## Implementation

- Cherry-pick the small upstream source changes into
`plugins/cua/vendor/cua-driver/source`.
- Update `plugins/cua/vendor/cua-driver/upstream.json` to record
`cua-driver-v0.1.5`.
- Leave packaged `plugins/cua/skills/cua-driver` MCP-first guidance intact
unless upstream skill files changed.

## Validation

- Run `pnpm run format`.
- Run `pnpm run i18n`.
- Run `pnpm run lint`.
- Run `pnpm run plugin:cua:validate`.

## Risk

The driver source is a maintained local fork, so direct upstream replacement
would risk losing DeepChat packaging, permissions, and MCP behavior. A focused
manual cherry-pick keeps the change auditable.
36 changes: 36 additions & 0 deletions docs/issues/cua-driver-v0-1-5-sync/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# CUA Driver v0.1.5 Sync

## Problem

The vendored DeepChat CUA driver snapshot is based on upstream `cua-driver-v0.1.4`.
Upstream `cua-driver-v0.1.5` fixes window enumeration for system overlay or
non-layer-0 windows when callers filter by pid.

## User Story

As a DeepChat user using the bundled Computer Use plugin, I need `list_windows`
and `get_window_state` to surface relevant overlay windows for a target pid so
agent workflows can inspect and act on the correct UI.

## Acceptance Criteria

- The vendored driver records upstream `cua-driver-v0.1.5` metadata.
- The upstream overlay-window fix is applied without replacing DeepChat's local
fork changes.
- Packaged plugin skills remain MCP-first. If upstream skills change in this
sync, start from upstream skill content and preserve DeepChat's MCP-first
guidance in the packaged skill.
- Validation covers formatting, i18n generation, lint, and CUA plugin package
validation where practical.

## Non-goals

- No rewrite of the CUA plugin manifest, settings UI, or MCP policy.
- No wholesale replacement of the DeepChat-owned driver fork.
- No behavior changes outside the CUA driver vendored source and plugin skills.

## Constraints

- Preserve local DeepChat fork behavior and packaging paths.
- Keep user-facing CUA skill guidance aligned with MCP tools, not CLI-first
workflows.
9 changes: 9 additions & 0 deletions docs/issues/cua-driver-v0-1-5-sync/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Tasks

- [x] Inspect upstream CUA tags and identify the latest driver release.
- [x] Compare upstream `cua-driver-v0.1.4` to `cua-driver-v0.1.5`.
- [x] Confirm upstream skills are unchanged in this release.
- [x] Apply the overlay-window source fix to the local fork.
- [x] Update vendored upstream metadata to `cua-driver-v0.1.5`.
- [x] Verify packaged skills keep DeepChat MCP-first guidance.
- [x] Run required formatting, i18n, lint, and CUA plugin validation.
17 changes: 17 additions & 0 deletions docs/issues/tool-call-path-summary/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Tool Call Path Summary Plan

## Approach

- Extend the shared tool call summary helper with optional tool context.
- Prefer tool-specific fields before falling back to the existing summary extraction.
- Pass the current tool name from the renderer tool call block and remote trace renderer.

## Affected Paths

- Collapsed tool call pill summaries in chat messages.
- Remote process trace lines generated from tool calls.

## Compatibility

- Existing helper callers remain valid because the context argument is optional.
- Missing or non-string `path` values fall back to the previous generic summary.
25 changes: 25 additions & 0 deletions docs/issues/tool-call-path-summary/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Tool Call Path Summary

## User Story

Users should see the target file path in collapsed `read` and `write` tool call pills, even when
pagination or content fields appear before `path` in the tool arguments.

## Acceptance Criteria

- `read` tool calls preview `path` before `offset`, `limit`, or `base_directory`.
- `write` tool calls preview `path` before `content` or `base_directory`.
- `exec` tool calls continue to preview `command`.
- Generic tool calls keep the existing fallback summary behavior.
- Malformed JSON and missing paths continue to fall back without throwing.

## Non-goals

- Do not change the visible collapsed pill layout.
- Do not add new i18n strings.
- Do not change raw params shown in the expanded details panel.

## Constraints

- Keep the summary helper compatible with existing callers.
- Apply the same path-first behavior to renderer pills and remote trace logs.
7 changes: 7 additions & 0 deletions docs/issues/tool-call-path-summary/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Tool Call Path Summary Tasks

- [x] Document the issue and intended behavior.
- [x] Add tool-aware summary extraction.
- [x] Pass tool names from renderer and remote trace callers.
- [x] Add regression tests for read/write path-first summaries.
- [x] Run targeted and required checks.
2 changes: 1 addition & 1 deletion plugins/cua/vendor/cua-driver/source/.bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.1.4
current_version = 0.1.5
commit = True
tag = True
tag_name = cua-driver-v{new_version}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,17 @@ public actor AppStateEngine {
var nextIndex = 0
var markdown = ""

let layer0WindowIds: Set<CGWindowID> = Set(
WindowEnumerator.allWindows()
.filter { $0.layer == 0 }
.map { CGWindowID($0.id) }
)

renderTree(
root,
depth: 0,
targetWindowId: windowId,
layer0WindowIds: layer0WindowIds,
elements: &elements,
nextIndex: &nextIndex,
output: &markdown
Expand Down Expand Up @@ -510,6 +517,7 @@ public actor AppStateEngine {
_ element: AXUIElement,
depth: Int,
targetWindowId: UInt32?,
layer0WindowIds: Set<CGWindowID> = [],
elements: inout [Int: AXUIElement],
nextIndex: inout Int,
output: inout String
Expand Down Expand Up @@ -595,7 +603,8 @@ public actor AppStateEngine {
// windows whose CGWindowID we can't read.
return true
}
return cgWindowId == targetWid
if cgWindowId == targetWid { return true }
return !layer0WindowIds.contains(cgWindowId)
}
}

Expand All @@ -604,6 +613,7 @@ public actor AppStateEngine {
child,
depth: depth + 1,
targetWindowId: targetWindowId,
layer0WindowIds: layer0WindowIds,
elements: &elements,
nextIndex: &nextIndex,
output: &output
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation

public enum CuaDriverCore {
public static let version = "0.1.4"
public static let version = "0.1.5"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ public enum ListWindowsTool {
tool: Tool(
name: "list_windows",
description: """
List every layer-0 top-level window currently known to
List every relevant top-level window currently known to
WindowServer — including off-screen ones (hidden-launched,
minimized into the Dock, on another Space). Each record
self-contains its owning app identity so the caller never
has to join back against list_apps.
minimized into the Dock, on another Space) and, when a pid
filter is supplied, that pid's overlay windows. Each record
self-contains its owning app identity so the caller never has
to join back against list_apps.

Use this — not list_apps — for any window-level reasoning:
"does this app have a visible window right now?", "which
Expand Down Expand Up @@ -52,9 +53,10 @@ public enum ListWindowsTool {

Inputs: pid (optional — restrict to one pid's windows),
on_screen_only (bool, default false — surface off-Space /
minimized windows by default). Layer 0 filtering is
always applied; menubar strips and dock shields are
noise for every current caller.
minimized windows by default). Without pid, layer-0 filtering
is applied so menubar strips and dock shields stay out of the
default result. With pid, all windows for that process are
returned so app-owned overlays remain inspectable.
""",
inputSchema: [
"type": "object",
Expand Down Expand Up @@ -98,9 +100,10 @@ public enum ListWindowsTool {
: WindowEnumerator.allWindows()

let windows = raw
.filter { $0.layer == 0 }
.filter { info in
guard let pid = pidFilter else { return true }
guard let pid = pidFilter else {
return info.layer == 0
}
return info.pid == pid
}

Expand Down
14 changes: 7 additions & 7 deletions plugins/cua/vendor/cua-driver/upstream.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
"sourceKind": "deepchat-owned-fork",
"upstreamRepo": "https://github.com/trycua/cua.git",
"upstreamSubdir": "libs/cua-driver",
"tag": "cua-driver-v0.1.4",
"commit": "d422294b848afec99b979ac1229446c83fa44807",
"version": "0.1.4",
"updatedAt": "2026-05-06",
"tag": "cua-driver-v0.1.5",
"commit": "534304f56290b290a3799d4168d3bdf42287be26",
"version": "0.1.5",
"updatedAt": "2026-05-08",
"forkPolicy": "Build from the DeepChat-maintained local source snapshot. Cherry-pick upstream fixes only when they directly improve the bundled DeepChat Computer Use helper.",
"lastCherryPick": {
"sourceTag": "cua-driver-v0.1.4",
"sourceCommit": "d422294b848afec99b979ac1229446c83fa44807",
"appliedAt": "2026-05-06"
"sourceTag": "cua-driver-v0.1.5",
"sourceCommit": "534304f56290b290a3799d4168d3bdf42287be26",
"appliedAt": "2026-05-08"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,11 @@ const escapeTracePreview = (value: string): string =>

const getTracePreview = (
value: string | undefined | null,
toolName?: string,
fallback: string = '(none)',
limit: number = TRACE_PREVIEW_LIMIT
): string => {
const preview = summarizeToolCallPreview(value) || fallback
const preview = summarizeToolCallPreview(value, { toolName }) || fallback
return escapeTracePreview(truncateSingleLine(preview, limit))
}

Expand Down Expand Up @@ -299,12 +300,16 @@ const getProcessLogLines = (block: AssistantMessageBlock): string[] => {

const toolName = normalizeText(block.tool_call?.name) || 'unknown_tool'
const lines = [
`${getTraceEmoji(toolName)} ${toolName}: "${getTracePreview(block.tool_call?.params)}"`
`${getTraceEmoji(toolName)} ${toolName}: "${getTracePreview(block.tool_call?.params, toolName)}"`
]

if (block.status === 'error') {
lines.push(
`❌ ${toolName}: "${getTracePreview(block.tool_call?.response || block.content, 'error')}"`
`❌ ${toolName}: "${getTracePreview(
block.tool_call?.response || block.content,
undefined,
'error'
)}"`
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ const summaryText = computed(() => {

const raw = paramsText.value.trim()
if (!raw) return ''
return summarizeToolCallPreview(raw)
return summarizeToolCallPreview(raw, { toolName: functionLabel.value })
})

const subagentTasks = computed<SubagentProgressTask[]>(() => {
Expand Down
55 changes: 53 additions & 2 deletions src/shared/lib/toolCallSummary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,51 @@ const normalizeInlineText = (value: string): string => value.replace(/\s+/g, ' '
const isRecord = (value: unknown): value is Record<string, unknown> =>
Boolean(value) && typeof value === 'object' && !Array.isArray(value)

type ToolCallSummaryContext = {
toolName?: string | null
}

const matchesToolContractName = (
toolName: string | undefined | null,
expectedName: string
): boolean => {
const normalized = toolName?.trim().toLowerCase() ?? ''
return (
normalized === expectedName ||
normalized.endsWith(`_${expectedName}`) ||
normalized.endsWith(`-${expectedName}`) ||
normalized.startsWith(`${expectedName}_`) ||
normalized.startsWith(`${expectedName}-`)
)
}

const extractStringField = (value: Record<string, unknown>, field: string): string | undefined => {
const fieldValue = value[field]
return typeof fieldValue === 'string' && fieldValue.trim().length > 0 ? fieldValue : undefined
}

const extractToolSpecificSummaryValue = (
value: unknown,
context?: ToolCallSummaryContext
): unknown => {
if (!isRecord(value)) {
return undefined
}

if (matchesToolContractName(context?.toolName, 'exec')) {
return extractStringField(value, 'command')
}

if (
matchesToolContractName(context?.toolName, 'read') ||
matchesToolContractName(context?.toolName, 'write')
) {
return extractStringField(value, 'path')
}

return undefined
}

const extractFirstSummaryValue = (value: unknown): unknown => {
if (Array.isArray(value)) {
return value.length > 0 ? value[0] : ''
Expand Down Expand Up @@ -44,14 +89,20 @@ const formatSummaryValue = (value: unknown): string => {
}
}

export const summarizeToolCallPreview = (value: string | undefined | null): string => {
export const summarizeToolCallPreview = (
value: string | undefined | null,
context?: ToolCallSummaryContext
): string => {
const raw = value?.trim() ?? ''
if (!raw) {
return ''
}

try {
return formatSummaryValue(extractFirstSummaryValue(JSON.parse(raw) as unknown))
const parsed = JSON.parse(raw) as unknown
return formatSummaryValue(
extractToolSpecificSummaryValue(parsed, context) ?? extractFirstSummaryValue(parsed)
)
} catch {
return normalizeInlineText(raw)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ describe('remoteBlockRenderer', () => {
tool_call: {
id: 'tool-2',
name: 'read_file',
params: '{"path":"/tmp/report.md"}'
params: '{"offset":5000,"path":"/tmp/report.md"}'
},
extra: {
toolCallArgsComplete: true
Expand Down
Loading