dsh-vscode-mode 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -111
- package/assets/shell/dsh-open.cs +65 -149
- package/assets/shell/dsh-open.mjs +241 -0
- package/assets/shell/dsh-open.ps1 +28 -47
- package/assets/shell/dsh-open.sh +25 -66
- package/lib/client.js +13362 -11806
- package/lib/client.js.map +1 -1
- package/lib/index.js +4110 -2507
- package/lib/index.js.map +1 -1
- package/package.json +9 -8
- package/skills/dsh-vscodemode-mcp/SKILL.md +94 -132
- package/src/childEnv.ts +19 -0
- package/src/client/activePathStore.ts +28 -0
- package/src/client/addToConversation.ts +4 -2
- package/src/client/ai/inlineProvider.ts +1 -8
- package/src/client/commandBridge.ts +14 -42
- package/src/client/externalOpen.ts +40 -19
- package/src/client/index.ts +101 -18
- package/src/client/keybindings.ts +36 -73
- package/src/client/monaco/loader.ts +3 -1
- package/src/client/openFlow.ts +36 -28
- package/src/client/openReceipt.ts +64 -0
- package/src/client/panelCommands.ts +101 -0
- package/src/client/pdf/pdfLoader.ts +4 -2
- package/src/client/receiptState.ts +120 -0
- package/src/client/rpc.ts +30 -4
- package/src/client/shortcutRecorder.ts +161 -0
- package/src/client/shortcutsOfficial.ts +460 -0
- package/src/client/sidebar/panels/RulesPanel.ts +15 -11
- package/src/client/sidebarBridge.ts +7 -0
- package/src/client/ui/DiffBadge.ts +2 -1
- package/src/client/ui/EditorView.ts +246 -137
- package/src/client/ui/McpSettings.ts +10 -8
- package/src/client/ui/OfficialSideTab.ts +5 -2
- package/src/client/ui/QuickOpen.ts +16 -16
- package/src/client/ui/ShortcutSettings.ts +274 -0
- package/src/client/ui/commandCatalog.ts +39 -43
- package/src/client/useOpenReceipt.ts +128 -0
- package/src/compat.ts +5 -3
- package/src/dap/manager.ts +46 -13
- package/src/devForm.ts +252 -92
- package/src/externalHandoff.ts +93 -52
- package/src/fileOpenSettings.ts +4 -24
- package/src/hostImport.ts +19 -0
- package/src/httpGuard.ts +101 -0
- package/src/index.ts +106 -97
- package/src/integrate.ts +56 -50
- package/src/lsp/transport.ts +84 -127
- package/src/mcp.ts +58 -9
- package/src/mcpIsolation.ts +211 -102
- package/src/mcpProject.ts +220 -133
- package/src/mcpRuntime.ts +343 -0
- package/src/openBridge.ts +69 -0
- package/src/openInbox.ts +207 -0
- package/src/paths.ts +8 -5
- package/src/processTree.ts +63 -0
- package/src/reveal.ts +5 -3
- package/src/routes.ts +156 -154
- package/src/rpc.ts +14 -13
- package/src/rules.ts +100 -44
- package/src/shared/appUrl.ts +19 -0
- package/src/shared/externalOpen.ts +71 -0
- package/src/shared/integration.ts +2 -0
- package/src/shared/keybindings.ts +256 -0
- package/src/shared/mcp.ts +4 -1
- package/src/shared/rpc.ts +7 -4
- package/src/shared/rules.ts +9 -1
- package/src/shared/svn.ts +6 -8
- package/src/svnPlanTool.ts +51 -0
- package/src/unityBridge.ts +26 -3
- package/unity/com.dsh.editor/Editor/DshCodeEditor.cs +99 -203
- package/unity/com.dsh.editor/Editor/DshOpenWorker.cs +166 -0
- package/unity/com.dsh.editor/Editor/DshOpenWorker.cs.meta +2 -0
- package/unity/com.dsh.editor/README.md +16 -25
- package/unity/com.dsh.editor/package.json +2 -2
- package/src/client/ui/KeybindingsPanel.ts +0 -190
package/src/mcpIsolation.ts
CHANGED
|
@@ -1,155 +1,264 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
* 项目 MCP 连接仍由 Host 维护,但每个 agent 只继承当前 workspace 的项目工具;
|
|
4
|
-
* tools.restrict() 负责模型可见性,tools.guard() 负责执行层兜底拒绝。
|
|
5
|
-
* 作者 ddj 2026年08月22号
|
|
6
|
-
*/
|
|
1
|
+
/** Project MCP visibility, instruction shadows, and execution guards. @author ddj 2026年09月28号 */
|
|
2
|
+
import { posix, win32 } from 'node:path'
|
|
7
3
|
import { LEGACY_PROJECT_PREFIX, entryHash, isProjectEntryId } from './compat.js'
|
|
8
4
|
import { entriesOf } from './mcp.js'
|
|
9
5
|
import { hashWorkspace } from './mcpProject.js'
|
|
6
|
+
import { runtimeOf } from './mcpRuntime.js'
|
|
10
7
|
import type { Ctx } from './store.js'
|
|
11
|
-
import { log } from './log.js'
|
|
12
8
|
|
|
13
9
|
const UNKNOWN_PROJECT = '__unknown_project__'
|
|
14
|
-
|
|
10
|
+
const RESOURCE_TOOLS = new Set(['list_mcp_resources', 'list_mcp_resource_templates', 'read_mcp_resource'])
|
|
15
11
|
type ToolProjects = Map<string, string>
|
|
16
|
-
type AgentState = { dispose?: () => void; denyKey: string }
|
|
12
|
+
type AgentState = { dispose?: () => void; denyKey: string; shadows: Map<string, () => void> }
|
|
17
13
|
|
|
18
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Normalize path separators and Windows case while retaining POSIX case sensitivity.
|
|
16
|
+
* @public @author ddj 2026年09月28号
|
|
17
|
+
* @param path Workspace or agent cwd.
|
|
18
|
+
* @returns Comparable path with a stable root representation.
|
|
19
|
+
*/
|
|
19
20
|
export function normalizePath(path: string): string {
|
|
20
|
-
const
|
|
21
|
-
|
|
21
|
+
const raw = String(path ?? '').replace(/\\/g, '/')
|
|
22
|
+
const windows = /^[a-z]:/i.test(raw) || raw.startsWith('//')
|
|
23
|
+
const normalized = (windows ? win32.normalize(raw) : posix.normalize(raw)).replace(/\\/g, '/')
|
|
24
|
+
const value = normalized.replace(/\/+$/, '') || '/'
|
|
25
|
+
return windows ? value.toLowerCase() : value
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Match the most specific registered parent workspace.
|
|
30
|
+
* @public @author ddj 2026年09月28号
|
|
31
|
+
* @param cwd Agent working directory.
|
|
32
|
+
* @param paths Registered workspace paths.
|
|
33
|
+
* @returns Matching original path, or undefined.
|
|
34
|
+
*/
|
|
25
35
|
export function matchWorkspace(cwd: string | undefined, paths: string[]): string | undefined {
|
|
26
36
|
if (!cwd) return undefined
|
|
27
37
|
const target = normalizePath(cwd)
|
|
28
38
|
const matches = paths.filter((path) => {
|
|
29
39
|
const root = normalizePath(path)
|
|
30
|
-
return target === root || target.startsWith(root + '/')
|
|
40
|
+
return target === root || target.startsWith(root === '/' ? root : root + '/')
|
|
31
41
|
})
|
|
32
42
|
matches.sort((a, b) => normalizePath(b).length - normalizePath(a).length)
|
|
33
43
|
return matches[0]
|
|
34
44
|
}
|
|
35
45
|
|
|
36
|
-
/** 根据 entry id 判断是否为新旧格式项目 MCP(兼容层统一判定)。 */
|
|
37
46
|
export const isProjectEntry = isProjectEntryId
|
|
38
|
-
|
|
39
|
-
/** 兼容:旧版前缀常量名(mcpIsolation 历史导出,语义不变)。 */
|
|
40
47
|
export const LEGACY_PREFIX = LEGACY_PROJECT_PREFIX
|
|
41
48
|
|
|
42
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Select foreign project tool names.
|
|
51
|
+
* @public @author ddj 2026年09月28号
|
|
52
|
+
* @param toolProjects Tool ownership map.
|
|
53
|
+
* @param currentPath Matching workspace.
|
|
54
|
+
* @returns Sorted deny list.
|
|
55
|
+
*/
|
|
43
56
|
export function denyTools(toolProjects: ToolProjects, currentPath: string | undefined): string[] {
|
|
44
|
-
return [...toolProjects
|
|
57
|
+
return [...toolProjects].filter(([, owner]) => owner !== currentPath).map(([name]) => name).sort()
|
|
45
58
|
}
|
|
46
59
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Include historical global entries in ownership until migration has disposed them.
|
|
62
|
+
* @public @author ddj 2026年09月28号
|
|
63
|
+
* @param ctx Host services.
|
|
64
|
+
* @returns Server-to-workspace ownership map.
|
|
65
|
+
*/
|
|
66
|
+
export function serverOwners(ctx: Ctx): Map<string, string> {
|
|
67
|
+
const result = runtimeOf(ctx).owners()
|
|
68
|
+
const workspaces = ctx.get('workspaceRegistry')?.list?.() ?? []
|
|
69
|
+
const hashes = new Map(workspaces.map((ws: { path: string }) => [hashWorkspace(ws.path), ws.path]))
|
|
55
70
|
for (const entry of entriesOf(ctx)) {
|
|
56
71
|
const id = String(entry.id ?? entry.options?.id ?? '')
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const serverName = entry.options?.config?.serverName
|
|
61
|
-
if (typeof serverName !== 'string') continue
|
|
62
|
-
const prefix = `mcp__${serverName}__`
|
|
63
|
-
for (const name of visible.keys()) if (String(name).startsWith(prefix)) result.set(String(name), owner)
|
|
72
|
+
const name = entry.options?.config?.serverName
|
|
73
|
+
if (!isProjectEntry(id) || typeof name !== 'string') continue
|
|
74
|
+
result.set(name, String(hashes.get(entryHash(id)) ?? UNKNOWN_PROJECT))
|
|
64
75
|
}
|
|
65
76
|
return result
|
|
66
77
|
}
|
|
67
78
|
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Resolve managed server attribution for namespaced tools and shared resource calls.
|
|
81
|
+
* @public @author ddj 2026年09月28号
|
|
82
|
+
* @param exec Tool execution with arguments.server for shared resource tools.
|
|
83
|
+
* @returns Attributed server name, or undefined for unrelated tools.
|
|
84
|
+
*/
|
|
85
|
+
export function callServer(exec: any): string | undefined {
|
|
86
|
+
const name = String(exec?.name ?? '')
|
|
87
|
+
if (RESOURCE_TOOLS.has(name)) return typeof exec?.arguments?.server === 'string' ? exec.arguments.server : undefined
|
|
88
|
+
return /^mcp__(.+?)__/.exec(name)?.[1]
|
|
73
89
|
}
|
|
74
90
|
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Check workspace ownership before any tool or resource network operation.
|
|
93
|
+
* @public @author ddj 2026年09月28号
|
|
94
|
+
* @param ctx Host services.
|
|
95
|
+
* @param exec Caller and tool arguments.
|
|
96
|
+
* @returns A denial message, or undefined for an allowed/global call.
|
|
97
|
+
*/
|
|
98
|
+
export function guardMcp(ctx: Ctx, exec: any): string | undefined {
|
|
99
|
+
const name = String(exec?.name ?? '')
|
|
100
|
+
if (!RESOURCE_TOOLS.has(name) && !name.startsWith('mcp__')) return undefined
|
|
101
|
+
const server = callServer(exec)
|
|
102
|
+
const owners = serverOwners(ctx)
|
|
103
|
+
const current = runtimeOf(ctx).workspace(exec.agent)
|
|
104
|
+
const owned = RESOURCE_TOOLS.has(name) ? [...owners].filter(([key]) => key === server)
|
|
105
|
+
: [...owners].filter(([key]) => name.startsWith('mcp__' + key + '__'))
|
|
106
|
+
if (!owned.some(([, owner]) => owner !== current)) return undefined
|
|
107
|
+
return current === undefined ? '项目 MCP 需要在已注册工作区的对话中使用' : '已拒绝:当前对话工作区不能使用其他项目的 MCP'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Restrict inherited names using the public restrictableNames set, even after already hidden.
|
|
112
|
+
* @private @author ddj 2026年09月28号
|
|
113
|
+
* @param ctx Host services.
|
|
114
|
+
* @param agent Target scope.
|
|
115
|
+
* @param state Owned restriction disposers.
|
|
116
|
+
* @param owners Server ownership.
|
|
117
|
+
* @sideEffects Replaces only this plugin's scoped restriction.
|
|
118
|
+
*/
|
|
119
|
+
function syncTools(ctx: Ctx, agent: any, state: AgentState, owners: Map<string, string>): void {
|
|
120
|
+
const tools = agent.ctx.get('tools')
|
|
121
|
+
const view = tools?.view?.(agent)
|
|
122
|
+
const names: Iterable<string> = view?.restrictableNames ?? view?.knownNames ?? view?.visible?.keys?.() ?? []
|
|
123
|
+
const current = runtimeOf(ctx).workspace(agent)
|
|
124
|
+
const denied = [...names].filter((name) => {
|
|
125
|
+
return [...owners].some(([server, owner]) => name.startsWith('mcp__' + server + '__') && owner !== current)
|
|
126
|
+
}).sort()
|
|
127
|
+
const key = denied.join('\n')
|
|
128
|
+
if (key === state.denyKey) return
|
|
129
|
+
state.denyKey = key
|
|
130
|
+
let dispose: (() => void) | undefined
|
|
131
|
+
try { dispose = denied.length > 0 ? tools.restrict({ deny: denied }) : undefined }
|
|
132
|
+
catch (error) { state.denyKey = ''; throw error }
|
|
81
133
|
state.dispose?.()
|
|
82
|
-
state.dispose =
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
134
|
+
state.dispose = dispose
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Filter the official assembled server list, retaining unmanaged and resource-only providers.
|
|
139
|
+
* @public @author ddj 2026年09月28号
|
|
140
|
+
* @param ctx Host services.
|
|
141
|
+
* @param agent Actual assembly scope, including a descendant of the listening scope.
|
|
142
|
+
* @param text Official mcp-resource-servers section text.
|
|
143
|
+
* @returns Scope-visible resource help; unfamiliar list formats fail closed for foreign projects.
|
|
144
|
+
*/
|
|
145
|
+
export function filterResources(ctx: Ctx, agent: any, text: string): string {
|
|
146
|
+
const current = runtimeOf(ctx).workspace(agent)
|
|
147
|
+
const owners = serverOwners(ctx)
|
|
148
|
+
const foreign = new Set([...owners].filter(([, owner]) => owner !== current).map(([name]) => name))
|
|
149
|
+
if (!foreign.size || !text) return text
|
|
150
|
+
const match = /(\[[^\n]*\])\.\s*$/.exec(text)
|
|
151
|
+
if (!match) return ''
|
|
152
|
+
let names: unknown
|
|
153
|
+
try { names = JSON.parse(match[1]) } catch { return '' }
|
|
154
|
+
if (!Array.isArray(names) || names.some((name) => typeof name !== 'string')) return ''
|
|
155
|
+
const visible = names.filter((name) => !foreign.has(name))
|
|
156
|
+
return visible.length ? text.slice(0, match.index) + JSON.stringify(visible) + '.' : ''
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Apply scope-specific resource visibility to the public assembled prompt snapshot.
|
|
161
|
+
* @private @author ddj 2026年09月28号
|
|
162
|
+
* @param ctx Host services.
|
|
163
|
+
* @param assembly Downstream assembly result.
|
|
164
|
+
* @param agent Actual viewing scope.
|
|
165
|
+
* @returns Detached section list; all unrelated sections retain their original identity.
|
|
166
|
+
*/
|
|
167
|
+
function filterAssembly(ctx: Ctx, assembly: any, agent: any): any {
|
|
168
|
+
const sections = assembly.sections.map((section: any) => section.name === 'mcp-resource-servers'
|
|
169
|
+
? { ...section, text: filterResources(ctx, agent, section.text) } : section)
|
|
170
|
+
return { ...assembly, sections }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Shadow inherited foreign instructions without changing the scope's parent chain.
|
|
175
|
+
* @private @author ddj 2026年09月28号
|
|
176
|
+
* @param ctx Host services.
|
|
177
|
+
* @param agent Target scope.
|
|
178
|
+
* @param state Owned prompt registrations.
|
|
179
|
+
* @param owners Current project server ownership.
|
|
180
|
+
* @sideEffects Registers blank foreign instruction sections in the exact agent scope.
|
|
181
|
+
*/
|
|
182
|
+
function syncPrompts(ctx: Ctx, agent: any, state: AgentState, owners: Map<string, string>): void {
|
|
183
|
+
const prompt = agent.ctx.get('systemPrompt')
|
|
184
|
+
if (!prompt) return
|
|
185
|
+
const current = runtimeOf(ctx).workspace(agent)
|
|
186
|
+
const foreign = new Set([...owners].filter(([, path]) => path !== current).map(([name]) => name))
|
|
187
|
+
for (const [name, dispose] of state.shadows) {
|
|
188
|
+
if (foreign.has(name)) continue
|
|
189
|
+
dispose()
|
|
190
|
+
state.shadows.delete(name)
|
|
191
|
+
}
|
|
192
|
+
const order = prompt.getSectionOrder('MCP_SERVERS')
|
|
193
|
+
for (const name of foreign) {
|
|
194
|
+
if (state.shadows.has(name)) continue
|
|
195
|
+
state.shadows.set(name, prompt.section({ name: 'mcp:' + name, order, interpolate: false, text: '' }))
|
|
90
196
|
}
|
|
91
|
-
state.denyKey = nextKey
|
|
92
197
|
}
|
|
93
198
|
|
|
94
|
-
/**
|
|
199
|
+
/**
|
|
200
|
+
* Dispose only isolation effects owned for one agent.
|
|
201
|
+
* @private @author ddj 2026年09月28号
|
|
202
|
+
* @param state Stored effect handles.
|
|
203
|
+
* @sideEffects Removes restrictions, blank instruction shadows, and resource help override.
|
|
204
|
+
*/
|
|
205
|
+
function clearState(state: AgentState): void {
|
|
206
|
+
state.dispose?.()
|
|
207
|
+
for (const dispose of state.shadows.values()) dispose()
|
|
208
|
+
state.shadows.clear()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Install synchronous creation isolation and guards before installing the MCP runtime.
|
|
213
|
+
* @public @author ddj 2026年09月28号
|
|
214
|
+
* @param ctx Host lifecycle context.
|
|
215
|
+
* @sideEffects Owns per-agent restrictions and prompt shadows plus a global execution guard.
|
|
216
|
+
*/
|
|
95
217
|
export function installIsolation(ctx: Ctx): void {
|
|
96
218
|
const tools = ctx.get('tools')
|
|
97
219
|
const agents = ctx.get('agents')
|
|
98
220
|
if (!tools || !agents) return
|
|
99
221
|
const states = new Map<any, AgentState>()
|
|
100
|
-
let toolProjects = projectTools(ctx)
|
|
101
|
-
let scheduled = false
|
|
102
222
|
let syncing = false
|
|
103
|
-
|
|
223
|
+
const syncAgent = (agent: any) => {
|
|
224
|
+
const state = states.get(agent) ?? { denyKey: '', shadows: new Map() }
|
|
225
|
+
states.set(agent, state)
|
|
226
|
+
const owners = serverOwners(ctx)
|
|
227
|
+
syncTools(ctx, agent, state, owners)
|
|
228
|
+
syncPrompts(ctx, agent, state, owners)
|
|
229
|
+
}
|
|
104
230
|
const syncAll = () => {
|
|
105
231
|
if (syncing) return
|
|
106
232
|
syncing = true
|
|
107
|
-
try {
|
|
108
|
-
|
|
109
|
-
for (const agent of agents.list?.() ?? []) {
|
|
110
|
-
const state = states.get(agent) ?? { denyKey: '' }
|
|
111
|
-
states.set(agent, state)
|
|
112
|
-
syncAgent(ctx, state, agent, toolProjects)
|
|
113
|
-
}
|
|
114
|
-
} finally {
|
|
115
|
-
syncing = false
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const scheduleSync = () => {
|
|
119
|
-
if (scheduled) return
|
|
120
|
-
scheduled = true
|
|
121
|
-
queueMicrotask(() => {
|
|
122
|
-
scheduled = false
|
|
123
|
-
syncAll()
|
|
124
|
-
})
|
|
233
|
+
try { for (const agent of agents.list?.() ?? []) syncAgent(agent) }
|
|
234
|
+
finally { syncing = false }
|
|
125
235
|
}
|
|
126
|
-
const onCreated =
|
|
127
|
-
|
|
128
|
-
states.set(agent, state)
|
|
129
|
-
syncAgent(ctx, state, agent, toolProjects)
|
|
130
|
-
})
|
|
131
|
-
const onDisposed = ctx.on('agent/disposed', ({ agent }: { agent: any }) => {
|
|
236
|
+
const onCreated = ({ agent }: { agent: any }) => { syncAgent(agent) }
|
|
237
|
+
const onDisposed = ({ agent }: { agent: any }) => {
|
|
132
238
|
const state = states.get(agent)
|
|
133
|
-
state
|
|
239
|
+
if (state) clearState(state)
|
|
134
240
|
states.delete(agent)
|
|
135
|
-
}
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
for (const
|
|
241
|
+
}
|
|
242
|
+
const guard = (exec: any) => guardMcp(ctx, exec)
|
|
243
|
+
/**
|
|
244
|
+
* Filter the completed assembly using its actual viewing scope.
|
|
245
|
+
* @private @author ddj 2026年09月28号
|
|
246
|
+
* @param assembly Initial prompt snapshot.
|
|
247
|
+
* @param context Assembly scope.
|
|
248
|
+
* @param next Downstream assembly continuation.
|
|
249
|
+
* @returns Completed prompt with foreign project resource names removed.
|
|
250
|
+
*/
|
|
251
|
+
const filterPrompt = async (assembly: any, context: any, next: () => Promise<any>) => {
|
|
252
|
+
return filterAssembly(ctx, await next(), context.scope)
|
|
253
|
+
}
|
|
254
|
+
const disposers = [ctx.on('agent/created', onCreated), ctx.on('agent/disposed', onDisposed),
|
|
255
|
+
ctx.on('tools/change', syncAll), ctx.on('vscode-mode/mcp-change', syncAll),
|
|
256
|
+
ctx.on('system-prompt/assemble', filterPrompt), tools.guard(guard)]
|
|
257
|
+
const cleanup = () => {
|
|
258
|
+
for (const dispose of disposers) dispose?.()
|
|
259
|
+
for (const state of states.values()) clearState(state)
|
|
153
260
|
states.clear()
|
|
154
|
-
}
|
|
261
|
+
}
|
|
262
|
+
ctx.effect(() => cleanup, 'vscode-mode:mcp-isolation')
|
|
263
|
+
syncAll()
|
|
155
264
|
}
|