Skip to content

Release: v2.0.1 - #168

Merged
Minitour merged 24 commits into
mainfrom
develop
Aug 4, 2026
Merged

Release: v2.0.1#168
Minitour merged 24 commits into
mainfrom
develop

Conversation

@Minitour

@Minitour Minitour commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

This release merges 24 commits on develop (including PRs #163#167) with 45 files changed (~2,743 additions, ~159 deletions). The work falls into three areas: agent activity observability, plugin install and skill content, and wrap / activity-ingest reliability (especially Cursor gate hooks and Windows).

Agent activity (feed, ingest, and persistence)

  • Conversation-scoped activity: Provider hook envelopes now drive correlation via configurable hooks.activityCorrelation field maps (no hardcoded provider IDs). Ingest persists conversation_id, generation_id, model, and attributes_json on tool_calls, with a new index for project + conversation queries.
  • Richer metadata: hooks.activityAttributes extracts envelope fields (model, versions, etc.) into bounded JSON for storage and future telemetry.
  • Provider-correct tool results: Post-tool activity reads result fields from provider config (e.g. Claude tool_response, Cursor tool_output) so Claude Code spans show output again.
  • MCP and shell visibility: Capa MCP and capa sh traces inherit the active conversation/generation; afterShell rows remain so Bash/Shell calls appear in the feed.
  • UI grouping: Activity feed nests conversation → generation → spans; provider labels sit on conversation headers; full conversation IDs are shown in headers.
  • Less noise: Dropped beforeFileRead from agent activity (Cursor already records reads via post-tool hooks; duplicate rows and gate risk removed).

Plugin system

Wrap, activity-ingest, and live indicator (#166, #167)

  • Cursor gate hooks: activity-ingest always emits valid allow/continue JSON in a finally block so ingest stays fail-open and does not block gate events with empty stdout.
  • Windows Cursor: Strip UTF-8 BOM from Agent hook stdin; fix conversation id bleed across providers on hook events.
  • Wrap warm start: wrap-ensure-server / warm reuse starts the server when needed; client-connectable origin for wildcard/IPv6 server hosts; test mock changes to avoid CI pollution in unrelated wrap tests.
  • Live indicator: Late SSE subscribers are notified when the shared project EventSource is already open so the activity feed does not stay stuck on “reconnecting” after capabilities sync connects first.

Database / API notes

  • Schema migration (additive): New optional columns on tool_calls: conversation_id, generation_id, model, attributes_json, plus index idx_tool_calls_project_conversation. Existing databases pick these up via ensureColumn.
  • Activity API / web UI: Feed grouping and types updated to match correlated records; see activity-routes, ActivityFeed, and groupActivityRuns.

Test plan

  • Fresh install / upgrade: open an existing project DB and confirm activity ingest still works; verify new tool_calls columns populate during a Cursor or Claude Code session.
  • Activity feed: Run an agent with multiple turns; confirm conversation → generation nesting, provider label on conversation header, MCP/shell spans under the right conversation, and live indicator recovers when opening the project after capabilities sync.
  • Claude Code: Confirm post-tool rows include tool output (not empty spans).
  • Plugins: Install capabilities with multiple plugins where one repo/manifest is invalid; confirm other plugins still resolve and install. Add a Cursor marketplace monorepo plugin with gitPath and confirm manifest/skill resolution under the subpath.
  • Skills UI: Add a plugin without running full provider install; confirm skill markdown loads from unpacked plugin tree; after install, confirm sanitized provider copy wins when applicable.
  • Cursor on Windows (if available): Gate hooks (beforeFileRead / tool gates) still return allow JSON; activity ingest does not block the agent.
  • capa wrap: Cold and warm start both reach a running server; activity ingest reaches the server with expected origin on non-localhost bind addresses.
  • CI: Unit/integration suites for activity ingest, plugin resolve isolation, cursor marketplace adapter, skill-content-plugin, wrap-ensure-server, and activity grouping (already added/extended in this branch).

Included pull requests

PR Theme
#163 Isolate plugin resolve failures
#164 Cursor marketplace monorepo gitPath / entry id
#165 Plugin skill content before capa install
#166 Activity-ingest empty stdout vs Cursor gate hooks
#167 Wrap ensure-server, Windows ingest, live indicator

Minitour and others added 24 commits August 3, 2026 02:58
Per-plugin errors become warnings; healthy plugins still expand into skills/servers for install and the web UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
Clear lock pins, merged expansions, and unpack dirs so a late error cannot leave a half-installed plugin while reporting it as skipped.

Co-authored-by: Cursor <cursoragent@cursor.com>
…lures

fix: isolate plugin resolve failures
Install snippets now pin ::subpath (and optional gitRef) from the marketplace API, and resolvePlugins falls back to finding a nested plugin by id when the repo root only has a catalog.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the full-tree findPluginInDirectory fallback with a direct-child and marketplace-catalog resolve, and fix the adapter test import for tsc.

Co-authored-by: Cursor <cursoragent@cursor.com>
…subpath

fix: resolve Cursor marketplace monorepo plugins
Read SKILL.md from the unpacked plugin tree under ~/.capa/plugins so the UI can show content as soon as a plugin is added, without requiring install to materialize provider skill dirs.

Co-authored-by: Cursor <cursoragent@cursor.com>
Prefer provider-installed skill copies over the unpacked plugin tree so post-install sanitized content wins, and cache plugin skill directory scans by mtime to avoid repeated walks.

Co-authored-by: Cursor <cursoragent@cursor.com>
…e-install

Fix plugin skill content loading before capa install
beforeFileRead and other gate events require valid allow/continue JSON; emit it in a finally so ingest stays fail-open without failing closed.

Co-authored-by: Cursor <cursoragent@cursor.com>
Cursor already records reads via afterTool/postToolUse; the beforeReadFile hook only duplicated them as kind file and risked gate failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add hooks.activityCorrelation field maps per provider so ingest extracts chat/turn ids without hardcoding, persist them on tool_calls, and nest the Activity feed conversation → generation → spans.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add hooks.activityAttributes field maps so model, model_id, versions, and related metadata are stored on tool_calls without provider-specific hardcoding.

Co-authored-by: Cursor <cursoragent@cursor.com>
Read PostToolUse results from provider-configured fields (Claude tool_response, Cursor tool_output) instead of assuming Cursor-only names.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep generation rows focused on the turn; show the provider once per conversation.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Inherit conversation/generation on MCP traces and keep afterShell rows for capa sh so Bash/Shell calls are visible in the activity feed.

Co-authored-by: Cursor <cursoragent@cursor.com>
Apply Biome format/import fixes across activity files and keep activity-ingest fail-open when stdout is broken.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Fix activity-ingest empty stdout blocking Cursor gate hooks
Ensure capa wrap starts the server on warm reuse, strip UTF-8 BOM from Cursor Agent hook stdin on Windows, and stop provider hook events from inheriting another provider's conversation ids.

Co-authored-by: Cursor <cursoragent@cursor.com>
Notify late SSE subscribers when the shared project EventSource is already open so the activity feed does not stay offline after capabilities sync connects first.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace wrapCommand module mocks that broke unrelated wrap tests, and build a client-connectable origin for wildcard/IPv6 server hosts.

Co-authored-by: Cursor <cursoragent@cursor.com>
Fix wrap server start, Windows Cursor activity ingest, and live indicator
Copilot AI lite review requested due to automatic review settings August 4, 2026 07:31
@Minitour Minitour changed the title Develop Release: v2.0.1 Aug 4, 2026
) {
return raw;
}
if (Array.isArray(raw) || (typeof raw === "object" && raw !== null)) {
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

v2.0.1: Correlated activity feed, resilient plugins, and wrap/ingest fixes

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Persist provider conversation/generation/model metadata to improve activity feed grouping.
• Make activity-ingest fail-open for gate hooks; fix Windows BOM and connect origins.
• Harden plugin resolution and skill content loading, including Cursor monorepo installs.
Diagram

graph TD
  Hooks{{"Provider hooks"}} --> Ingest["activity-ingest CLI"] --> Routes["Activity API"] --> Tracer["ToolCallTracer"] --> DB[("tool_calls")]
  Tracer --> SSE["Project SSE"] --> UI["Web ActivityFeed"]

  subgraph Legend
    direction LR
    _ext{{"External"}} ~~~ _proc["Process/Service"] ~~~ _db[("Database")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Separate activity correlation table
  • ➕ Avoids widening tool_calls and keeps tool_calls focused on spans only
  • ➕ Enables different indexing/retention for correlation metadata
  • ➖ Adds join complexity to feed queries and SSE payload shaping
  • ➖ More migration/code paths vs. additive nullable columns
2. Require explicit monorepo pinning only (::subpath/@id) and fail otherwise
  • ➕ Simpler resolver; fewer heuristics and fewer edge-cases
  • ➕ Clearer user intent reduces accidental matches
  • ➖ Worse UX for Cursor marketplace installs where listings already carry gitPath/id
  • ➖ Breaks partial marketplace repos that only expose root catalogs

Recommendation: The PR’s approach (additive nullable columns + provider-configured field maps + bounded monorepo lookup) is a pragmatic balance: it improves observability and UX without introducing heavy new persistence models or expensive repository scans. If feed query performance becomes a bottleneck, consider a follow-up to formalize conversation/generation entities (or a dedicated correlation table), but the current schema change is a reasonable incremental step.

Files changed (45) +2743 / -159

Enhancement (26) +1146 / -92
adapter.tsSupport monorepo listings via gitPath and optional gitRef pins +52/-7

Support monorepo listings via gitPath and optional gitRef pins

• Extends marketplace plugin metadata to include gitPath/gitRef. Reworks pluginDef to validate/normalize subpaths, produce capa ::subpath repo specs, and pass through optional ref pins for deterministic installs.

registries/cursor-marketplace/adapter.ts

wrap-ensure-server.tsAdd helper to ensure server is running before wrap +13/-0

Add helper to ensure server is running before wrap

• Introduces ensureWrapServerRunning, which calls ensureServer(VERSION) and fails fast when the server cannot be started. Intended to cover warm wrap flows that skip install.

src/cli/commands/wrap-ensure-server.ts

database.tsExpose latest activity correlation lookup on the database facade +8/-0

Expose latest activity correlation lookup on the database facade

• Adds findLatestActivityCorrelation to CapaDatabase, delegating to ToolCallsRepo. Used by ToolCallTracer to inherit conversation/generation for MCP and shell spans.

src/db/database.ts

schema.tsAdd tool_calls correlation/metadata columns and conversation index +12/-0

Add tool_calls correlation/metadata columns and conversation index

• Extends tool_calls schema with conversation_id, generation_id, model, and attributes_json, and ensures columns are added on existing DBs. Adds idx_tool_calls_project_conversation to optimize project + conversation queries.

src/db/schema.ts

tool-calls.tsPersist conversation/generation/model/attributes and improve grouping expansion +108/-12

Persist conversation/generation/model/attributes and improve grouping expansion

• Updates ToolCallInsert and insert statements to include new metadata columns. Adds findLatestCorrelation for correlation inheritance and improves older-page expansion to prefer generation_id boundaries when present (fallback remains heuristic).

src/db/tool-calls.ts

activity-routes.tsAccept and store correlation/attributes on activity ingest events +33/-7

Accept and store correlation/attributes on activity ingest events

• Extends ActivityIngestBody to accept conversationId, generationId, model, and attributes. Trims and serializes these fields into tool_calls via ToolCallTracer while explicitly disabling correlation inheritance for hook-ingested rows.

src/server/activity-routes.ts

mcp-meta-routes.tsPass projectId into skill content resolution options +1/-0

Pass projectId into skill content resolution options

• Updates handleGetSkillContent to provide projectId so resolveSkillContentById can locate unpacked plugin trees under the per-project plugins directory.

src/server/mcp-meta-routes.ts

project-routes.tsResolve skill descriptions using project-scoped plugin unpack paths +1/-0

Resolve skill descriptions using project-scoped plugin unpack paths

• Passes projectId through to resolveSkillDescription to allow pre-install plugin skill metadata (frontmatter) to be shown in project responses.

src/server/project-routes.ts

skill-content.tsServe SKILL.md from unpacked plugin trees before provider install +136/-4

Serve SKILL.md from unpacked plugin trees before provider install

• Adds SkillContentResolveOptions and a plugin unpack resolver that indexes SKILL.md locations (skills/, .capa-commands/, then cached scan) under ~/.capa/plugins/<projectId>/. Prefers installed provider copies when present, then falls back to the unpacked plugin tree.

src/server/skill-content.ts

tool-call-tracer.tsAdd correlation fields and inherit latest conversation/generation when appropriate +30/-0

Add correlation fields and inherit latest conversation/generation when appropriate

• Extends ToolCallStartInput to include conversationId/generationId/model/attributesJson and adds inheritCorrelation (default true). When ids are missing, ToolCallTracer can inherit the latest provider correlation to group MCP/shell spans with the active agent turn.

src/server/tool-call-tracer.ts

activity-attributes.tsAdd provider-configured attribute extraction with size-bounded serialization +116/-0

Add provider-configured attribute extraction with size-bounded serialization

• Introduces extractActivityAttributes driven by provider hook configuration, plus serializeActivityAttributes with a max JSON char cap to keep stored metadata bounded.

src/shared/activity-attributes.ts

activity-correlation.tsAdd provider-configured conversation/generation correlation extraction +52/-0

Add provider-configured conversation/generation correlation extraction

• Introduces extractActivityCorrelation to resolve conversationId/generationId from hook payloads using provider-declared field maps, eliminating provider-specific hardcoding in ingest/normalize paths.

src/shared/activity-correlation.ts

activity-result.tsAdd provider-configured result field extraction for after* events +37/-0

Add provider-configured result field extraction for after* events

• Introduces extractActivityResult that reads the tool/shell output field(s) based on provider configuration, restoring correct result previews across providers with differing payload shapes.

src/shared/activity-result.ts

agent-activity-normalize.tsNormalize correlation/attributes/results and reduce before-hook noise +66/-37

Normalize correlation/attributes/results and reduce before-hook noise

• Extends normalization to extract conversation/generation ids, model, and attribute bags via provider config. Uses provider-declared result fields for resultPreview, skips beforeFileRead events, and keeps afterShell rows (including capa sh) for agent-visible command spans.

src/shared/agent-activity-normalize.ts

agent-activity.tsStop installing before* system activity hooks (including beforeFileRead) +5/-7

Stop installing before* system activity hooks (including beforeFileRead)

• Updates the system activity hook set to omit all before* hooks to reduce duplicate/noisy rows and gate risk. Clarifies that reads are covered via afterTool and edits via afterFileEdit.

src/shared/agent-activity.ts

lockfile.tsAdd lockfile removal hook for failed plugin resolve rollback +5/-0

Add lockfile removal hook for failed plugin resolve rollback

• Adds LockfileBuilder.removePlugin so failed plugin resolves can undo pins after an upsert has already occurred, supporting per-plugin isolation with clean state.

src/shared/lockfile.ts

detect.tsAdd bounded nested plugin resolution by id for marketplace monorepos +82/-0

Add bounded nested plugin resolution by id for marketplace monorepos

• Introduces resolveNestedPluginById which avoids full-tree walks by checking direct child paths and root marketplace catalogs (Cursor/Claude) to map plugin name→source. Returns a discovered entry + parsed manifest when found.

src/shared/plugin-manifest/detect.ts

index.tsExport nested plugin id resolution helper +1/-0

Export nested plugin id resolution helper

• Exports resolveNestedPluginById from the plugin-manifest module index so CLI/plugin resolution can use the bounded monorepo lookup.

src/shared/plugin-manifest/index.ts

claude-code.tsDeclare activity correlation/attributes/result fields for Claude Code hooks +22/-0

Declare activity correlation/attributes/result fields for Claude Code hooks

• Adds provider hook config for correlation ids (session_id/prompt_id), metadata attributes extraction, and tool result field priority (tool_response first). Enables provider-correct normalization without hardcoding.

src/shared/providers/entries/claude-code.ts

codex.tsDeclare activity correlation/attributes/result fields for Codex hooks +15/-0

Declare activity correlation/attributes/result fields for Codex hooks

• Adds hook configuration mirroring Claude-style payloads (session_id/prompt_id) plus a small attribute allowlist and result field precedence to support consistent activity ingest/normalization.

src/shared/providers/entries/codex.ts

cursor.tsDeclare activity correlation/attributes/result fields for Cursor hooks +44/-0

Declare activity correlation/attributes/result fields for Cursor hooks

• Adds Cursor hook config for correlation ids (conversation_id/generation_id), a detailed attribute allowlist (model/version/etc.), and result field precedence (tool_output/output). Improves grouping and restores result previews for Cursor activity.

src/shared/providers/entries/cursor.ts

database.tsExtend ToolCallRecord with correlation and metadata fields +8/-0

Extend ToolCallRecord with correlation and metadata fields

• Adds conversation_id, generation_id, model, and attributes_json to the ToolCallRecord type so server and UI can handle correlated activity grouping and display richer metadata.

src/types/database.ts

providers.tsAdd provider hook configuration types for correlation, attributes, and results +64/-0

Add provider hook configuration types for correlation, attributes, and results

• Introduces ActivityCorrelationIntegration, ActivityAttributesIntegration, and activityResultFields on HooksIntegration. Establishes a data-driven contract for provider-specific hook payload parsing.

src/types/providers.ts

ActivityFeed.tsxRender activity grouped by conversation → generation, with conversation headers +69/-14

Render activity grouped by conversation → generation, with conversation headers

• Switches feed grouping from flat runs to conversation blocks with nested generation rows. Moves provider labels onto conversation headers, shows full conversation id in the header, and adjusts row padding for nested generations.

web-ui/src/features/projects/components/activity/ActivityFeed.tsx

groupActivityRuns.tsGroup feed by correlated conversations and generations with heuristic fallback +162/-4

Group feed by correlated conversations and generations with heuristic fallback

• Introduces groupActivityConversations to first group by conversation_id then generation_id, with fallback to the legacy prompt/stop heuristic for uncorrelated rows. Keeps groupActivityRuns as a flattening helper and adds conversation-level aggregation metadata.

web-ui/src/features/projects/components/activity/groupActivityRuns.ts

api.tsExtend web ToolCallRecord API type with correlation and metadata fields +4/-0

Extend web ToolCallRecord API type with correlation and metadata fields

• Adds conversation_id, generation_id, model, and attributes_json to the UI-facing ToolCallRecord type, enabling correlated grouping and richer display payloads.

web-ui/src/types/api.ts

Bug fix (4) +255 / -53
activity-ingest.tsFail-open gate hook stdout, BOM-safe stdin parse, and origin normalization +107/-12

Fail-open gate hook stdout, BOM-safe stdin parse, and origin normalization

• Adds explicit gate hook stdout JSON emission in a finally block to prevent Cursor gate events from blocking on empty stdout. Introduces parseHookStdinJson to strip BOMs (notably on Windows) and clientConnectOrigin to produce client-routable origins when server binds to wildcards/IPv6.

src/cli/commands/activity-ingest.ts

plugin-install.tsIsolate plugin resolution failures and add bounded monorepo id fallback +113/-40

Isolate plugin resolution failures and add bounded monorepo id fallback

• Wraps per-plugin resolve in a try/catch so failures become warnings instead of aborting the entire merge. Adds bounded nested-plugin lookup by entry id for marketplace-style monorepos, and ensures partial state (lock pins, merged expansions, unpack dirs) is rolled back on late failures.

src/cli/commands/plugin-install.ts

wrap.tsEnsure server availability for warm wrap sessions +10/-0

Ensure server availability for warm wrap sessions

• Invokes ensureWrapServerRunning early in wrapCommand so warm-start reuse still has a running server for MCP tools, capa sh, and activity hooks. Exits with an error message when the server cannot be started.

src/cli/commands/wrap.ts

project-events.tsNotify late SSE subscribers when shared EventSource is already open +25/-1

Notify late SSE subscribers when shared EventSource is already open

• Adds a test-only reset helper and updates subscribeProjectEvents to queue an onOpen callback when subscribing to an already-open shared EventSource. Prevents the UI from being stuck in “reconnecting” after other features open the socket first.

web-ui/src/features/projects/project-events.ts

Tests (14) +1341 / -14
adapter.test.tsAdd tests for Cursor marketplace gitUrl/gitPath/gitRef mapping +40/-0

Add tests for Cursor marketplace gitUrl/gitPath/gitRef mapping

• Introduces a new bun:test suite covering pluginDef URL parsing for GitHub/GitLab, monorepo ::subpath pinning, path normalization, traversal rejection, and ref pinning.

registries/cursor-marketplace/adapter.test.ts

activity-ingest.test.tsTest gate stdout, BOM-stripping stdin parse, and connect-origin normalization +139/-0

Test gate stdout, BOM-stripping stdin parse, and connect-origin normalization

• Adds unit tests for gate hook stdout JSON behavior, Windows UTF-8 BOM stripping for hook stdin, and clientConnectOrigin host handling (wildcards and IPv6). Includes an integration-style check that stdout remains valid JSON even on failures.

src/cli/commands/tests/activity-ingest.test.ts

plugin-monorepo-id-fallback.test.tsTest resolving nested plugins via marketplace catalog/id fallback +109/-0

Test resolving nested plugins via marketplace catalog/id fallback

• Adds a fixture monorepo snapshot with a root marketplace.json and a nested plugin manifest. Verifies resolvePlugins can locate the nested plugin by entry id when the repo string lacks ::subpath/@id pinning.

src/cli/commands/tests/plugin-monorepo-id-fallback.test.ts

plugin-resolve-isolate.test.tsTest per-plugin failure isolation and rollback during resolve +275/-0

Test per-plugin failure isolation and rollback during resolve

• Introduces tests ensuring one plugin’s missing manifest or clone failure doesn’t wipe other plugins’ expansions. Also verifies late failures roll back lock pins, merged capability expansions, and any partially materialized plugin directory.

src/cli/commands/tests/plugin-resolve-isolate.test.ts

wrap-ensure-server.test.tsTest wrap server ensure behavior via server-manager mocks +42/-0

Test wrap server ensure behavior via server-manager mocks

• Adds tests for ensureWrapServerRunning using module mocking of server-manager. Covers both the running-server success case and the failure-to-start error path.

src/cli/commands/tests/wrap-ensure-server.test.ts

activity-routes.test.tsTest persistence of conversation/generation/model/attributes and provider isolation +78/-0

Test persistence of conversation/generation/model/attributes and provider isolation

• Adds coverage that handlePostProjectActivityEvent stores correlation ids and attributes_json. Verifies missing ids do not inherit a previous provider’s conversation, preventing cross-provider bleed.

src/server/tests/activity-routes.test.ts

skill-content-plugin.test.tsTest skill content fallback to unpacked plugin trees pre-install +176/-0

Test skill content fallback to unpacked plugin trees pre-install

• Adds tests verifying resolveSkillContent and related helpers can serve SKILL.md from ~/.capa/plugins/<projectId>/<pluginId> without provider install. Confirms installed provider copies take precedence and that nested layouts are discoverable via a cached scan.

src/server/tests/skill-content-plugin.test.ts

tool-call-tracer.test.tsTest correlation inheritance behavior in ToolCallTracer +115/-0

Test correlation inheritance behavior in ToolCallTracer

• Adds tests confirming MCP/shell tool calls inherit the latest activity correlation when enabled. Also verifies inheritCorrelation=false prevents adoption, and explicit ids are never overridden.

src/server/tests/tool-call-tracer.test.ts

activity-attributes.test.tsTest provider-configured activity attributes extraction and serialization +63/-0

Test provider-configured activity attributes extraction and serialization

• Adds tests for Cursor/Claude attribute extraction (allowlisted envelope keys) and validates serialization behavior, including null for empty bags and JSON round-trip for small sets.

src/shared/tests/activity-attributes.test.ts

activity-correlation.test.tsTest provider-configured correlation id extraction +46/-0

Test provider-configured correlation id extraction

• Adds tests ensuring correlation ids are pulled from Cursor and Claude hook payloads via provider config, and that missing/unknown providers safely return null ids.

src/shared/tests/activity-correlation.test.ts

activity-result.test.tsTest provider-configured tool result extraction and normalization +71/-0

Test provider-configured tool result extraction and normalization

• Adds tests that extractActivityResult reads Claude tool_response and Cursor tool_output, and that normalization stores Claude post-tool results as resultPreview for afterTool/afterShell events.

src/shared/tests/activity-result.test.ts

agent-activity.test.tsUpdate activity hook selection and normalization expectations +42/-13

Update activity hook selection and normalization expectations

• Adjusts tests to reflect dropping beforeFileRead system hooks and to validate correlation/model/attribute extraction during normalization. Updates behavior to keep capa sh afterShell rows and detect SKILL.md reads via afterTool Read payloads.

src/shared/tests/agent-activity.test.ts

project-events.test.tsTest late subscriber onOpen notification for shared EventSource sockets +84/-0

Test late subscriber onOpen notification for shared EventSource sockets

• Adds tests for subscribeProjectEvents ensuring late subscribers get an onOpen callback when the shared socket is already OPEN, and that unsubscribing immediately prevents spurious callbacks.

web-ui/src/features/projects/tests/project-events.test.ts

groupActivityRuns.test.tsAdd tests for conversation/generation grouping using correlation ids +61/-1

Add tests for conversation/generation grouping using correlation ids

• Updates the ToolCallRecord test helper to include new fields and adds coverage validating grouping into conversations and generations when conversation_id/generation_id are present.

web-ui/src/features/projects/components/activity/groupActivityRuns.test.ts

Documentation (1) +1 / -0
resolve-effective-capabilities.tsDocument warning-based behavior for individual plugin failures +1/-0

Document warning-based behavior for individual plugin failures

• Updates inline documentation to clarify that plugin resolve failures are collected as warnings and do not prevent healthy plugins from expanding.

src/server/resolve-effective-capabilities.ts

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Truncated attributes break JSON 🐞 Bug ≡ Correctness
Description
serializeActivityAttributes() slices the serialized JSON string and appends an ellipsis when over
the size cap, which can produce non-JSON text stored in tool_calls.attributes_json. Any consumer
that JSON.parse()s attributes_json will throw once a large enough attribute bag is ingested.
Code

src/shared/activity-attributes.ts[R67-70]

+	try {
+		const json = JSON.stringify(attributes);
+		if (json.length <= ACTIVITY_ATTRIBUTES_MAX_JSON_CHARS) return json;
+		return `${json.slice(0, ACTIVITY_ATTRIBUTES_MAX_JSON_CHARS - 1)}…`;
Evidence
The serializer currently returns a sliced JSON string plus an ellipsis, which is not guaranteed to
remain syntactically valid JSON; callers already assume parseable JSON for persisted
attributes_json.

src/shared/activity-attributes.ts[63-71]
src/server/tests/activity-routes.test.ts[112-145]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`serializeActivityAttributes()` enforces a size cap by truncating the *serialized JSON string* and appending `…`, which can make the stored value invalid JSON.

## Issue Context
`attributes_json` is treated as JSON elsewhere (e.g. tests parse it), so the serializer must return either **valid JSON** or **null**.

## Fix Focus Areas
- src/shared/activity-attributes.ts[63-73]
- src/shared/__tests__/activity-attributes.test.ts[1-63]

## Suggested fix
- Change `serializeActivityAttributes()` to guarantee valid JSON:
 - Option A (simplest): if JSON exceeds the cap, return `null`.
 - Option B (preferred): truncate at the **attribute/value** level while keeping JSON valid (e.g., drop lowest-priority keys, or truncate long string values, then `JSON.stringify()` again until under the cap).
- Add a regression test that passes an attributes object whose serialized size exceeds `ACTIVITY_ATTRIBUTES_MAX_JSON_CHARS` and asserts the result is either `null` or `JSON.parse(result)` succeeds.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Plugin fallback guard wrong 🐞 Bug ≡ Correctness
Description
resolvePluginSkillContent() uses skill.type !== "plugin" && !skill.sourcePlugin as its
early-return guard, so non-plugin skill types that still carry sourcePlugin metadata can flow into
the unpacked-plugin resolution path. With inconsistent/stale capability data, resolveSkillContent()
can return SKILL.md content from the unpacked plugin tree instead of the skill's declared source.
Code

src/server/skill-content.ts[R233-236]

+): SkillContentResult | null {
+	if (skill.type !== "plugin" && !skill.sourcePlugin) return null;
+	const pluginId = skill.sourcePlugin?.id;
+	if (
Evidence
The guard in resolvePluginSkillContent() is permissive, and the Skill type definition explicitly
allows sourcePlugin for any skill.type, enabling the inconsistent metadata case.

src/server/skill-content.ts[230-258]
src/types/capabilities.ts[284-289]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`resolvePluginSkillContent()` is intended as a pre-install fallback for **plugin** skills, but its guard condition allows the code path to run for non-plugin skills whenever `sourcePlugin` is present.

## Issue Context
`Skill.sourcePlugin` is optional on the `Skill` interface for any skill type; relying on it alone can accidentally classify a non-plugin skill as plugin-backed.

## Fix Focus Areas
- src/server/skill-content.ts[230-242]
- src/types/capabilities.ts[284-289]

## Suggested fix
- Tighten the guard to only allow plugin skills, e.g.:
 - `if (skill.type !== "plugin" || !skill.sourcePlugin) return null;`
- (Optional) Add a unit test that constructs a non-`plugin` Skill with `sourcePlugin` set and asserts `resolveSkillContent()` does **not** resolve via the unpacked plugin tree.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Generation id not namespaced 🐞 Bug ≡ Correctness
Description
For correlated activity, ActivityRun.id is set to generationId, but ActivityFeed selects the run via
runs.find(r => r.id === selectedId) across all conversations. If two conversations reuse the same
generation_id value, the dialog can open the wrong run because the id is not namespaced by
conversation.
Code

web-ui/src/features/projects/components/activity/groupActivityRuns.ts[R204-206]

+  const run: ActivityRun = {
+    id: generationId || first.id,
+    conversationId,
Evidence
The grouping code assigns the run id from generationId, while the feed dialog selection searches by
id across all conversations' runs, making duplicate ids ambiguous.

web-ui/src/features/projects/components/activity/groupActivityRuns.ts[194-218]
web-ui/src/features/projects/components/activity/ActivityFeed.tsx[149-163]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Correlated runs use `generation_id` as the run's identifier, but the UI selects a run by `id` alone across the flattened run list. Duplicate generation ids across conversations make selection ambiguous.

## Issue Context
The UI flattens all conversations into a single `runs` array and does a first-match lookup by id.

## Fix Focus Areas
- web-ui/src/features/projects/components/activity/groupActivityRuns.ts[194-218]
- web-ui/src/features/projects/components/activity/ActivityFeed.tsx[149-163]
- web-ui/src/features/projects/components/activity/groupActivityRuns.test.ts[98-152]

## Suggested fix
- Make `ActivityRun.id` globally unique by namespacing, e.g. `id: `${conversationId}:${generationId}``.
- Keep `conversationId`/`generationId` fields as-is for display/correlation.
- Update `ActivityFeed` selection to use the new composite id.
- Add a test with two different `conversation_id` values sharing the same `generation_id` and assert selecting one opens the correct run.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +67 to +70
try {
const json = JSON.stringify(attributes);
if (json.length <= ACTIVITY_ATTRIBUTES_MAX_JSON_CHARS) return json;
return `${json.slice(0, ACTIVITY_ATTRIBUTES_MAX_JSON_CHARS - 1)}…`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Truncated attributes break json 🐞 Bug ≡ Correctness

serializeActivityAttributes() slices the serialized JSON string and appends an ellipsis when over
the size cap, which can produce non-JSON text stored in tool_calls.attributes_json. Any consumer
that JSON.parse()s attributes_json will throw once a large enough attribute bag is ingested.
Agent Prompt
## Issue description
`serializeActivityAttributes()` enforces a size cap by truncating the *serialized JSON string* and appending `…`, which can make the stored value invalid JSON.

## Issue Context
`attributes_json` is treated as JSON elsewhere (e.g. tests parse it), so the serializer must return either **valid JSON** or **null**.

## Fix Focus Areas
- src/shared/activity-attributes.ts[63-73]
- src/shared/__tests__/activity-attributes.test.ts[1-63]

## Suggested fix
- Change `serializeActivityAttributes()` to guarantee valid JSON:
  - Option A (simplest): if JSON exceeds the cap, return `null`.
  - Option B (preferred): truncate at the **attribute/value** level while keeping JSON valid (e.g., drop lowest-priority keys, or truncate long string values, then `JSON.stringify()` again until under the cap).
- Add a regression test that passes an attributes object whose serialized size exceeds `ACTIVITY_ATTRIBUTES_MAX_JSON_CHARS` and asserts the result is either `null` or `JSON.parse(result)` succeeds.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +233 to +236
): SkillContentResult | null {
if (skill.type !== "plugin" && !skill.sourcePlugin) return null;
const pluginId = skill.sourcePlugin?.id;
if (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Plugin fallback guard wrong 🐞 Bug ≡ Correctness

resolvePluginSkillContent() uses skill.type !== "plugin" && !skill.sourcePlugin as its
early-return guard, so non-plugin skill types that still carry sourcePlugin metadata can flow into
the unpacked-plugin resolution path. With inconsistent/stale capability data, resolveSkillContent()
can return SKILL.md content from the unpacked plugin tree instead of the skill's declared source.
Agent Prompt
## Issue description
`resolvePluginSkillContent()` is intended as a pre-install fallback for **plugin** skills, but its guard condition allows the code path to run for non-plugin skills whenever `sourcePlugin` is present.

## Issue Context
`Skill.sourcePlugin` is optional on the `Skill` interface for any skill type; relying on it alone can accidentally classify a non-plugin skill as plugin-backed.

## Fix Focus Areas
- src/server/skill-content.ts[230-242]
- src/types/capabilities.ts[284-289]

## Suggested fix
- Tighten the guard to only allow plugin skills, e.g.:
  - `if (skill.type !== "plugin" || !skill.sourcePlugin) return null;`
- (Optional) Add a unit test that constructs a non-`plugin` Skill with `sourcePlugin` set and asserts `resolveSkillContent()` does **not** resolve via the unpacked plugin tree.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +204 to +206
const run: ActivityRun = {
id: generationId || first.id,
conversationId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

3. Generation id not namespaced 🐞 Bug ≡ Correctness

For correlated activity, ActivityRun.id is set to generationId, but ActivityFeed selects the run via
runs.find(r => r.id === selectedId) across all conversations. If two conversations reuse the same
generation_id value, the dialog can open the wrong run because the id is not namespaced by
conversation.
Agent Prompt
## Issue description
Correlated runs use `generation_id` as the run's identifier, but the UI selects a run by `id` alone across the flattened run list. Duplicate generation ids across conversations make selection ambiguous.

## Issue Context
The UI flattens all conversations into a single `runs` array and does a first-match lookup by id.

## Fix Focus Areas
- web-ui/src/features/projects/components/activity/groupActivityRuns.ts[194-218]
- web-ui/src/features/projects/components/activity/ActivityFeed.tsx[149-163]
- web-ui/src/features/projects/components/activity/groupActivityRuns.test.ts[98-152]

## Suggested fix
- Make `ActivityRun.id` globally unique by namespacing, e.g. `id: `${conversationId}:${generationId}``.
- Keep `conversationId`/`generationId` fields as-is for display/correlation.
- Update `ActivityFeed` selection to use the new composite id.
- Add a test with two different `conversation_id` values sharing the same `generation_id` and assert selecting one opens the correct run.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This release PR extends capa’s observability and reliability across agent activity ingestion, plugin resolution/installation (including Cursor marketplace monorepos), and wrap session startup—plus corresponding server/UI updates to persist and present correlated activity (conversation → generation → spans).

Changes:

  • Add provider-configurable activity correlation/attributes/result extraction, persist those fields on tool_calls, and update the web UI to group activity by conversation/generation.
  • Improve plugin robustness: isolate per-plugin failures, support monorepo gitPath installs + bounded nested-plugin lookup by entry id, and allow SKILL.md content to be served pre-install from unpacked plugin trees.
  • Improve wrap/activity ingest reliability: always emit valid gate JSON on stdout, strip BOM on Windows hook stdin, ensure wrap warm-starts have a running server, and fix late SSE subscriber “reconnecting” state.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web-ui/src/types/api.ts Extends ToolCallRecord API typing with correlation/attribute fields.
web-ui/src/features/projects/project-events.ts Adds test reset helper and late-subscriber EventSource.OPEN notification.
web-ui/src/features/projects/components/activity/groupActivityRuns.ts Introduces conversation/generation grouping and labels for activity feed.
web-ui/src/features/projects/components/activity/groupActivityRuns.test.ts Adds coverage for conversation/generation grouping behavior.
web-ui/src/features/projects/components/activity/ActivityFeed.tsx Updates UI to render conversation headers and nested generations.
web-ui/src/features/projects/tests/project-events.test.ts Adds tests for late-subscriber open notifications and reset helper.
src/types/providers.ts Adds provider hook integration types for correlation/attributes/result fields.
src/types/database.ts Extends DB ToolCallRecord shape with new persisted fields.
src/shared/providers/entries/cursor.ts Declares Cursor correlation/attributes/result field mappings.
src/shared/providers/entries/codex.ts Declares Codex correlation/attributes/result field mappings (Claude-like).
src/shared/providers/entries/claude-code.ts Declares Claude Code correlation/attributes/result field mappings.
src/shared/plugin-manifest/index.ts Re-exports new nested-plugin resolver helper.
src/shared/plugin-manifest/detect.ts Adds bounded nested-plugin lookup by entry id for monorepos.
src/shared/lockfile.ts Adds ability to remove a plugin pin after partial resolve failure.
src/shared/agent-activity.ts Adjusts system activity events to omit all before* hooks.
src/shared/agent-activity-normalize.ts Normalizes correlation/attributes/result preview and updated skip logic.
src/shared/activity-result.ts Implements provider-configured result extraction from hook stdin.
src/shared/activity-correlation.ts Implements provider-configured conversation/generation id extraction.
src/shared/activity-attributes.ts Implements provider-configured attribute extraction and serialization.
src/shared/tests/agent-activity.test.ts Updates tests for new normalization behavior and hook selection.
src/shared/tests/activity-result.test.ts Adds tests for provider-configured result extraction.
src/shared/tests/activity-correlation.test.ts Adds tests for provider-configured correlation extraction.
src/shared/tests/activity-attributes.test.ts Adds tests for provider-configured attribute extraction/serialization.
src/server/tool-call-tracer.ts Persists correlation/attributes and optionally inherits correlation for MCP/shell traces.
src/server/skill-content.ts Adds pre-install plugin skill content resolution from unpacked plugin trees.
src/server/resolve-effective-capabilities.ts Documents per-plugin failure isolation behavior.
src/server/project-routes.ts Threads projectId into skill description resolution for plugin unpack fallback.
src/server/mcp-meta-routes.ts Threads projectId into skill content resolution for plugin unpack fallback.
src/server/activity-routes.ts Accepts/persists correlation/model/attributes and disables correlation inheritance for hook ingest.
src/server/tests/tool-call-tracer.test.ts Adds tests for correlation inheritance behavior and explicit override behavior.
src/server/tests/skill-content-plugin.test.ts Adds tests for plugin unpack skill content/description and precedence rules.
src/server/tests/activity-routes.test.ts Adds tests for persisting correlation/model/attributes and no cross-provider inheritance.
src/db/tool-calls.ts Extends inserts and adds correlation lookup + generation-aware page expansion.
src/db/schema.ts Adds new columns and project+conversation index.
src/db/database.ts Exposes latest-activity correlation lookup through DB facade.
src/cli/commands/wrap.ts Ensures server is running for warm wrap sessions.
src/cli/commands/wrap-ensure-server.ts Adds helper to start/verify server for wrap.
src/cli/commands/plugin-install.ts Isolates per-plugin failures, adds monorepo id fallback, and rolls back partial plugin state.
src/cli/commands/activity-ingest.ts Ensures gate stdout JSON, strips BOM, uses client-connectable origin, posts correlation/attributes.
src/cli/commands/tests/wrap-ensure-server.test.ts Adds tests for wrap server ensure helper.
src/cli/commands/tests/plugin-resolve-isolate.test.ts Adds tests ensuring one bad plugin doesn’t wipe others and partial rollback works.
src/cli/commands/tests/plugin-monorepo-id-fallback.test.ts Adds tests for nested plugin resolution by entry id.
src/cli/commands/tests/activity-ingest.test.ts Adds tests for gate stdout behavior, BOM stripping, and origin building.
registries/cursor-marketplace/adapter.ts Supports gitPath/gitRef and emits ::subpath installs with traversal rejection.
registries/cursor-marketplace/adapter.test.ts Adds tests for gitPath/gitRef parsing and traversal rejection.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +63 to +74
export function serializeActivityAttributes(
attributes: ActivityAttributes,
): string | null {
if (!attributes || Object.keys(attributes).length === 0) return null;
try {
const json = JSON.stringify(attributes);
if (json.length <= ACTIVITY_ATTRIBUTES_MAX_JSON_CHARS) return json;
return `${json.slice(0, ACTIVITY_ATTRIBUTES_MAX_JSON_CHARS - 1)}…`;
} catch {
return null;
}
}
expect(result).not.toBeNull();
expect(result!.content).toContain("Cmd body.");
});
it("prefers installed provider copy over unpacked plugin tree after install", () => {
Comment on lines +438 to +451
const pluginRoot = join(repoRoot, cleaned);
if (!existsSync(pluginRoot)) return null;
const manifest = detectAndParseManifest(pluginRoot, preferredProviders);
if (!manifest) return null;
const dirName = cleaned.split("/").filter(Boolean).pop() ?? cleaned;
return {
entry: {
subpath: cleaned,
manifestName: manifest.name || dirName,
dirName,
manifestFile: "",
},
manifest,
};
@Minitour

Minitour commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Deferring comment fixes to next release.

@Minitour
Minitour merged commit e41ce2d into main Aug 4, 2026
15 checks passed
Minitour added a commit that referenced this pull request Aug 4, 2026
Keep attributes_json valid JSON under the size cap, tighten plugin skill fallback guards, namespace activity run ids by conversation, populate nested plugin manifest paths, and skip Cursor id reconcile when generation chat ids conflict.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants