Skip to content

feat(skills): isolate agent skill roots#2007

Merged
zerob13 merged 18 commits into
devfrom
feat/independent-agent-skills
Jul 23, 2026
Merged

feat(skills): isolate agent skill roots#2007
zerob13 merged 18 commits into
devfrom
feat/independent-agent-skills

Conversation

@zhangmo8

@zhangmo8 zhangmo8 commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Give every DeepChat Agent an independent Skill root, management state, discovery catalog, and runtime scope.
  • Remove built-in Agent configuration inheritance and migrate legacy Agent and Session state.
  • Replace the shared Skill sync workflow with explicit import from another Agent or external DeepChat data.
  • Fence Agent deletion against concurrent Session assignment and harden Skill import and shutdown boundaries.

UI

BEFORE

Skills
└── Shared DeepChat catalog
    └── Assign or install into Agents

AFTER

Agent Skills
├── Agent A catalog
├── Agent B catalog
└── Import from another Agent or external DeepChat

Validation

  • pnpm run format
  • pnpm run i18n
  • pnpm run lint
  • pnpm run typecheck
  • Renderer: 189 files, 1492 tests passed.
  • Main: 380 files and 4384 tests passed. The existing architecture baseline test exceeded its default 10 second timeout in the full parallel run, then passed 2 of 2 standalone with a 30 second timeout.
  • Packaged two-Agent same-name Skill E2E was not run.

Summary by CodeRabbit

  • New Features
    • Skills are now fully scoped per agent, including agent-specific catalogs, management state, filesystem roots, and runtime execution context.
    • Added Import Skills from Agent with source selection, previews, and skip/rename/overwrite strategies, including partial-success results.
  • Bug Fixes
    • Improved lifecycle gating prevents stale updates during session transfers and agent deletion; concurrent session actions are now safely serialized.
    • Tool and chat-settings skill activation now uses validated, agent-scoped active skills to reduce incorrect enable/disable behavior.
  • Documentation
    • Updated architecture and skills-management docs to reflect agent ownership, scoping rules, and migration/import semantics.

zhangmo8 added 15 commits July 20, 2026 12:23
Restructure renderer scope boundaries per docs/architecture/renderer-scope-optimization:

- Move ChatTabView into apps/chat-main; drop unused views/SettingsTabView
- Extract foundation/appearance/documentAppearance shared by all renderer entries
- Session store: paginated list with cursor, tombstoned deletes, targeted
  activate/deactivate IPC; refetch when a pending first page is invalidated
- Chat search: canonical debounced query, incremental per-row highlighting
  with a MutationObserver; drain pending observer work on same-query re-apply
  and count only rendered user-message content
- Message window fast path returns a fresh array so totalHeight and the
  window range react to tail growth; batch measures no longer force per-row
  full layout rebuilds
- Scroll state machine rejects a late session-restore scroll after the user
  explicitly navigated (search/spotlight)
- Split row-level isStreamingMessage (activity grouping) from thread-level
  isInGeneratingThread (fork gating, variant auto-switch)
- Always clear plan snapshots for deleted messages even after switching views
Sidebar search is a pure client-side filter, so pagination must keep
auto-filling while a query is active or matches are limited to already
loaded pages. Collapsed-group guards still apply when not searching.
Preserve targeted session updates across stale page loads and fence environment reorder rollbacks. Align search indexing with activatable DOM content and refresh the renderer architecture baseline.
…e-optimization

Resolve language/i18n conflicts to preserve this PR's scope-boundary race
fixes (languageInitialization promise cache, listener-before-snapshot
registration, documentAppearance abstraction) alongside the lazy renderer
locale loading (#2003) and dashboard CPU reduction (#2004) perf wins from dev.
Address coderabbit review comments on PR #2000:
- ChatInputToolbar.test: trigger steer click and assert no emission while
  pending (was only checking static attributes)
- session store: rename isNewerSessionUpdate -> isStaleOrSameSessionUpdate
  so the predicate name matches its "skip stale/equal update" semantics
- extract shared createDeferred helper (with reject) to test/renderer/utils,
  replacing three local copies in useComposerSubmit/useMessageActions/
  useToolInteraction tests
- useMessageVirtualization.test: cover layoutSegments provided but returning
  null at runtime (inline-stream reinsertion fallback)
- SettingsApp.test: reset document.documentElement lang/dir in afterEach to
  match sibling documentAppearance cleanup
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR replaces shared DeepChat Skill ownership and configuration inheritance with independent Agent-scoped roots, catalogs, migration, runtime validation, import workflows, lifecycle fencing, protected filesystem access, typed routes, and renderer support.

Changes

Independent Agent Skills

Layer / File(s) Summary
Architecture and migration contracts
docs/architecture/*, docs/features/*, docs/FLOWS.md
Documents independent Agent configuration, per-Agent Skill roots, migration, session/Agent Skill intersection, snapshot imports, and lifecycle rules.
Agent configuration and lifecycle
src/main/agent/*, src/main/session/*, src/main/app/composition.ts
Removes builtin configuration inheritance, materializes legacy configs, moves defaults to settings, adds deletion cleanup debt, startup barriers, and serialized session/Agent operations.
Scoped Skill service and import
src/main/skill/*, src/shared/types/skill*.ts
Adds per-Agent catalogs and management state, physical path confinement, migration, agent-aware CRUD, external import validation, and snapshot installation.
Routes, tools, and renderer
src/shared/contracts/*, src/main/tool/*, src/renderer/*
Adds typed Agent Skill import routes, protected Skill directories, agent-scoped clients/stores/UI, stale-request guards, and localized import strings.
Tests and validation
test/main/*, test/renderer/*, test/e2e/*
Covers migration, scoped catalogs, import safety, lifecycle interleavings, route contracts, tool isolation, renderer races, and Agent-scoped chat behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: zerob13

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately captures the main change: isolating skills per agent.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/independent-agent-skills

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
src/renderer/src/i18n/da-DK/settings.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

src/renderer/src/i18n/de-DE/settings.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

src/renderer/src/i18n/en-US/settings.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

  • 17 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/agent/deepchat/resources/systemPromptBuilder.ts (1)

104-141: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unhandled rejection from resolveSessionAgentId breaks the fail-safe pattern used everywhere else in this function.

Every other skillService call in buildSystemPromptWithSkills (getMetadataList, getActiveSkills, loadSkillContent) is wrapped in try/catch with a warn-and-continue fallback. The new skillService.resolveSessionAgentId(sessionId) call is not, despite running on every prompt build. An error here (e.g. transient lookup/session-state issue) will propagate uncaught and fail the entire turn instead of degrading gracefully.

🛡️ Proposed fix
   const skillsEnabled = dependencies.skillSettings.isEnabled();
   const skillService = dependencies.skillService;
-  const sessionAgentId = skillsEnabled
-    ? await skillService.resolveSessionAgentId(sessionId)
-    : null;
+  let sessionAgentId: string | null = null;
+  if (skillsEnabled) {
+    try {
+      sessionAgentId = await skillService.resolveSessionAgentId(sessionId);
+    } catch (error) {
+      console.warn(
+        `[DeepChatAgent] Failed to resolve agent id for skills in session ${sessionId}:`,
+        error,
+      );
+    }
+  }
🤖 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 `@src/main/agent/deepchat/resources/systemPromptBuilder.ts` around lines 104 -
141, Wrap the skillService.resolveSessionAgentId call in
buildSystemPromptWithSkills with the same warn-and-continue error handling used
for getMetadataList, getActiveSkills, and loadSkillContent. On failure, log the
session context and continue with sessionAgentId set to null so prompt
construction remains fail-safe.
src/main/agent/acp/compatibility/dependencies.ts (1)

105-163: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Persist only the validated activeSkills in userContent.

runtimeActiveSkills is already the validated, permission-checked set used for prompt/tool resolution, but userContent.activeSkills still stores the raw request array. Store runtimeActiveSkills instead so persisted messages don’t list skills that were rejected for the session.

🛡️ Proposed fix
           userContent: {
             text: content.text,
             files: content.files ?? [],
             links: [],
             search: false,
             think: false,
-            ...(content.activeSkills?.length ? { activeSkills: content.activeSkills } : {}),
+            ...(runtimeActiveSkills.length ? { activeSkills: runtimeActiveSkills } : {}),
             ...(content.inlineItems?.length ? { inlineItems: content.inlineItems } : {})
           },
🤖 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 `@src/main/agent/acp/compatibility/dependencies.ts` around lines 105 - 163,
Update the userContent construction in the session request flow to persist the
validated runtimeActiveSkills result instead of the raw content.activeSkills
array. Use runtimeActiveSkills for the activeSkills field while preserving the
existing omission behavior when no validated skills remain.
🧹 Nitpick comments (6)
src/renderer/src/i18n/tr-TR/settings.json (1)

2146-2176: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

New skills.agentImport block shipped untranslated in Turkish and Vietnamese locales. Both files copy the English source strings verbatim for the entire new "Import Skills from Agent" feature, while the same block is fully localized in zh-CN/zh-HK/zh-TW.

  • src/renderer/src/i18n/tr-TR/settings.json#L2146-L2176: translate all skills.agentImport strings into Turkish.
  • src/renderer/src/i18n/vi-VN/settings.json#L2146-L2176: translate all skills.agentImport strings into Vietnamese.

As per coding guidelines, "After completing a feature, run pnpm run format, pnpm run i18n, and pnpm run lint" — this translation gap should be caught by that step.

🤖 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 `@src/renderer/src/i18n/tr-TR/settings.json` around lines 2146 - 2176,
Translate every string in the skills.agentImport block from English into Turkish
in src/renderer/src/i18n/tr-TR/settings.json (lines 2146-2176), preserving keys
and placeholders; translate the corresponding entire block into Vietnamese in
src/renderer/src/i18n/vi-VN/settings.json (lines 2146-2176). Run the required
format, i18n, and lint commands afterward.

Source: Coding guidelines

test/main/skill/skillTools.test.ts (1)

161-169: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Strengthen the "current-message active skills" guard test with a non-empty payload.

Passing [] as the second argument to handleSkillList('conv-123', []) can't actually demonstrate that current-message active skills aren't merged into the catalog — the assertions would pass identically even without the guard this test is meant to protect. Use a non-empty skill-name array (e.g. ['git-commit']) and assert it isn't reflected in result.skills/activeCount to meaningfully exercise the intended behavior.

🤖 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 `@test/main/skill/skillTools.test.ts` around lines 161 - 169, Strengthen the
test around handleSkillList by passing a non-empty current-message skill array
such as ['git-commit'] instead of []. Keep the catalog assertions and explicitly
verify that this payload is not reflected in result.skills or activeCount, while
preserving the expected catalog entries and total count.
src/main/skill/index.ts (1)

1047-1057: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Reads and fully sanitizes management state once per skill during visibility filtering.

getVisibleMetadataFromCache calls isSkillVisibleisSkillDisabled for every metadata entry, and isSkillDisabled (Line 1288-1290) re-invokes getStoredManagementState(), which re-sanitizes the entire stored state (all agents, skills, migration) on each call. That makes each catalog read O(N²) on a hot path (getMetadataList, discoverSkills, discoverScopedSkills). getUnifiedSkillCatalog (Line 1339) already resolves state once and maps — mirror that here by resolving the agent's disabled set once.

♻️ Resolve state once and pass disabled lookup into the filter
-  private getVisibleMetadataFromCache(agentId: string): SkillMetadata[] {
-    return this.sortSkillMetadata(
-      Array.from(this.getMetadataCacheForAgent(agentId).values()).filter((skill) =>
-        this.isSkillVisible(skill, agentId)
-      )
-    )
-  }
-
-  private isSkillVisible(metadata: SkillMetadata, agentId: string): boolean {
-    return Boolean(metadata) && !this.isSkillDisabled(agentId, metadata.name)
-  }
+  private getVisibleMetadataFromCache(agentId: string): SkillMetadata[] {
+    const agentSkills = this.getStoredManagementState().agents[agentId]?.skills
+    return this.sortSkillMetadata(
+      Array.from(this.getMetadataCacheForAgent(agentId).values()).filter(
+        (skill) => Boolean(skill) && agentSkills?.[skill.name]?.disabled !== true
+      )
+    )
+  }
+
+  private isSkillVisible(metadata: SkillMetadata, agentId: string): boolean {
+    return Boolean(metadata) && !this.isSkillDisabled(agentId, metadata.name)
+  }
🤖 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 `@src/main/skill/index.ts` around lines 1047 - 1057, Update
getVisibleMetadataFromCache to resolve the agent’s disabled-skill state once
before filtering, then pass that lookup into isSkillVisible (and use it when
checking visibility) instead of calling isSkillDisabled for every metadata
entry. Preserve the existing sorting and visibility behavior while avoiding
repeated getStoredManagementState sanitization.
src/main/tool/agentTools/agentToolManager.ts (1)

1009-1018: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Duplicate resolveActiveSkillRoots work per filesystem tool call.

buildAllowedDirectories (via resolveActiveSkillRoots when includeSkillRoots is true) and buildProtectedSkillDirectoryRules (which also calls resolveActiveSkillRoots) are invoked back-to-back with the same conversationId/activeSkillNames, each redoing resolveSessionAgentId, getMetadataList, and per-skill fs.existsSync/fs.statSync calls. This doubles the I/O cost on every read/write/edit/glob/grep tool invocation.

Consider resolving the active skill roots once per call and passing the result into both builders.

Also applies to: 1358-1381

🤖 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 `@src/main/tool/agentTools/agentToolManager.ts` around lines 1009 - 1018,
Resolve active skill roots once in the tool-call flow before invoking
buildAllowedDirectories and buildProtectedSkillDirectoryRules, then pass the
shared result into both builders through their existing or updated parameters.
Ensure this applies to both affected call sites and preserves the exec behavior
where skill roots are excluded, avoiding repeated resolveActiveSkillRoots work
for the same conversationId and activeSkillNames.
src/main/skill/skillTools.ts (1)

21-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the duplicate builtin-agent constant and reuse the shared one.

src/main/skill/skillTools.ts still falls back to the literal 'deepchat' in handleSkillList, while src/main/skill/index.ts and src/main/skill/agentSkillRoots.ts already define and use BUILTIN_SKILL_AGENT_ID. Import the exported constant instead to keep the fallback value in one place.

♻️ Suggested fix
-    const resolvedAgentId = conversationId
-      ? await this.skillService.resolveSessionAgentId(conversationId)
-      : 'deepchat'
+    const resolvedAgentId = conversationId
+      ? await this.skillService.resolveSessionAgentId(conversationId)
+      : BUILTIN_SKILL_AGENT_ID
🤖 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 `@src/main/skill/skillTools.ts` around lines 21 - 27, Update handleSkillList in
skillTools.ts to import and use the shared BUILTIN_SKILL_AGENT_ID from the skill
module instead of the literal 'deepchat' fallback, keeping the agent identifier
defined in one place.
src/main/agent/settings.ts (1)

547-563: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Wire retryPendingDeletedAgentSkillCleanup() into startup

The retry method isn't referenced anywhere in the checked-in code, so pending Agent Skill cleanups added via addPendingAgentSkillCleanup will not be retried until the app exits or they are removed directly. Call it from the startup flow (for example src/main/app/composition.ts) instead of leaving it unreachable.

🤖 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 `@src/main/agent/settings.ts` around lines 547 - 563, Invoke
retryPendingDeletedAgentSkillCleanup() from the application startup flow, such
as the composition/bootstrap initialization path, after AgentSettings and its
repository are available. Ensure startup awaits or otherwise handles the
method’s Promise so pending cleanups registered by addPendingAgentSkillCleanup
are retried automatically.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@docs/architecture/agent-system.md`:
- Around line 32-33: Clarify the migration contract in the architecture
documentation: legacy migration must materialize each readable manual Agent’s
effective config using the built-in config and prior fail-closed fallback rules,
including for corrupt rows where applicable. State that the prohibition on
silently applying built-in values applies only to the post-migration independent
resolver, and align the wording with the contracts in plan.md and spec.md.

In `@src/main/agent/settings.ts`:
- Around line 201-212: Wrap the one-time migration call
materializeIndependentDeepChatAgentConfigs() in start() with try/catch, logging
a warning and continuing startup when it fails. Apply the same error-handling
pattern to the related migration flow around
reconcileLegacyBuiltinAgentSelections and ensure a partial failure does not
rethrow on subsequent launches.
- Around line 449-469: Update the config construction in the agent creation
method so both autoCompactionTriggerThreshold and
autoCompactionRetainRecentPairs are normalized after resolving either the
explicit input.config value or the settings fallback. Preserve the existing
defaults while ensuring explicitly supplied values are passed through
normalizeAutoCompactionTriggerThreshold and
normalizeAutoCompactionRetainRecentPairs before repository.createDeepChatAgent
stores them.

In `@src/main/skill/index.ts`:
- Around line 635-639: Update migrateLegacyAgentSkillScopes’ builtinCatalog loop
to catch failures from migrateLegacySkillExtension per skill, log a warning, and
continue processing remaining skills. Ensure the migration still records
completion and initialization can proceed despite an unreadable sidecar, without
changing successful migration behavior.

In `@src/main/tool/agentTools/agentToolManager.ts`:
- Around line 1441-1472: Update buildProtectedSkillDirectoryRules so failure to
resolve the skills root never returns an empty protected-rule set. When both
skillService.getSkillsDir() and skillSettings.getPath() fail, fail closed by
propagating the resolution error or returning a deny-all rule rooted at a safe
best-effort skills directory; preserve the configured fallback path when
available.

In `@src/renderer/settings/components/skills/InstallFromGitDialog.vue`:
- Around line 181-205: Update the repository URL watcher and scan flow around
scanRequestId and requestedRepoUrl so changing the URL invalidates any in-flight
scan, even while scanning is true. Increment or otherwise invalidate
scanRequestId before the watcher exits, clear stale scan results and selections
as appropriate, and ensure late responses cannot populate results for a
different URL or allow installation from the old scanResult.repoUrl.

In `@src/renderer/src/i18n/da-DK/settings.json`:
- Around line 1849-1879: Translate every string in the agentImport block into
the appropriate locale, preserving all keys, placeholders, nested structures,
and JSON validity. Update src/renderer/src/i18n/da-DK/settings.json lines
1849-1879 in Danish; src/renderer/src/i18n/de-DE/settings.json lines 2146-2176
in German; src/renderer/src/i18n/es-ES/settings.json lines 2146-2176 in Spanish;
src/renderer/src/i18n/fa-IR/settings.json lines 1916-1946 in Persian;
src/renderer/src/i18n/fr-FR/settings.json lines 1916-1946 in French; and
src/renderer/src/i18n/he-IL/settings.json lines 1916-1946 in Hebrew.

In `@src/renderer/src/i18n/id-ID/settings.json`:
- Around line 2146-2176: Translate every string in the agentImport section for
the affected locales: src/renderer/src/i18n/id-ID/settings.json lines 2146-2176,
src/renderer/src/i18n/it-IT/settings.json lines 2146-2176, and
src/renderer/src/i18n/ja-JP/settings.json lines 1916-1946. Preserve all keys,
placeholders, nesting, and JSON validity while replacing the English values with
natural Indonesian, Italian, and Japanese translations respectively.

---

Outside diff comments:
In `@src/main/agent/acp/compatibility/dependencies.ts`:
- Around line 105-163: Update the userContent construction in the session
request flow to persist the validated runtimeActiveSkills result instead of the
raw content.activeSkills array. Use runtimeActiveSkills for the activeSkills
field while preserving the existing omission behavior when no validated skills
remain.

In `@src/main/agent/deepchat/resources/systemPromptBuilder.ts`:
- Around line 104-141: Wrap the skillService.resolveSessionAgentId call in
buildSystemPromptWithSkills with the same warn-and-continue error handling used
for getMetadataList, getActiveSkills, and loadSkillContent. On failure, log the
session context and continue with sessionAgentId set to null so prompt
construction remains fail-safe.

---

Nitpick comments:
In `@src/main/agent/settings.ts`:
- Around line 547-563: Invoke retryPendingDeletedAgentSkillCleanup() from the
application startup flow, such as the composition/bootstrap initialization path,
after AgentSettings and its repository are available. Ensure startup awaits or
otherwise handles the method’s Promise so pending cleanups registered by
addPendingAgentSkillCleanup are retried automatically.

In `@src/main/skill/index.ts`:
- Around line 1047-1057: Update getVisibleMetadataFromCache to resolve the
agent’s disabled-skill state once before filtering, then pass that lookup into
isSkillVisible (and use it when checking visibility) instead of calling
isSkillDisabled for every metadata entry. Preserve the existing sorting and
visibility behavior while avoiding repeated getStoredManagementState
sanitization.

In `@src/main/skill/skillTools.ts`:
- Around line 21-27: Update handleSkillList in skillTools.ts to import and use
the shared BUILTIN_SKILL_AGENT_ID from the skill module instead of the literal
'deepchat' fallback, keeping the agent identifier defined in one place.

In `@src/main/tool/agentTools/agentToolManager.ts`:
- Around line 1009-1018: Resolve active skill roots once in the tool-call flow
before invoking buildAllowedDirectories and buildProtectedSkillDirectoryRules,
then pass the shared result into both builders through their existing or updated
parameters. Ensure this applies to both affected call sites and preserves the
exec behavior where skill roots are excluded, avoiding repeated
resolveActiveSkillRoots work for the same conversationId and activeSkillNames.

In `@src/renderer/src/i18n/tr-TR/settings.json`:
- Around line 2146-2176: Translate every string in the skills.agentImport block
from English into Turkish in src/renderer/src/i18n/tr-TR/settings.json (lines
2146-2176), preserving keys and placeholders; translate the corresponding entire
block into Vietnamese in src/renderer/src/i18n/vi-VN/settings.json (lines
2146-2176). Run the required format, i18n, and lint commands afterward.

In `@test/main/skill/skillTools.test.ts`:
- Around line 161-169: Strengthen the test around handleSkillList by passing a
non-empty current-message skill array such as ['git-commit'] instead of []. Keep
the catalog assertions and explicitly verify that this payload is not reflected
in result.skills or activeCount, while preserving the expected catalog entries
and total count.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cb0f8a17-bcf1-4466-8b34-f0feb0726782

📥 Commits

Reviewing files that changed from the base of the PR and between ca9775e and 1cb1522.

📒 Files selected for processing (142)
  • docs/FLOWS.md
  • docs/architecture/agent-system.md
  • docs/architecture/independent-agent-skills/plan.md
  • docs/architecture/independent-agent-skills/spec.md
  • docs/architecture/independent-agent-skills/tasks.md
  • docs/architecture/tool-system.md
  • docs/features/deepchat-skills-management/spec.md
  • src/main/agent/acp/compatibility/dependencies.ts
  • src/main/agent/deepchat/deepChatAgentRepository.ts
  • src/main/agent/deepchat/defaults.ts
  • src/main/agent/deepchat/resources/systemPromptBuilder.ts
  • src/main/agent/deepchat/runtime/deepChatLoopRunner.ts
  • src/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.ts
  • src/main/agent/deepchat/runtime/deferredToolExecutor.ts
  • src/main/agent/deepchat/runtime/dispatch.ts
  • src/main/agent/deepchat/runtime/sessionSettingsCoordinator.ts
  • src/main/agent/deepchat/runtime/toolResolver.ts
  • src/main/agent/deepchat/runtime/turnCoordinator.ts
  • src/main/agent/deepchat/runtime/types.ts
  • src/main/agent/lifecycleGate.ts
  • src/main/agent/repository/index.ts
  • src/main/agent/settings.ts
  • src/main/app/composition.ts
  • src/main/memory/index.ts
  • src/main/memory/services/maintenanceService.ts
  • src/main/session/assignment.ts
  • src/main/session/assignmentPolicy.ts
  • src/main/session/contracts.ts
  • src/main/session/lifecycle.ts
  • src/main/session/turn.ts
  • src/main/skill/agentSkillImportService.ts
  • src/main/skill/agentSkillRoots.ts
  • src/main/skill/discoveryWorker.ts
  • src/main/skill/index.ts
  • src/main/skill/routes.ts
  • src/main/skill/settings.ts
  • src/main/skill/skillExecutionService.ts
  • src/main/skill/skillTools.ts
  • src/main/skill/sync/index.ts
  • src/main/tool/agentTools/agentFffSearchHandler.ts
  • src/main/tool/agentTools/agentFileSystemHandler.ts
  • src/main/tool/agentTools/agentToolManager.ts
  • src/main/tool/index.ts
  • src/renderer/api/SkillClient.ts
  • src/renderer/api/SkillSyncClient.ts
  • src/renderer/settings/components/DeepChatAgentsSettings.vue
  • src/renderer/settings/components/skills/AdoptSkillDialog.vue
  • src/renderer/settings/components/skills/AgentSkillTable.vue
  • src/renderer/settings/components/skills/ImportSkillsFromAgentDialog.vue
  • src/renderer/settings/components/skills/InstallFromGitDialog.vue
  • src/renderer/settings/components/skills/InstallSkillToAgentDialog.vue
  • src/renderer/settings/components/skills/SkillAgentsTab.vue
  • src/renderer/settings/components/skills/SkillCard.vue
  • src/renderer/settings/components/skills/SkillDetailDialog.vue
  • src/renderer/settings/components/skills/SkillInstallDialog.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/ConflictResolver.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/ExportWizard.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/ImportWizard.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/SkillSelector.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/SkillSyncDialog.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/SyncResult.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/ToolSelector.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/index.ts
  • src/renderer/settings/components/skills/SkillsSettings.vue
  • src/renderer/src/components/chat-input/SkillsIndicator.vue
  • src/renderer/src/components/chat-input/composables/useSkillsData.ts
  • src/renderer/src/components/chat/ChatInputBox.vue
  • src/renderer/src/components/chat/composables/useChatInputMentions.ts
  • src/renderer/src/features/chat-page/ChatPage.vue
  • src/renderer/src/i18n/da-DK/settings.json
  • src/renderer/src/i18n/de-DE/settings.json
  • src/renderer/src/i18n/en-US/settings.json
  • src/renderer/src/i18n/es-ES/settings.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • src/renderer/src/i18n/fr-FR/settings.json
  • src/renderer/src/i18n/he-IL/settings.json
  • src/renderer/src/i18n/id-ID/settings.json
  • src/renderer/src/i18n/it-IT/settings.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/ms-MY/settings.json
  • src/renderer/src/i18n/pl-PL/settings.json
  • src/renderer/src/i18n/pt-BR/settings.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/tr-TR/settings.json
  • src/renderer/src/i18n/vi-VN/settings.json
  • src/renderer/src/i18n/zh-CN/settings.json
  • src/renderer/src/i18n/zh-HK/settings.json
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/renderer/src/pages/NewThreadPage.vue
  • src/renderer/src/stores/skillsStore.ts
  • src/shared/contracts/events/skills.events.ts
  • src/shared/contracts/routes.ts
  • src/shared/contracts/routes/skillSync.routes.ts
  • src/shared/contracts/routes/skills.routes.ts
  • src/shared/types/agentSkillImport.ts
  • src/shared/types/skill.ts
  • src/shared/types/skillManagement.ts
  • src/shared/types/tool.d.ts
  • src/types/i18n.d.ts
  • test/e2e/specs/16-skills-readonly-route.smoke.spec.ts
  • test/main/agent/deepchat/deepChatAgentRepository.test.ts
  • test/main/agent/deepchat/defaults.test.ts
  • test/main/agent/deepchat/resources/systemPromptBuilder.test.ts
  • test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts
  • test/main/agent/deepchat/runtime/toolAdapters.test.ts
  • test/main/agent/deepchat/runtime/toolResolver.test.ts
  • test/main/agent/lifecycleGate.test.ts
  • test/main/agent/repository.test.ts
  • test/main/agent/settings.test.ts
  • test/main/app/compositionBoundaries.test.ts
  • test/main/memory/maintenanceService.test.ts
  • test/main/routes/contracts.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/main/session/assignment.test.ts
  • test/main/session/assignmentPolicy.test.ts
  • test/main/session/lifecycle.test.ts
  • test/main/session/runtimeIntegration.test.ts
  • test/main/session/sessionFixture.ts
  • test/main/session/turn.test.ts
  • test/main/skill/agentSkillImportService.test.ts
  • test/main/skill/agentSkillRoots.test.ts
  • test/main/skill/discoveryWorker.test.ts
  • test/main/skill/settings.test.ts
  • test/main/skill/skillExecutionService.test.ts
  • test/main/skill/skillService.test.ts
  • test/main/skill/skillServiceAgentScopes.test.ts
  • test/main/skill/skillTools.test.ts
  • test/main/skill/sync/skillSyncService.test.ts
  • test/main/tool/agentTools/agentToolManagerSettings.test.ts
  • test/main/tool/agentTools/agentToolManagerSkillAccess.test.ts
  • test/renderer/api/clients.test.ts
  • test/renderer/components/ChatInputBox.test.ts
  • test/renderer/components/ChatPage.test.ts
  • test/renderer/components/DeepChatAgentsSettings.test.ts
  • test/renderer/components/ImportSkillsFromAgentDialog.test.ts
  • test/renderer/components/NewThreadPage.test.ts
  • test/renderer/components/SkillInstallTargetScope.test.ts
  • test/renderer/components/SkillSyncSettings.test.ts
  • test/renderer/components/SkillsSettingsAgentScope.test.ts
  • test/renderer/composables/useChatInputSkillScope.test.ts
  • test/renderer/stores/skillsStore.test.ts
💤 Files with no reviewable changes (20)
  • src/renderer/settings/components/skills/SkillSyncDialog/SkillSyncDialog.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/index.ts
  • src/renderer/settings/components/skills/SkillSyncDialog/SyncResult.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/ExportWizard.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/SkillSelector.vue
  • src/renderer/settings/components/skills/AdoptSkillDialog.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/ToolSelector.vue
  • src/main/agent/deepchat/runtime/types.ts
  • src/main/agent/deepchat/runtime/deferredToolExecutor.ts
  • src/renderer/settings/components/skills/SkillCard.vue
  • src/renderer/settings/components/skills/InstallSkillToAgentDialog.vue
  • src/renderer/settings/components/skills/SkillSyncDialog/ConflictResolver.vue
  • src/main/memory/services/maintenanceService.ts
  • src/renderer/settings/components/skills/SkillSyncDialog/ImportWizard.vue
  • src/main/agent/deepchat/runtime/dispatch.ts
  • src/main/memory/index.ts
  • test/main/agent/deepchat/runtime/toolAdapters.test.ts
  • src/renderer/api/SkillSyncClient.ts
  • src/shared/types/tool.d.ts
  • src/shared/contracts/routes/skillSync.routes.ts

Comment thread docs/architecture/agent-system.md Outdated
Comment thread src/main/agent/settings.ts
Comment thread src/main/agent/settings.ts
Comment thread src/main/skill/index.ts
Comment thread src/main/tool/agentTools/agentToolManager.ts
Comment thread src/renderer/settings/components/skills/InstallFromGitDialog.vue
Comment thread src/renderer/src/i18n/da-DK/settings.json
Comment thread src/renderer/src/i18n/id-ID/settings.json

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
test/renderer/stores/skillsStore.test.ts (1)

84-86: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the catalog refresh target.

This verifies only the call count, so a regression that refreshes the wrong Agent would still pass. Assert that the deepchat scope is forwarded to the catalog request.

Proposed test assertion
 catalogListener?.({ agentIds: ['deepchat'] })
 await Promise.resolve()
 expect(skillClient.getUnifiedSkillCatalog).toHaveBeenCalledTimes(1)
+expect(skillClient.getUnifiedSkillCatalog).toHaveBeenCalledWith('deepchat')
🤖 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 `@test/renderer/stores/skillsStore.test.ts` around lines 84 - 86, Update the
test around catalogListener to assert that skillClient.getUnifiedSkillCatalog
was called with the deepchat agent scope, not only that it was called once.
Preserve the existing call-count assertion while verifying the forwarded
agentIds value.
test/main/skill/toolNameMapping.test.ts (1)

44-45: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the exact canonical tool list.

toHaveLength(5) would still pass if a settings name were dropped and replaced with another value. Assert result.tools against the five expected names to protect the normalization contract.

Proposed test assertion
-    expect(result.tools).toHaveLength(5)
+    expect(result.tools).toEqual([
+      'deepchat_settings_toggle',
+      'deepchat_settings_set_language',
+      'deepchat_settings_set_theme',
+      'deepchat_settings_set_font_size',
+      'deepchat_settings_open'
+    ])
🤖 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 `@test/main/skill/toolNameMapping.test.ts` around lines 44 - 45, Update the
assertions in the tool-name mapping test to compare result.tools against the
exact five expected canonical tool names, rather than checking only its length.
Keep the existing empty result.warnings assertion unchanged.
🤖 Prompt for all review comments with 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.

Nitpick comments:
In `@test/main/skill/toolNameMapping.test.ts`:
- Around line 44-45: Update the assertions in the tool-name mapping test to
compare result.tools against the exact five expected canonical tool names,
rather than checking only its length. Keep the existing empty result.warnings
assertion unchanged.

In `@test/renderer/stores/skillsStore.test.ts`:
- Around line 84-86: Update the test around catalogListener to assert that
skillClient.getUnifiedSkillCatalog was called with the deepchat agent scope, not
only that it was called once. Preserve the existing call-count assertion while
verifying the forwarded agentIds value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a29ddeb4-3e22-450d-83e4-33e8878c4bbc

📥 Commits

Reviewing files that changed from the base of the PR and between 3a068b1 and ec0ed53.

📒 Files selected for processing (8)
  • docs/issues/skill-tool-activation-refresh/spec.md
  • src/main/skill/toolNameMapping.ts
  • src/main/tool/agentTools/agentToolManager.ts
  • src/main/tool/agentTools/chatSettingsTools.ts
  • src/renderer/src/stores/skillsStore.ts
  • test/main/skill/toolNameMapping.test.ts
  • test/main/tool/agentTools/chatSettingsTools.test.ts
  • test/renderer/stores/skillsStore.test.ts
💤 Files with no reviewable changes (1)
  • src/renderer/src/stores/skillsStore.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/tool/agentTools/agentToolManager.ts

…-skills

# Conflicts:
#	src/main/session/lifecycle.ts
#	src/main/session/turn.ts
#	test/main/session/lifecycle.test.ts
#	test/renderer/components/NewThreadPage.test.ts

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.ts (1)

2011-2015: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Redact queue-drain errors before console.error.

deepChatRuntimeCoordinator.ts and turnCoordinator.ts both log the raw error plus sessionId in production logs. Reuse an existing redaction/logging path and avoid logging the raw error object so failure diagnostics cannot persist sensitive content embedded in provider/user data.

🤖 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 `@src/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.ts` around lines
2011 - 2015, Redact queue-drain errors before logging: update the error handlers
around drainPendingQueueIfPossible in
src/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.ts:2011-2015 and
src/main/agent/deepchat/runtime/turnCoordinator.ts:1398-1402 to reuse the
existing redaction/logging path, avoid passing raw error objects to
console.error, and preserve the session and reason context without exposing
sensitive provider or user data.

Source: Linters/SAST tools

🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/main/session/lifecycle.ts`:
- Around line 60-63: Resolve the assignment before acquiring the lifecycle gate,
and use the canonical resolved agent ID for locking: in
src/main/session/lifecycle.ts lines 60-63 and 165-170, gate on
assignment.agentId; in lines 241-246, gate on runtimeConfig.agentId and pass
that resolved config into the gated helper so assignment resolution is not
repeated. Update the create, detached-session, and subagent lifecycle flows
accordingly.

---

Outside diff comments:
In `@src/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.ts`:
- Around line 2011-2015: Redact queue-drain errors before logging: update the
error handlers around drainPendingQueueIfPossible in
src/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.ts:2011-2015 and
src/main/agent/deepchat/runtime/turnCoordinator.ts:1398-1402 to reuse the
existing redaction/logging path, avoid passing raw error objects to
console.error, and preserve the session and reason context without exposing
sensitive provider or user data.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 617c9369-5467-4d56-84e9-fb7ad00a521d

📥 Commits

Reviewing files that changed from the base of the PR and between ec0ed53 and 9adb4e2.

📒 Files selected for processing (47)
  • docs/issues/skill-tool-activation-refresh/spec.md
  • src/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.ts
  • src/main/agent/deepchat/runtime/turnCoordinator.ts
  • src/main/app/composition.ts
  • src/main/session/contracts.ts
  • src/main/session/lifecycle.ts
  • src/main/session/turn.ts
  • src/main/skill/skillTools.ts
  • src/renderer/settings/components/skills/SkillsSettings.vue
  • src/renderer/src/components/chat/ChatInputBox.vue
  • src/renderer/src/features/chat-page/ChatPage.vue
  • src/renderer/src/i18n/da-DK/settings.json
  • src/renderer/src/i18n/de-DE/settings.json
  • src/renderer/src/i18n/en-US/settings.json
  • src/renderer/src/i18n/es-ES/settings.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • src/renderer/src/i18n/fr-FR/settings.json
  • src/renderer/src/i18n/he-IL/settings.json
  • src/renderer/src/i18n/id-ID/settings.json
  • src/renderer/src/i18n/it-IT/settings.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/ms-MY/settings.json
  • src/renderer/src/i18n/pl-PL/settings.json
  • src/renderer/src/i18n/pt-BR/settings.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/tr-TR/settings.json
  • src/renderer/src/i18n/vi-VN/settings.json
  • src/renderer/src/i18n/zh-CN/settings.json
  • src/renderer/src/i18n/zh-HK/settings.json
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/renderer/src/pages/NewThreadPage.vue
  • src/shared/contracts/routes.ts
  • test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts
  • test/main/routes/contracts.test.ts
  • test/main/routes/dispatcher.test.ts
  • test/main/session/assignmentPolicy.test.ts
  • test/main/session/lifecycle.test.ts
  • test/main/session/runtimeIntegration.test.ts
  • test/main/session/sessionFixture.ts
  • test/main/session/turn.test.ts
  • test/main/skill/skillTools.test.ts
  • test/renderer/api/clients.test.ts
  • test/renderer/components/ChatInputBox.test.ts
  • test/renderer/components/ChatPage.test.ts
  • test/renderer/components/NewThreadPage.test.ts
  • test/renderer/components/SkillsSettingsAgentScope.test.ts
🚧 Files skipped from review as they are similar to previous changes (35)
  • docs/issues/skill-tool-activation-refresh/spec.md
  • src/main/session/contracts.ts
  • src/renderer/src/features/chat-page/ChatPage.vue
  • test/main/session/turn.test.ts
  • src/renderer/src/i18n/fr-FR/settings.json
  • test/main/session/assignmentPolicy.test.ts
  • src/renderer/src/i18n/vi-VN/settings.json
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/de-DE/settings.json
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/renderer/src/i18n/pl-PL/settings.json
  • src/renderer/src/i18n/en-US/settings.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/id-ID/settings.json
  • src/renderer/src/i18n/es-ES/settings.json
  • test/renderer/components/ChatPage.test.ts
  • src/renderer/src/i18n/pt-BR/settings.json
  • src/shared/contracts/routes.ts
  • src/renderer/src/i18n/he-IL/settings.json
  • src/main/session/turn.ts
  • test/main/session/sessionFixture.ts
  • src/renderer/src/i18n/zh-CN/settings.json
  • src/renderer/src/i18n/ms-MY/settings.json
  • test/main/routes/contracts.test.ts
  • test/main/session/runtimeIntegration.test.ts
  • test/renderer/components/ChatInputBox.test.ts
  • src/renderer/src/i18n/fa-IR/settings.json
  • test/main/agent/deepchat/runtime/deepChatRuntimeCoordinator.test.ts
  • src/main/skill/skillTools.ts
  • src/renderer/src/components/chat/ChatInputBox.vue
  • test/main/skill/skillTools.test.ts
  • test/renderer/api/clients.test.ts
  • src/main/app/composition.ts
  • test/renderer/components/SkillsSettingsAgentScope.test.ts
  • src/renderer/settings/components/skills/SkillsSettings.vue

Comment thread src/main/session/lifecycle.ts
@zerob13
zerob13 merged commit a69dd21 into dev Jul 23, 2026
4 checks passed
@zhangmo8
zhangmo8 deleted the feat/independent-agent-skills branch July 23, 2026 09:43
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