Repository navigation
feat(memory): add autoSkill background project skill extraction #3673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
e05e614
4cfa191
73c5445
f7601a0
60fbe2c
ab40689
8780a25
f03c73a
e430ff9
1323b17
493e251
37d83a2
f5e38c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,6 +74,7 @@ import { type ContextState, templateString } from './agent-headless.js'; | |
| */ | ||
| export const EXCLUDED_TOOLS_FOR_SUBAGENTS: ReadonlySet<string> = new Set([ | ||
| ToolNames.AGENT, | ||
| ToolNames.SKILL_MANAGE, | ||
| ToolNames.CRON_CREATE, | ||
| ToolNames.CRON_LIST, | ||
| ToolNames.CRON_DELETE, | ||
|
|
@@ -334,7 +335,12 @@ export class AgentCore { | |
| ), | ||
| ); | ||
| } | ||
| toolsList.push(...onlyInlineDecls); | ||
| // Also filter inline FunctionDeclaration[] passed directly in toolConfig. | ||
| toolsList.push( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] 内联 — DeepSeek/deepseek-v4-pro via Qwen Code /review |
||
| ...onlyInlineDecls.filter( | ||
| (d) => !(d.name && excludedFromSubagents.has(d.name)), | ||
| ), | ||
| ); | ||
| } else { | ||
| // Inherit all available tools by default when not specified. | ||
| toolsList.push( | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -462,6 +462,8 @@ export interface ConfigParameters { | |||||||
| enableManagedAutoMemory?: boolean; | ||||||||
| /** Enable managed auto-dream consolidation separately from extraction. Defaults to true. */ | ||||||||
| enableManagedAutoDream?: boolean; | ||||||||
| /** Enable automatic project skill review after tool-heavy sessions. Defaults to true. */ | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Critical] JSDoc says
Suggested change
— deepseek-v4-pro via Qwen Code /review |
||||||||
| enableAutoSkill?: boolean; | ||||||||
| /** | ||||||||
| * Lightweight model for background tasks (memory extraction, dream, /btw side questions). | ||||||||
| * When set and valid for the current auth type, forked agents use this model instead of | ||||||||
|
|
@@ -690,6 +692,7 @@ export class Config { | |||||||
| private readonly defaultFileEncoding: FileEncodingType | undefined; | ||||||||
| private readonly enableManagedAutoMemory: boolean; | ||||||||
| private readonly enableManagedAutoDream: boolean; | ||||||||
| private readonly enableAutoSkill: boolean; | ||||||||
| private fastModel?: string; | ||||||||
| private readonly disableAllHooks: boolean; | ||||||||
| /** User-level hooks (always loaded regardless of trust) */ | ||||||||
|
|
@@ -886,6 +889,7 @@ export class Config { | |||||||
| }); | ||||||||
| this.enableManagedAutoMemory = params.enableManagedAutoMemory ?? true; | ||||||||
| this.enableManagedAutoDream = params.enableManagedAutoDream ?? false; | ||||||||
| this.enableAutoSkill = params.enableAutoSkill ?? false; | ||||||||
| this.fastModel = params.fastModel || undefined; | ||||||||
| this.disableAllHooks = params.disableAllHooks ?? false; | ||||||||
| // Store user and project hooks separately for proper source attribution | ||||||||
|
|
@@ -2128,6 +2132,10 @@ export class Config { | |||||||
| return this.enableManagedAutoDream && !this.getBareMode(); | ||||||||
| } | ||||||||
|
|
||||||||
| getAutoSkillEnabled(): boolean { | ||||||||
| return this.enableAutoSkill && !this.getBareMode(); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Return the MemoryManager instance created for this Config. | ||||||||
| * Use this to share background-task state (registry, drainer) with memory | ||||||||
|
|
@@ -2630,6 +2638,10 @@ export class Config { | |||||||
| const { SkillTool } = await import('../tools/skill.js'); | ||||||||
| return new SkillTool(this); | ||||||||
| }); | ||||||||
| await registerLazy(ToolNames.SKILL_MANAGE, async () => { | ||||||||
| const { SkillManageTool } = await import('../tools/skill-manage.js'); | ||||||||
| return new SkillManageTool(this); | ||||||||
| }); | ||||||||
| await registerLazy(ToolNames.LS, async () => { | ||||||||
| const { LSTool } = await import('../tools/ls.js'); | ||||||||
| return new LSTool(this); | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -128,6 +128,7 @@ const EMPTY_RELEVANT_AUTO_MEMORY_RESULT: RelevantAutoMemoryPromptResult = { | |||||||||||||||||||||||||||||||||||||
| export class GeminiClient { | ||||||||||||||||||||||||||||||||||||||
| private chat?: GeminiChat; | ||||||||||||||||||||||||||||||||||||||
| private sessionTurnCount = 0; | ||||||||||||||||||||||||||||||||||||||
| private toolCallCount = 0; | ||||||||||||||||||||||||||||||||||||||
| private readonly surfacedRelevantAutoMemoryPaths = new Set<string>(); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private readonly loopDetector: LoopDetectionService; | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -513,18 +514,45 @@ export class GeminiClient { | |||||||||||||||||||||||||||||||||||||
| private runManagedAutoMemoryBackgroundTasks( | ||||||||||||||||||||||||||||||||||||||
| messageType: SendMessageType, | ||||||||||||||||||||||||||||||||||||||
| ): void { | ||||||||||||||||||||||||||||||||||||||
| if (messageType !== SendMessageType.UserQuery) { | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!this.config.getManagedAutoMemoryEnabled()) { | ||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||
| messageType !== SendMessageType.UserQuery && | ||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Critical] Each call triggers
Suggested change
— deepseek-v4-pro via Qwen Code /review |
||||||||||||||||||||||||||||||||||||||
| messageType !== SendMessageType.ToolResult | ||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const projectRoot = this.config.getProjectRoot(); | ||||||||||||||||||||||||||||||||||||||
| const sessionId = this.config.getSessionId(); | ||||||||||||||||||||||||||||||||||||||
| const history = this.getHistory(); | ||||||||||||||||||||||||||||||||||||||
| const mgr = this.config.getMemoryManager(); | ||||||||||||||||||||||||||||||||||||||
| const autoSkillEnabled = this.config.getAutoSkillEnabled(); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (autoSkillEnabled) { | ||||||||||||||||||||||||||||||||||||||
| const skillReviewResult = mgr.scheduleSkillReview({ | ||||||||||||||||||||||||||||||||||||||
| projectRoot, | ||||||||||||||||||||||||||||||||||||||
| sessionId, | ||||||||||||||||||||||||||||||||||||||
| history, | ||||||||||||||||||||||||||||||||||||||
| config: this.config, | ||||||||||||||||||||||||||||||||||||||
| toolCallCount: this.toolCallCount, | ||||||||||||||||||||||||||||||||||||||
| enabled: autoSkillEnabled, | ||||||||||||||||||||||||||||||||||||||
| threshold: 20, | ||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Also Fix: Drop the four explicit fields and let — claude-opus-4-7 via Qwen Code /review |
||||||||||||||||||||||||||||||||||||||
| maxTurns: 8, | ||||||||||||||||||||||||||||||||||||||
| timeoutMs: 120_000, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| if (skillReviewResult.promise) { | ||||||||||||||||||||||||||||||||||||||
| this.pendingMemoryTaskPromises.push( | ||||||||||||||||||||||||||||||||||||||
| skillReviewResult.promise.then((record) => { | ||||||||||||||||||||||||||||||||||||||
| const touched = record.metadata?.['touchedSkillFiles']; | ||||||||||||||||||||||||||||||||||||||
| return Array.isArray(touched) ? touched.length : 0; | ||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| this.toolCallCount = 0; | ||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Two observable consequences:
Also, Fix: Either (a) move the reset to a real session-end / — claude-opus-4-7 via Qwen Code /review |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!this.config.getManagedAutoMemoryEnabled()) { | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const extractPromise = mgr | ||||||||||||||||||||||||||||||||||||||
| .scheduleExtract({ | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -581,6 +609,14 @@ export class GeminiClient { | |||||||||||||||||||||||||||||||||||||
| return promises; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| recordCompletedToolCall(toolName: string): void { | ||||||||||||||||||||||||||||||||||||||
| if (toolName === 'skill_manage') { | ||||||||||||||||||||||||||||||||||||||
| this.toolCallCount = 0; | ||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| this.toolCallCount += 1; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Consider weighting write operations higher, or counting only non-read-only tools. — deepseek-v4-pro via Qwen Code /review |
||||||||||||||||||||||||||||||||||||||
| async *sendMessageStream( | ||||||||||||||||||||||||||||||||||||||
| request: PartListUnion, | ||||||||||||||||||||||||||||||||||||||
| signal: AbortSignal, | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Critical] TS2353:
consumePendingMemoryTaskPromises在 mock 对象类型中不存在。GeminiClient新增了此方法(第 762 行),但 mock 类型未包含它,导致tsc --noEmit编译失败。— DeepSeek/deepseek-v4-pro via Qwen Code /review