Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
8b5baf0
Modernize and ship Feed entry points
lawrencecchen Jul 15, 2026
684aef9
test: keep Feed panes out of sidebar focus
lawrencecchen Jul 15, 2026
d6f5586
Fix Feed pane interaction and navigation
lawrencecchen Jul 15, 2026
063015c
Align Feed pane filters with content edge
lawrencecchen Jul 16, 2026
e70b5f8
Fix Feed Swift concurrency checks
lawrencecchen Jul 16, 2026
dfe9628
Polish Feed menu gating and alignment
lawrencecchen Jul 17, 2026
6a0dd52
Merge remote-tracking branch 'origin/main' into task-modernize-feed
lawrencecchen Jul 17, 2026
047d237
Harden Feed concurrency and rollout gates
lawrencecchen Jul 17, 2026
ad4d6db
Merge remote-tracking branch 'origin/main' into task-modernize-feed
lawrencecchen Jul 17, 2026
e28efdd
Wire merged workspace todo regression test
lawrencecchen Jul 17, 2026
2de82a6
Fix Feed notification policy and lifecycle
lawrencecchen Jul 17, 2026
e9da3ea
Merge remote-tracking branch 'origin/main' into task-modernize-feed
lawrencecchen Jul 17, 2026
bb2d623
Use async notification center APIs for Feed
lawrencecchen Jul 17, 2026
b6e9143
Test Feed bridge deadline fallback
lawrencecchen Jul 17, 2026
4c4bdec
Bound Feed socket waits and cache list ordering
lawrencecchen Jul 17, 2026
dec2d93
Remove duplicate workspace todo project reference
lawrencecchen Jul 17, 2026
f40923e
Merge remote-tracking branch 'origin/main' into task-modernize-feed
lawrencecchen Jul 17, 2026
6574773
Share Feed projections and cancel timed-out ingest
lawrencecchen Jul 17, 2026
611e3b9
Refine Feed component boundaries
lawrencecchen Jul 17, 2026
d3fa8c4
Test Feed reply and hook fallbacks
lawrencecchen Jul 17, 2026
832dd8d
Restore Feed reply and notification fallbacks
lawrencecchen Jul 17, 2026
75d2c3b
Restore Feed editor debug dependency
lawrencecchen Jul 17, 2026
f989061
Test Feed editor scope transitions
lawrencecchen Jul 17, 2026
39348c5
Scope Feed editor blur handling
lawrencecchen Jul 17, 2026
3c85d48
Test Feed timeout and editor layout boundaries
lawrencecchen Jul 17, 2026
73e0dfa
Close Feed timeout and layout races
lawrencecchen Jul 17, 2026
028f4cf
Remove stale Feed beta UI coverage
lawrencecchen Jul 17, 2026
2ae5360
Test Feed routing and history boundaries
lawrencecchen Jul 17, 2026
50a1c2b
Fix Feed review findings and test isolation
lawrencecchen Jul 17, 2026
f36af54
Split Feed waiter completion state
lawrencecchen Jul 17, 2026
63f3705
Test Feed reply delivery failures
lawrencecchen Jul 17, 2026
9083f19
Preserve failed Feed replies
lawrencecchen Jul 17, 2026
3b1869c
Import Feed task identifiers
lawrencecchen Jul 17, 2026
8738372
Test Feed focus pair validation
lawrencecchen Jul 17, 2026
e14b8ab
Validate Feed focus targets before routing
lawrencecchen Jul 17, 2026
c30be07
Merge remote-tracking branch 'origin/main' into task-modernize-feed
lawrencecchen Jul 18, 2026
7301411
Make Feed rollout fail closed in Release
lawrencecchen Jul 18, 2026
a698bfe
Add Feed interaction regression coverage
lawrencecchen Jul 18, 2026
956a371
Fix Feed card interaction and history actions
lawrencecchen Jul 18, 2026
bf414a8
Cover restored Feed jump targets
lawrencecchen Jul 18, 2026
e2b97cc
Resolve restored Feed jump destinations
lawrencecchen Jul 18, 2026
a9864fa
Add Feed retention regression coverage
lawrencecchen Jul 18, 2026
510b4ba
Bound Feed retention to pending decisions
lawrencecchen Jul 18, 2026
58a947f
Cover Feed-only workspace navigation
lawrencecchen Jul 18, 2026
54d808b
Route Feed jumps through live surface focus
lawrencecchen Jul 18, 2026
a3696c4
Route Feed socket jumps through navigation
lawrencecchen Jul 18, 2026
6137e4a
Cover dormant Feed workspace routes
lawrencecchen Jul 18, 2026
9058686
Prefer live Feed surface owners
lawrencecchen Jul 18, 2026
62c014b
Cover Feed jump UI dispatch
lawrencecchen Jul 18, 2026
7e79a5f
Execute Feed jumps on the UI lane
lawrencecchen Jul 18, 2026
911c868
Cover stale Feed workspace identities
lawrencecchen Jul 18, 2026
5513a89
Follow stable Feed surface identities
lawrencecchen Jul 18, 2026
61afec9
Cover Feed hook surface identities
lawrencecchen Jul 18, 2026
898c251
Resolve Feed hook surface identities
lawrencecchen Jul 18, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,131 @@ public struct WorkstreamItem: Identifiable, Codable, Sendable, Equatable {
self.ppid = ppid
}
}

extension WorkstreamItem {
/// Returns a copy whose variable-sized fields have deterministic limits.
/// This bounds both the in-memory ring and the pending-item disk snapshot.
func retainedForFeed() -> WorkstreamItem {
WorkstreamItem(
id: id,
workstreamId: workstreamId.feedPrefix(512),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not truncate protocol identifiers.

This rewrites session, request, question, option, and todo IDs, creating collisions and breaking exact reply routing. It also mismatches the context cache: lookup/removal uses raw event.sessionId, while insertion uses the truncated workstreamId.

Preserve identifiers exactly, or reject over-limit events at the transport boundary.

As per path instructions, correctness-critical identity must use one authoritative structured source and fail closed rather than be rewritten.

Also applies to: 125-125, 132-132, 138-148, 175-175

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamItem.swift`
at line 89, Remove the feedPrefix(512) truncation from WorkstreamItem identity
fields and preserve the original protocol identifiers consistently across
session, request, question, option, todo, and workstream references. Ensure
context-cache insertion, lookup, and removal use the same raw authoritative
identifiers, and reject over-limit events at the transport boundary rather than
rewriting IDs.

Source: Path instructions

source: source,
kind: kind,
createdAt: createdAt,
updatedAt: updatedAt,
cwd: cwd?.feedPrefix(4_096),
title: title?.feedPrefix(1_024),
status: status.retainedForFeed(),
payload: payload.retainedForFeed(),
context: context?.retainedForFeed(),
ppid: ppid
)
}
}

private extension WorkstreamStatus {
func retainedForFeed() -> WorkstreamStatus {
switch self {
case .resolved(.question(let selections), let date):
return .resolved(
.question(selections: selections.prefix(20).map { $0.feedPrefix(1_024) }),
at: date
)
case .resolved(.exitPlan(let mode, let feedback), let date):
return .resolved(.exitPlan(mode, feedback: feedback?.feedPrefix(8_192)), at: date)
default:
return self
}
}
}

private extension WorkstreamPayload {
func retainedForFeed() -> WorkstreamPayload {
switch self {
case .permissionRequest(let requestId, let toolName, let toolInputJSON, let pattern):
return .permissionRequest(
requestId: requestId.feedPrefix(512),
toolName: toolName.feedPrefix(512),
toolInputJSON: toolInputJSON.feedPrefix(32_768),
pattern: pattern?.feedPrefix(4_096)
Comment on lines +123 to +128

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Redact JSON before any destructive truncation.

An over-limit JSON payload becomes invalid before persistence redaction. The redactor then falls back to environment-assignment matching, so JSON secrets such as "api_key":"sk-..." near the beginning can be persisted unredacted.

  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamItem.swift#L123-L128: avoid raw prefix truncation of permission JSON before redaction.
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamItem.swift#L155-L164: apply the same fix to tool-use and tool-result JSON.
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamPersistence.swift#L95-L98: redact the original JSON first, then bound the safe projection; add an over-limit secret regression test.
📍 Affects 2 files
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamItem.swift#L123-L128 (this comment)
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamItem.swift#L155-L164
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamPersistence.swift#L95-L98
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamItem.swift`
around lines 123 - 128, Redact permission JSON before truncating it, so
over-limit payloads remain valid for JSON-aware secret redaction; update the
permissionRequest handling in WorkstreamItem.swift at lines 123-128 accordingly.
Apply the same ordering to tool-use and tool-result JSON at WorkstreamItem.swift
lines 155-164. In WorkstreamPersistence.swift lines 95-98, redact the original
JSON first and then bound the safe result, and add a regression test covering an
over-limit payload containing a secret.

)
case .exitPlan(let requestId, let plan, let defaultMode):
return .exitPlan(
requestId: requestId.feedPrefix(512),
plan: plan.feedPrefix(65_536),
defaultMode: defaultMode
)
case .question(let requestId, let questions):
return .question(
requestId: requestId.feedPrefix(512),
questions: questions.prefix(4).map { question in
WorkstreamQuestionPrompt(
id: question.id.feedPrefix(512),
header: question.header?.feedPrefix(1_024),
prompt: question.prompt.feedPrefix(8_192),
multiSelect: question.multiSelect,
options: question.options.prefix(12).map { option in
WorkstreamQuestionOption(
id: option.id.feedPrefix(512),
label: option.label.feedPrefix(1_024),
description: option.description?.feedPrefix(2_048)
)
}
)
}
)
case .toolUse(let toolName, let toolInputJSON):
return .toolUse(
toolName: toolName.feedPrefix(512),
toolInputJSON: toolInputJSON.feedPrefix(32_768)
)
case .toolResult(let toolName, let resultJSON, let isError):
return .toolResult(
toolName: toolName.feedPrefix(512),
resultJSON: resultJSON.feedPrefix(32_768),
isError: isError
)
case .userPrompt(let text):
return .userPrompt(text: text.feedPrefix(16_384))
case .assistantMessage(let text):
return .assistantMessage(text: text.feedPrefix(16_384))
case .stop(let reason):
return .stop(reason: reason?.feedPrefix(4_096))
case .todos(let todos):
return .todos(todos.prefix(100).map { todo in
WorkstreamTaskTodo(
id: todo.id.feedPrefix(512),
content: todo.content.feedPrefix(2_048),
state: todo.state
)
})
case .sessionStart, .sessionEnd:
return self
}
}
}

extension WorkstreamContext {
func retainedForFeed() -> WorkstreamContext {
WorkstreamContext(
lastUserMessage: lastUserMessage?.feedPrefix(16_384),
assistantPreamble: assistantPreamble?.feedPrefix(16_384),
planSummary: planSummary?.feedPrefix(8_192),
allowedPrompts: allowedPrompts.prefix(20).map { prompt in
WorkstreamAllowedPrompt(
tool: prompt.tool.feedPrefix(512),
prompt: prompt.prompt.feedPrefix(2_048)
)
},
toolSummary: toolSummary?.feedPrefix(8_192),
permissionMode: permissionMode?.feedPrefix(256)
)
}
}

private extension String {
func feedPrefix(_ maximumCharacters: Int) -> String {
guard count > maximumCharacters else { return self }
return String(prefix(maximumCharacters))
}
Comment on lines +204 to +208

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound UTF-8 bytes without scanning the full string.

String.count traverses the entire input, while one extended grapheme can contain unbounded bytes. Agent-controlled fields can therefore bypass the intended memory ceiling and stall MainActor ingestion. Implement a byte-bounded prefix instead.

As per coding guidelines, production hot paths must use bounded construction rather than full-input scans.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/Workstream/WorkstreamItem.swift`
around lines 204 - 208, Update String.feedPrefix(_:) to enforce the limit using
UTF-8 byte capacity rather than String.count, avoiding a full-input scan and
safely handling grapheme boundaries. Construct only the bounded UTF-8 prefix,
ensuring the returned string never exceeds maximumCharacters bytes while
preserving the original string when already within the limit.

Source: Coding guidelines

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import Foundation

/// Classifies a `WorkstreamItem`. Actionable kinds are surfaced in the
/// default Feed view; telemetry kinds are stored but hidden behind the
/// "All" filter toggle.
/// Classifies a `WorkstreamItem`. Actionable kinds enter the Feed. Telemetry
/// kinds can enrich nearby actionable context but are not retained.
public enum WorkstreamKind: String, Codable, Sendable, CaseIterable, Equatable {
// Actionable — shown by default.
case permissionRequest
case exitPlan
case question

// Telemetry — stored, hidden by default.
// Telemetry — transient context only.
case toolUse
case toolResult
case userPrompt
Expand Down
Loading