dsh-agent-plugins-market 0.9.2 → 0.11.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 +6 -6
- package/README.zh.md +6 -6
- package/client/client.js +339 -223
- package/client/client.js.map +1 -1
- package/lib/packages/market-bundle/src/application/catalog.js +8 -0
- package/lib/packages/market-bundle/src/index.js +4 -4
- package/lib/packages/market-bundle/src/platform/settings-namespace.js +4 -16
- package/lib/packages/market-bundle/src/platform/settings-schema.js +6 -6
- package/lib/packages/market-bundle/src/routes.js +14 -0
- package/lib/packages/market-catalog/src/scanning/archive.js +31 -0
- package/lib/packages/market-catalog/src/scanning/validate.js +18 -1
- package/lib/packages/market-contracts/src/contracts/market.js +1 -0
- package/lib/packages/market-contracts/src/contracts/settings.js +20 -43
- package/lib/packages/market-contracts/src/ports/ports.js +1 -0
- package/lib/packages/market-mcp/src/application/mcp/mcp-direct-config.js +28 -0
- package/lib/packages/market-mcp/src/application/mcp/mcp-status.js +36 -8
- package/lib/packages/market-mcp/src/application/mcp-service.js +44 -3
- package/lib/packages/market-mcp/src/runtime/mcp/bridge/connection.js +42 -23
- package/lib/packages/market-mcp/src/runtime/mcp/bridge/oauth.js +29 -2
- package/lib/packages/market-mcp/src/runtime/mcp/bridge/transport.js +8 -5
- package/lib/packages/market-runtime/src/runtime/agents/agent-role-router.js +2 -1
- package/lib/packages/market-runtime/src/runtime/agents/subagent-seam.js +29 -0
- package/lib/packages/market-runtime/src/runtime/agents/teammate-role-runtime.js +8 -2
- package/lib/packages/market-runtime/src/runtime/core/reconciler.js +12 -0
- package/lib/packages/market-runtime/src/runtime/host/scoped-contributors.js +4 -0
- package/lib/packages/market-runtime/src/runtime/surfaces/project-runtime.js +4 -1
- package/lib/types/packages/market-bundle/src/application/catalog.d.ts +6 -0
- package/lib/types/packages/market-bundle/src/index.d.ts +1 -1
- package/lib/types/packages/market-bundle/src/platform/settings-namespace.d.ts +4 -18
- package/lib/types/packages/market-bundle/src/platform/settings-schema.d.ts +6 -6
- package/lib/types/packages/market-contracts/src/contracts/market.d.ts +1 -0
- package/lib/types/packages/market-contracts/src/contracts/mcp-status.d.ts +16 -0
- package/lib/types/packages/market-contracts/src/contracts/settings.d.ts +15 -39
- package/lib/types/packages/market-contracts/src/ports/ports.d.ts +11 -0
- package/lib/types/packages/market-contracts/src/ports/queries.d.ts +2 -0
- package/lib/types/packages/market-mcp/src/application/mcp/mcp-direct-config.d.ts +12 -0
- package/lib/types/packages/market-mcp/src/application/mcp/mcp-status.d.ts +8 -2
- package/lib/types/packages/market-mcp/src/application/mcp-service.d.ts +12 -0
- package/lib/types/packages/market-mcp/src/runtime/mcp/bridge/connection.d.ts +4 -1
- package/lib/types/packages/market-mcp/src/runtime/mcp/bridge/oauth.d.ts +14 -0
- package/lib/types/packages/market-mcp/src/runtime/mcp/bridge/transport.d.ts +5 -1
- package/lib/types/packages/market-runtime/src/runtime/agents/agent-role-router.d.ts +2 -10
- package/lib/types/packages/market-runtime/src/runtime/agents/subagent-seam.d.ts +63 -0
- package/lib/types/packages/market-runtime/src/runtime/core/reconciler.d.ts +13 -0
- package/package.json +67 -62
- package/schemas/1.0.0/mcp.schema.json +0 -120
- package/schemas/1.0.0/plugin.schema.json +0 -65
- package/schemas/1.1.0/mcp.schema.json +0 -120
- package/schemas/1.1.0/plugin.schema.json +0 -65
- package/schemas/README.md +0 -79
- package/schemas/agent-plugins/spec.md +0 -62
- package/schemas/claude-code/marketplace.schema.json +0 -143
- package/schemas/claude-code/plugin.schema.json +0 -105
- package/schemas/claude-code/spec.md +0 -108
- package/schemas/codex/marketplace.schema.json +0 -78
- package/schemas/codex/plugin.schema.json +0 -77
- package/schemas/codex/spec.md +0 -91
- package/schemas/com.deepseek.harness/namespace.schema.json +0 -42
- package/schemas/com.deepseek.harness/spec.md +0 -52
- package/schemas/cursor/marketplace.schema.json +0 -82
- package/schemas/cursor/plugin.schema.json +0 -58
- package/schemas/cursor/spec.md +0 -92
- package/schemas/github-copilot/marketplace.schema.json +0 -107
- package/schemas/github-copilot/plugin.schema.json +0 -89
- package/schemas/github-copilot/spec.md +0 -105
- package/schemas/kimi/marketplace.schema.json +0 -38
- package/schemas/kimi/plugin.schema.json +0 -84
- package/schemas/kimi/spec.md +0 -94
- package/schemas/qoder/marketplace.schema.json +0 -115
- package/schemas/qoder/plugin.schema.json +0 -127
- package/schemas/qoder/spec.md +0 -111
- package/schemas/skill-collection/spec.md +0 -37
- package/schemas/universal/marketplace.schema.json +0 -98
- package/schemas/universal/plugin.schema.json +0 -49
- package/schemas/universal/spec.md +0 -53
- package/schemas/zcode/marketplace.schema.json +0 -161
- package/schemas/zcode/plugin.schema.json +0 -89
- package/schemas/zcode/spec.md +0 -102
|
@@ -132,6 +132,14 @@ export class LoopbackOAuthClientProvider {
|
|
|
132
132
|
expectedState;
|
|
133
133
|
/** Resolves `true` when the leg saved tokens, `false` when it failed; undefined while no leg is open. */
|
|
134
134
|
browserLegSettled;
|
|
135
|
+
/**
|
|
136
|
+
* Outcome of the most recently finished browser leg, `true` when the code
|
|
137
|
+
* exchange saved tokens. The pending-leg promise disappears when the leg
|
|
138
|
+
* settles, and a leg that finishes inside the `connect` that raised the
|
|
139
|
+
* `401` leaves nothing pending for a supervisor to observe — this field is
|
|
140
|
+
* how the supervisor still learns that the authorization succeeded.
|
|
141
|
+
*/
|
|
142
|
+
lastLegOutcome;
|
|
135
143
|
callbackServer;
|
|
136
144
|
callbackTimer;
|
|
137
145
|
boundPort;
|
|
@@ -149,7 +157,10 @@ export class LoopbackOAuthClientProvider {
|
|
|
149
157
|
get redirectUrl() {
|
|
150
158
|
// RFC 8252 §7.3: loopback redirects may vary the port per request, so the
|
|
151
159
|
// bound port is filled in when the listener starts and registration
|
|
152
|
-
// metadata always reflects the current attempt's URL.
|
|
160
|
+
// metadata always reflects the current attempt's URL. The value must keep
|
|
161
|
+
// reading as the bound port until the code exchange finishes: the token
|
|
162
|
+
// request repeats this exact URL (RFC 6749 §4.1.3), and an authorization
|
|
163
|
+
// server that compares it with the authorization request rejects port `0`.
|
|
153
164
|
return `http://127.0.0.1:${this.boundPort ?? this.storage.callbackPort ?? 0}/callback`;
|
|
154
165
|
}
|
|
155
166
|
get clientMetadata() {
|
|
@@ -214,6 +225,12 @@ export class LoopbackOAuthClientProvider {
|
|
|
214
225
|
return;
|
|
215
226
|
}
|
|
216
227
|
finally {
|
|
228
|
+
// The loopback URL stays published until here. Clearing the port when
|
|
229
|
+
// the callback arrives would hand the token endpoint `redirect_uri =
|
|
230
|
+
// http://127.0.0.1:0/callback` while the authorization request carried
|
|
231
|
+
// the real port, which RFC 6749 §4.1.3 forbids and servers enforce.
|
|
232
|
+
this.boundPort = undefined;
|
|
233
|
+
this.lastLegOutcome = succeeded;
|
|
217
234
|
this.browserLegSettled?.resolve(succeeded);
|
|
218
235
|
this.browserLegSettled = undefined;
|
|
219
236
|
}
|
|
@@ -232,6 +249,14 @@ export class LoopbackOAuthClientProvider {
|
|
|
232
249
|
get browserLeg() {
|
|
233
250
|
return this.browserLegSettled?.promise;
|
|
234
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* The most recent finished leg's outcome; `undefined` until one finishes.
|
|
254
|
+
* The supervisor reads it after a failed attempt: a leg that settled inside
|
|
255
|
+
* the attempt itself leaves no pending {@link browserLeg} behind.
|
|
256
|
+
*/
|
|
257
|
+
get lastBrowserLegOutcome() {
|
|
258
|
+
return this.lastLegOutcome;
|
|
259
|
+
}
|
|
235
260
|
/**
|
|
236
261
|
* Register the transport generation whose `finishAuth` completes the flow.
|
|
237
262
|
* The transport calls `redirectToAuthorization` during `connect`; binding
|
|
@@ -420,7 +445,9 @@ export class LoopbackOAuthClientProvider {
|
|
|
420
445
|
}
|
|
421
446
|
this.clearCallbackTimer();
|
|
422
447
|
this.callbackServer = undefined;
|
|
423
|
-
this
|
|
448
|
+
// The bound port deliberately survives this callback: the code exchange
|
|
449
|
+
// runs a moment later and must present the same `redirect_uri` the
|
|
450
|
+
// authorization request used. An aborted leg clears it in resetCallback.
|
|
424
451
|
this.expectedState = undefined;
|
|
425
452
|
if (oauthError !== null) {
|
|
426
453
|
const description = redactErrorMessage(url.searchParams.get('error_description') ?? '');
|
|
@@ -33,11 +33,11 @@ function hasStaticAuthorization(headers) {
|
|
|
33
33
|
* when the config opts out or the headers already carry a static
|
|
34
34
|
* Authorization.
|
|
35
35
|
*/
|
|
36
|
-
function buildOAuthProvider(config, credentials, log) {
|
|
36
|
+
function buildOAuthProvider(config, credentials, log, opener) {
|
|
37
37
|
const auth = config.auth;
|
|
38
38
|
if (auth?.enabled === false || hasStaticAuthorization(config.headers))
|
|
39
39
|
return undefined;
|
|
40
|
-
return new LoopbackOAuthClientProvider(config.serverName, auth?.storage ?? {}, auth?.scope, log, credentials);
|
|
40
|
+
return new LoopbackOAuthClientProvider(config.serverName, auth?.storage ?? {}, auth?.scope, log, credentials, opener);
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Create an MCP transport from the resolved bridge config.
|
|
@@ -54,11 +54,14 @@ function buildOAuthProvider(config, credentials, log) {
|
|
|
54
54
|
* @param credentials - The composition's credential store, when mounted; OAuth
|
|
55
55
|
* state persists only when it is.
|
|
56
56
|
* @param log - Diagnostic sink for the human leg of the flow.
|
|
57
|
+
* @param opener - Browser opener for the OAuth human leg; the platform browser
|
|
58
|
+
* by default. A caller that owns the human leg (a host surface, or a test
|
|
59
|
+
* driving the authorization endpoint directly) passes its own.
|
|
57
60
|
* @returns The transport plus its OAuth provider, when active — the
|
|
58
61
|
* supervisor queries the provider to hold the generation open while a
|
|
59
62
|
* browser leg is pending.
|
|
60
63
|
*/
|
|
61
|
-
export function createTransport(config, credentials, log = () => { }) {
|
|
64
|
+
export function createTransport(config, credentials, log = () => { }, opener) {
|
|
62
65
|
switch (config.transport) {
|
|
63
66
|
case 'stdio':
|
|
64
67
|
return {
|
|
@@ -71,7 +74,7 @@ export function createTransport(config, credentials, log = () => { }) {
|
|
|
71
74
|
oauthProvider: undefined
|
|
72
75
|
};
|
|
73
76
|
case 'streamable-http': {
|
|
74
|
-
const oauthProvider = buildOAuthProvider(config, credentials, log);
|
|
77
|
+
const oauthProvider = buildOAuthProvider(config, credentials, log, opener);
|
|
75
78
|
// The MCP SDK's StreamableHTTPClientTransport has optional callback
|
|
76
79
|
// properties typed without `| undefined` (exactOptionalPropertyTypes
|
|
77
80
|
// mismatch with the Transport interface); the SDK constructed the
|
|
@@ -86,7 +89,7 @@ export function createTransport(config, credentials, log = () => { }) {
|
|
|
86
89
|
return { transport, oauthProvider };
|
|
87
90
|
}
|
|
88
91
|
case 'sse': {
|
|
89
|
-
const oauthProvider = buildOAuthProvider(config, credentials, log);
|
|
92
|
+
const oauthProvider = buildOAuthProvider(config, credentials, log, opener);
|
|
90
93
|
// The legacy SSE transport negotiates a separate endpoint POST channel.
|
|
91
94
|
// The SDK applies `requestInit.headers` to both the stream request and
|
|
92
95
|
// every endpoint POST (its `_commonHeaders` merge), so no separate
|
|
@@ -6,6 +6,7 @@ import { isAbsentPath } from '../../application/fs-probe.js';
|
|
|
6
6
|
import { namedAgentRoles } from './agent-role-names.js';
|
|
7
7
|
import { expandPluginPaths } from '../../../../market-catalog/src/index.js';
|
|
8
8
|
import { parseAgentRole } from '../../application/agent-roles.js';
|
|
9
|
+
import { startContinuableChild } from './subagent-seam.js';
|
|
9
10
|
export { parseAgentRole };
|
|
10
11
|
export const AGENT_ROLE_TOOL_NAME = 'subagent_role';
|
|
11
12
|
/**
|
|
@@ -217,7 +218,7 @@ export async function executeAgentRole(host, listRoles, agentName, prompt, paren
|
|
|
217
218
|
...(agentOptions === undefined ? {} : { agentOptions })
|
|
218
219
|
};
|
|
219
220
|
if (mode === 'continuable') {
|
|
220
|
-
const started = await host.subagents
|
|
221
|
+
const started = await startContinuableChild(host.subagents, { provider: 'spawn', label: agentName, request, signal });
|
|
221
222
|
return { kind: 'continuable', subagentId: started.childId };
|
|
222
223
|
}
|
|
223
224
|
const run = await host.subagents.start('spawn', { ...request, signal });
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start one continuable child on whichever start member the host exposes.
|
|
3
|
+
* @param seam - the live `ctx.subagents` service.
|
|
4
|
+
* @param input - provider, label, delegation request, and cancellation.
|
|
5
|
+
* @returns the child identity the host reserved.
|
|
6
|
+
* @throws when the host exposes neither start member.
|
|
7
|
+
*/
|
|
8
|
+
export function startContinuableChild(seam, input) {
|
|
9
|
+
if (typeof seam.startContinuable === 'function')
|
|
10
|
+
return seam.startContinuable(input);
|
|
11
|
+
if (typeof seam.startActivation === 'function')
|
|
12
|
+
return seam.startActivation({ ...input, delivery: 'parent' });
|
|
13
|
+
return Promise.reject(new Error('the host subagent service exposes neither startContinuable nor startActivation'));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Release selected resident continuable children on whichever drain member the
|
|
17
|
+
* host exposes.
|
|
18
|
+
* @param seam - the live `ctx.subagents` service.
|
|
19
|
+
* @param parent - exact live direct parent authorizing the release.
|
|
20
|
+
* @param childIds - durable direct-child ids to release when resident.
|
|
21
|
+
* @throws when the host exposes neither drain member.
|
|
22
|
+
*/
|
|
23
|
+
export function drainContinuableChildIds(seam, parent, childIds) {
|
|
24
|
+
if (typeof seam.drainContinuableChildren === 'function')
|
|
25
|
+
return seam.drainContinuableChildren(parent, childIds);
|
|
26
|
+
if (typeof seam.drainChildren === 'function')
|
|
27
|
+
return seam.drainChildren(parent, childIds);
|
|
28
|
+
return Promise.reject(new Error('the host subagent service exposes neither drainContinuableChildren nor drainChildren'));
|
|
29
|
+
}
|
|
@@ -4,6 +4,7 @@ import { installModelSelection } from '@deepseek-ai/dsh-agent';
|
|
|
4
4
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
5
5
|
import { foldSubagentDescriptor } from '@deepseek-ai/dsh-subagent';
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
+
import { drainContinuableChildIds } from './subagent-seam.js';
|
|
7
8
|
const PROVIDER = 'market-role-team';
|
|
8
9
|
export const TEAM_ROLE_SOURCE = 'teammate-role-binding';
|
|
9
10
|
const routeSchema = z.object({ provider: z.string().min(1), model: z.string().min(1), reasoningEffort: z.string().min(1).optional() }).strict();
|
|
@@ -79,10 +80,15 @@ export class TeammateRoleRuntime {
|
|
|
79
80
|
this.ctx = ctx;
|
|
80
81
|
this.teams = ctx.get('agentTeams');
|
|
81
82
|
this.subagents = ctx.get('subagents');
|
|
83
|
+
// The 0.2.1-alpha.4 host removed `toolFilter` and `persona` from the capability
|
|
84
|
+
// record and added `agentPreset`; the 0.2.0-rc.2 baseline types require the first
|
|
85
|
+
// two. One variable satisfies both shapes because the host reads only the flags
|
|
86
|
+
// it knows, while an object literal would fail the newer excess-property check.
|
|
87
|
+
const capabilities = { agentOptions: false, agentPreset: false, outputSchema: false, depthLimit: false, toolFilter: false, persona: false };
|
|
82
88
|
this.registrations.push(this.subagents.registerProvider({
|
|
83
89
|
name: PROVIDER,
|
|
84
90
|
inheritsParentContext: false,
|
|
85
|
-
capabilities
|
|
91
|
+
capabilities,
|
|
86
92
|
start: () => Promise.reject(new Error('role teammates require Team continuation')),
|
|
87
93
|
prepareContinuable: request => {
|
|
88
94
|
const pending = this.pending.getStore();
|
|
@@ -227,7 +233,7 @@ export class TeammateRoleRuntime {
|
|
|
227
233
|
byParent.set(parent, [...(byParent.get(parent) ?? []), agent.id]);
|
|
228
234
|
}
|
|
229
235
|
try {
|
|
230
|
-
await Promise.all([...byParent].map(([parent, ids]) => this.subagents
|
|
236
|
+
await Promise.all([...byParent].map(([parent, ids]) => drainContinuableChildIds(this.subagents, parent, ids)));
|
|
231
237
|
}
|
|
232
238
|
finally {
|
|
233
239
|
for (const off of this.registrations.splice(0).reverse())
|
|
@@ -111,6 +111,18 @@ export class RuntimeReconciler {
|
|
|
111
111
|
mcpServerOwner(serverName) {
|
|
112
112
|
return this.mcp.serverOwner(serverName);
|
|
113
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* The suite/server pairs a live session's selection keeps mounted.
|
|
116
|
+
*
|
|
117
|
+
* This is the demand the mount pass merged in as `enabled: true`, so a server
|
|
118
|
+
* listed here stays live however its stored override reads. The MCP status
|
|
119
|
+
* surface needs it to tell a server its user switched off — kept mounted by
|
|
120
|
+
* that user's own preset — from one whose teardown genuinely failed.
|
|
121
|
+
* @returns the demanded pairs, in suite-then-server order.
|
|
122
|
+
*/
|
|
123
|
+
demandedMcpMounts() {
|
|
124
|
+
return [...this.demandedMcp].flatMap(([suiteId, keys]) => [...keys].map(serverKey => ({ suiteId, serverKey })));
|
|
125
|
+
}
|
|
114
126
|
/** Publish fresh immutable authority without mounting or waiting behind network work. */
|
|
115
127
|
setCatalogAuthority(candidates, global) {
|
|
116
128
|
if (this.disposed)
|
|
@@ -98,6 +98,10 @@ export class ScopedExtensionContributors {
|
|
|
98
98
|
scope.systemPrompt.section({
|
|
99
99
|
name: 'agent-plugins:instructions',
|
|
100
100
|
order: 500,
|
|
101
|
+
// Suite-authored Markdown is literal text, never a template: the host
|
|
102
|
+
// interpolates by default and refuses an unknown `{{name}}`, so a suite
|
|
103
|
+
// that quotes `{{cwd}}` would fail prompt assembly before any request.
|
|
104
|
+
interpolate: false,
|
|
101
105
|
text: () => mount.active
|
|
102
106
|
? mount.instructions
|
|
103
107
|
.filter(row => this.ports.allows(agent, row.parent) && this.ports.allows(agent, row.id))
|
|
@@ -28,7 +28,10 @@ suiteAllowed) {
|
|
|
28
28
|
return mountProjectSurface(ctx, catalog, 'systemPrompt', (scope, agent) => {
|
|
29
29
|
let text = '';
|
|
30
30
|
const host = scope;
|
|
31
|
-
|
|
31
|
+
// Suite-authored Markdown is literal text, never a template: the host
|
|
32
|
+
// interpolates by default and refuses an unknown `{{name}}`, so a suite that
|
|
33
|
+
// quotes `{{cwd}}` would fail prompt assembly before any model request.
|
|
34
|
+
const dispose = host.systemPrompt.section({ name: 'agent-plugins:instructions', order: 500, interpolate: false, text: () => text });
|
|
32
35
|
return {
|
|
33
36
|
async reconcile(projectSuites) {
|
|
34
37
|
const projectDir = agent.session.header.cwd;
|
|
@@ -181,6 +181,12 @@ export declare class Catalog implements MarketService {
|
|
|
181
181
|
mcpStatus(): Promise<McpStatusPayload>;
|
|
182
182
|
/** Validate and persist a user-owned MCP service with optional policy before reconciling its mount. */
|
|
183
183
|
addMcpServer(name: string, server: unknown, policy?: unknown): Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* Remove one user-owned MCP service, its override record, and its OAuth
|
|
186
|
+
* grant. A server a suite declares is refused: that declaration is
|
|
187
|
+
* source-owned, so disabling it is the supported operation.
|
|
188
|
+
*/
|
|
189
|
+
removeMcpServer(suiteKey: string, serverKey: string): Promise<void>;
|
|
184
190
|
/**
|
|
185
191
|
* Read a service without credential literals, or an unsaved template when
|
|
186
192
|
* `create` is true. The two transports have separate owners, so this facade
|
|
@@ -40,7 +40,7 @@ export interface Config {
|
|
|
40
40
|
autoUpdateSources: Volatile<boolean | undefined>;
|
|
41
41
|
/** UI translation switch; updated live through the host settings service. */
|
|
42
42
|
translationEnabled: Volatile<boolean | undefined>;
|
|
43
|
-
/** Agent preset manager switch
|
|
43
|
+
/** Agent preset manager switch; updated live through the host settings service. */
|
|
44
44
|
agentPresetsEnabled: Volatile<boolean | undefined>;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
@@ -8,9 +8,7 @@
|
|
|
8
8
|
* Every consumer reads through {@link MarketSettingsNamespace.settings} so
|
|
9
9
|
* "the field is absent" has exactly one answer —
|
|
10
10
|
* {@link resolveMarketSettings} — instead of each caller inventing its own
|
|
11
|
-
* fallback for the window before the host applies its first value.
|
|
12
|
-
* also takes the interface language, because one field's default follows it
|
|
13
|
-
* rather than a constant.
|
|
11
|
+
* fallback for the window before the host applies its first value.
|
|
14
12
|
*
|
|
15
13
|
* @module runtime/settings-namespace
|
|
16
14
|
*/
|
|
@@ -58,12 +56,6 @@ export declare class MarketSettingsNamespace {
|
|
|
58
56
|
private readonly dataRoot;
|
|
59
57
|
private readonly locale;
|
|
60
58
|
private readonly host;
|
|
61
|
-
/**
|
|
62
|
-
* The host interface language, read per call. It is a reader rather than a
|
|
63
|
-
* captured string because a language switch must move the translation
|
|
64
|
-
* default without rebuilding this namespace.
|
|
65
|
-
*/
|
|
66
|
-
private readonly localePreference;
|
|
67
59
|
private readonly refs;
|
|
68
60
|
private readonly watchers;
|
|
69
61
|
private feedbackDisposer;
|
|
@@ -76,20 +68,14 @@ export declare class MarketSettingsNamespace {
|
|
|
76
68
|
private feedbackMountState;
|
|
77
69
|
constructor(ctx: Context, refs: MarketSettingRefs, dataRoot: string, locale: {
|
|
78
70
|
t: HostTranslate;
|
|
79
|
-
}, host: SettingsNamespaceHost
|
|
80
|
-
/**
|
|
81
|
-
* The host interface language, read per call. It is a reader rather than a
|
|
82
|
-
* captured string because a language switch must move the translation
|
|
83
|
-
* default without rebuilding this namespace.
|
|
84
|
-
*/
|
|
85
|
-
localePreference: () => string);
|
|
71
|
+
}, host: SettingsNamespaceHost);
|
|
86
72
|
/** Subscribe the runtime reactions to live settings updates. */
|
|
87
73
|
mount(): void;
|
|
88
74
|
/** Release every watcher and unmount the feedback tool. */
|
|
89
75
|
dispose(): void;
|
|
90
76
|
/**
|
|
91
77
|
* The resolved settings, from the volatile references the host updates in
|
|
92
|
-
* place
|
|
78
|
+
* place. A field whose reference has no value yet takes the contract default.
|
|
93
79
|
*/
|
|
94
80
|
private settings;
|
|
95
81
|
/** The persisted MCP backend choice; the built-in client until the host applies a value. */
|
|
@@ -104,7 +90,7 @@ export declare class MarketSettingsNamespace {
|
|
|
104
90
|
downloadRegion(): Promise<DownloadRegionSetting>;
|
|
105
91
|
/**
|
|
106
92
|
* Read the live translation switch: the stored preference while the user has
|
|
107
|
-
* one, the
|
|
93
|
+
* one, the plugin default (on) otherwise.
|
|
108
94
|
*/
|
|
109
95
|
translationEnabled(): boolean;
|
|
110
96
|
/**
|
|
@@ -5,12 +5,12 @@ import z from '@deepseek-ai/schemastery';
|
|
|
5
5
|
* belong to {@link MARKET_SETTINGS_DEFAULTS}; this schema states them, it does
|
|
6
6
|
* not own them.
|
|
7
7
|
*
|
|
8
|
-
* `translationEnabled` deliberately declares no default.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* the
|
|
13
|
-
*
|
|
8
|
+
* `translationEnabled` deliberately declares no default. A declared default
|
|
9
|
+
* would be materialized into the live config reference, where "the user never
|
|
10
|
+
* set this" and "the user turned it off" would both read the same boolean, and
|
|
11
|
+
* the card could no longer tell the two states apart. With the field absent,
|
|
12
|
+
* the reference answers `undefined` until the user writes it, and
|
|
13
|
+
* {@link resolveMarketSettings} answers the plugin default for that case.
|
|
14
14
|
*/
|
|
15
15
|
export declare const MarketSettingsFields: {
|
|
16
16
|
readonly mcpEnhanced: z<boolean, boolean, "defined">;
|
|
@@ -8,6 +8,7 @@ export declare const MARKET_ROUTES: {
|
|
|
8
8
|
readonly setSurfaceToggle: "/api/agent-plugins/surface-toggles/set";
|
|
9
9
|
readonly mcpStatus: "/api/agent-plugins/mcp-status";
|
|
10
10
|
readonly addMcpServer: "/api/agent-plugins/mcp-servers/add";
|
|
11
|
+
readonly removeMcpServer: "/api/agent-plugins/mcp-servers/remove";
|
|
11
12
|
readonly lspStatus: "/api/agent-plugins/lsp-status";
|
|
12
13
|
readonly lspServers: "/api/agent-plugins/lsp-servers";
|
|
13
14
|
readonly addLspServer: "/api/agent-plugins/lsp-servers/add";
|
|
@@ -21,10 +21,26 @@ export type McpStatusState = 'connected' | 'degraded' | 'failed' | 'needs-creden
|
|
|
21
21
|
export type McpStatusCode = 'unsupported-transport' | 'missing-credential' | 'credential-error' | 'unmount-failed' | 'mount-failed' | 'foreign-mount' | 'duplicate-mount'
|
|
22
22
|
/** Tools are still registered although the surface that declared them is gone. */
|
|
23
23
|
| 'orphaned-tools'
|
|
24
|
+
/** Switched off through an override record, and still mounted because the active session selection demands it. */
|
|
25
|
+
| 'preset-demand'
|
|
24
26
|
/** Switched off through this plugin's own override record. */
|
|
25
27
|
| 'disabled-override'
|
|
26
28
|
/** A stored override changed the configuration the suite declares. */
|
|
27
29
|
| 'modified-override';
|
|
30
|
+
/**
|
|
31
|
+
* One MCP suite/server pair a live session's selection keeps mounted.
|
|
32
|
+
*
|
|
33
|
+
* The mount pass merges session demand into the overrides as `enabled: true`,
|
|
34
|
+
* so a pair listed here is live whatever its stored switch says. The status
|
|
35
|
+
* surface reads the list to name that cause instead of reporting orphaned
|
|
36
|
+
* tools.
|
|
37
|
+
*/
|
|
38
|
+
export interface McpDemandedMount {
|
|
39
|
+
/** Source-qualified suite id, as the status rows carry it. */
|
|
40
|
+
suiteId: string;
|
|
41
|
+
/** The server's key inside that suite's declaration. */
|
|
42
|
+
serverKey: string;
|
|
43
|
+
}
|
|
28
44
|
/** One MCP service row for the status surface. */
|
|
29
45
|
export interface McpStatusEntry {
|
|
30
46
|
id: string;
|
|
@@ -9,11 +9,10 @@
|
|
|
9
9
|
* resolved values off the settings snapshot it is handed. Changing a default is
|
|
10
10
|
* therefore a one-line edit in this module.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* selects the language of displayed translations, including English.
|
|
12
|
+
* Every default is a plain boolean or word, so this module alone answers a
|
|
13
|
+
* section that says nothing. {@link resolveTranslationTarget} still selects the
|
|
14
|
+
* language translations are written in; that choice is independent of whether
|
|
15
|
+
* translations are displayed.
|
|
17
16
|
*
|
|
18
17
|
* @module contracts/settings
|
|
19
18
|
*/
|
|
@@ -33,13 +32,9 @@ export interface MarketSettings {
|
|
|
33
32
|
feedbackEnabled: boolean;
|
|
34
33
|
/** ON = every configured source refreshes in the background. */
|
|
35
34
|
autoUpdateSources: boolean;
|
|
36
|
-
/**
|
|
37
|
-
* ON = suite, skill, command, agent, MCP and LSP text is localized for the
|
|
38
|
-
* host locale. The stored value while the user has one, the interface
|
|
39
|
-
* language's default otherwise.
|
|
40
|
-
*/
|
|
35
|
+
/** ON = suite, skill, command, agent, MCP and LSP text is localized for the host locale. */
|
|
41
36
|
translationEnabled: boolean;
|
|
42
|
-
/** ON = the composer carries the Agent preset manager entry
|
|
37
|
+
/** ON = the composer carries the Agent preset manager entry. */
|
|
43
38
|
agentPresetsEnabled: boolean;
|
|
44
39
|
}
|
|
45
40
|
/**
|
|
@@ -48,14 +43,11 @@ export interface MarketSettings {
|
|
|
48
43
|
* These are user-visible behavior: a flip here is a behavior change that ships
|
|
49
44
|
* with its documentation, not a private implementation detail.
|
|
50
45
|
*
|
|
51
|
-
* `translationEnabled` is the one
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* answers
|
|
55
|
-
*
|
|
56
|
-
* the field at all, an absence that is load-bearing: a schema default would
|
|
57
|
-
* reach the live config reference and erase the difference between "the user
|
|
58
|
-
* never set this" and "the user turned it off".
|
|
46
|
+
* `translationEnabled` is the one field the namespace schema declares no
|
|
47
|
+
* default for. The field stays absent until the user writes it, which is what
|
|
48
|
+
* lets the card tell "never set" from an explicit value, and this constant
|
|
49
|
+
* answers the absent case. A schema default would reach the live config
|
|
50
|
+
* reference and erase that difference.
|
|
59
51
|
*/
|
|
60
52
|
export declare const MARKET_SETTINGS_DEFAULTS: MarketSettings;
|
|
61
53
|
/** Every field name the market's settings section carries. */
|
|
@@ -67,20 +59,6 @@ export type MarketSettingKey = keyof MarketSettings;
|
|
|
67
59
|
* @param fallback - the value an unrecognized input reads as.
|
|
68
60
|
*/
|
|
69
61
|
export declare function narrowDownloadRegion(value: unknown, fallback?: DownloadRegionSetting): DownloadRegionSetting;
|
|
70
|
-
/**
|
|
71
|
-
* Whether one interface language renders the market's Chinese dictionary.
|
|
72
|
-
*
|
|
73
|
-
* This is the host's own rule rather than a second reading of it:
|
|
74
|
-
* `bindHostLocale` (`runtime/host/host-locale.ts`) answers every preference
|
|
75
|
-
* that is not English with the Chinese dictionary, so as far as the interface is
|
|
76
|
-
* concerned everything that is not English is Chinese — an absent preference
|
|
77
|
-
* included, which the host reads as zh.
|
|
78
|
-
*
|
|
79
|
-
* This determines only the default display preference: Chinese starts on and
|
|
80
|
-
* English starts off. It must not gate an explicitly enabled translation.
|
|
81
|
-
* @param localePreference - the host `locale.preference`, or undefined while nothing supplies one.
|
|
82
|
-
*/
|
|
83
|
-
export declare function interfaceLanguageTranslates(localePreference: string | undefined): boolean;
|
|
84
62
|
/**
|
|
85
63
|
* The language the interface renders, as the translation chain names it.
|
|
86
64
|
*
|
|
@@ -111,11 +89,9 @@ export declare function resolveTranslationTarget(localePreference: string | unde
|
|
|
111
89
|
* function is what keeps a second reading of "what does missing mean" from
|
|
112
90
|
* growing somewhere else.
|
|
113
91
|
*
|
|
114
|
-
* A stored
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* a user who never set the field and leave every other user alone.
|
|
92
|
+
* A stored value always wins: the user's own answer is never overridden by a
|
|
93
|
+
* default. Only a section that says nothing about the field takes the value in
|
|
94
|
+
* {@link MARKET_SETTINGS_DEFAULTS}.
|
|
118
95
|
* @param section - the stored (or absent) namespace section, untrusted.
|
|
119
|
-
* @param localePreference - the host `locale.preference` the language-derived default follows.
|
|
120
96
|
*/
|
|
121
|
-
export declare function resolveMarketSettings(section: unknown
|
|
97
|
+
export declare function resolveMarketSettings(section: unknown): MarketSettings;
|
|
@@ -14,6 +14,7 @@ import type { MenuRowSource } from '../contracts/market.js';
|
|
|
14
14
|
export type { McpBackendInfo } from '../contracts/market.js';
|
|
15
15
|
import type { DownloadRegionSetting } from '../contracts/settings.js';
|
|
16
16
|
import type { McpBackend } from '../contracts/mcp.js';
|
|
17
|
+
import type { McpDemandedMount } from '../contracts/mcp-status.js';
|
|
17
18
|
import type { LspMountDiagnostic } from '../contracts/lsp.js';
|
|
18
19
|
import type { DocumentTranslation, TranslationFields, TranslationProvider, TranslationSurfaceKind } from '../contracts/translation.js';
|
|
19
20
|
/**
|
|
@@ -135,6 +136,16 @@ export interface CatalogPorts {
|
|
|
135
136
|
suiteId: string;
|
|
136
137
|
serverKey: string;
|
|
137
138
|
} | undefined;
|
|
139
|
+
/**
|
|
140
|
+
* The suite/server pairs a live session's selection keeps mounted.
|
|
141
|
+
*
|
|
142
|
+
* Session demand is merged into the mount pass as `enabled: true`, so these
|
|
143
|
+
* servers stay live whatever their stored override says. The status surface
|
|
144
|
+
* reads them to report such a server as disabled and demanded rather than as
|
|
145
|
+
* tools left behind by a failed teardown. Optional: a composition with no
|
|
146
|
+
* session layer demands nothing, and the status reads exactly as before.
|
|
147
|
+
*/
|
|
148
|
+
mcpDemandedMounts?: (() => readonly McpDemandedMount[]) | undefined;
|
|
138
149
|
/** Latest LSP mount diagnostics. */
|
|
139
150
|
lspStatusSource: LspMountStatusSource;
|
|
140
151
|
/** The persisted MCP mount backend choice. */
|
|
@@ -43,6 +43,8 @@ export interface MarketQueries {
|
|
|
43
43
|
export interface MarketMutations {
|
|
44
44
|
saveServerConfig(kind: 'mcp' | 'lsp', id: string, config: unknown, policy?: unknown): Promise<void>;
|
|
45
45
|
addMcpServer(name: string, server: unknown, policy?: unknown): Promise<void>;
|
|
46
|
+
/** Remove one user-owned MCP server declaration; a suite-owned server is refused. */
|
|
47
|
+
removeMcpServer(suiteKey: string, serverKey: string): Promise<void>;
|
|
46
48
|
addLspServer(name: string, config: unknown): Promise<void>;
|
|
47
49
|
addSource(input: SourceInput): Promise<SourceRef>;
|
|
48
50
|
updateSource(sourceId: string, patch: SourcePatch): Promise<void>;
|
|
@@ -27,5 +27,17 @@ export declare function addUserMcpServer(agentsRoot: string, name: string, serve
|
|
|
27
27
|
* once, within the same serialized mutation, after any policy has been saved.
|
|
28
28
|
*/
|
|
29
29
|
export declare function prepareUserMcpServer(agentsRoot: string, name: string, server: unknown): Promise<() => Promise<void>>;
|
|
30
|
+
/**
|
|
31
|
+
* Validate a removal without publishing it. Call the returned atomic write once,
|
|
32
|
+
* within the same serialized mutation, after the plan is settled.
|
|
33
|
+
*
|
|
34
|
+
* The file is re-read rather than rebuilt from the parsed suite: rebuilding
|
|
35
|
+
* would drop top-level keys this client does not model and rewrite the other
|
|
36
|
+
* declarations into the parsed shape, while a removal must change exactly one
|
|
37
|
+
* key. A file the loader rejected is refused here for the same reason creation
|
|
38
|
+
* refuses it — the write would otherwise discard a declaration nothing could
|
|
39
|
+
* read.
|
|
40
|
+
*/
|
|
41
|
+
export declare function prepareUserMcpServerRemoval(agentsRoot: string, name: string): Promise<() => Promise<void>>;
|
|
30
42
|
/** The user's hand-written MCP declaration file: `<agentsRoot>/mcp.json`. */
|
|
31
43
|
export declare function userMcpPath(agentsRoot: string): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { McpSuiteOverrides } from './mcp-overrides.js';
|
|
2
|
-
import type { McpStatusCode, McpStatusPayload } from '../../../../market-contracts/src/contracts/mcp-status.js';
|
|
2
|
+
import type { McpDemandedMount, McpStatusCode, McpStatusPayload } from '../../../../market-contracts/src/contracts/mcp-status.js';
|
|
3
3
|
import type { Localization, McpToolSnapshot } from '../../../../market-contracts/src/ports/ports.js';
|
|
4
4
|
import type { Suite } from '../../../../market-contracts/src/model/types.js';
|
|
5
5
|
export type { McpStatusEntry, McpStatusPayload, McpStatusKind, McpStatusState } from '../../../../market-contracts/src/contracts/mcp-status.js';
|
|
@@ -35,6 +35,12 @@ export declare function declaresAuthHeader(config: Record<string, unknown> | und
|
|
|
35
35
|
* resolver bound to it, when the deployment translates at all. Resolved once by
|
|
36
36
|
* the caller: a server and each of its tools is localized from this one value,
|
|
37
37
|
* never from a fresh read of the host preference.
|
|
38
|
+
* @param demanded - the suite/server pairs a live session's selection keeps
|
|
39
|
+
* mounted. The mount pass merged them in as `enabled: true`, so a stored
|
|
40
|
+
* `enabled: false` beside live tools has two possible causes: this demand, or
|
|
41
|
+
* a teardown that failed. Only the caller knows the demand, so it travels
|
|
42
|
+
* here; an omitted list means nothing is demanded and every such row keeps
|
|
43
|
+
* reading as an orphan.
|
|
38
44
|
* @returns the status payload with every translatable field carrying its translation.
|
|
39
45
|
*/
|
|
40
|
-
export declare function buildMcpStatus(suites: Suite[], diagnostics: McpDiagnostic[], observed: readonly McpToolSnapshot[], overrides?: Map<string, McpSuiteOverrides>, localization?: Localization): McpStatusPayload;
|
|
46
|
+
export declare function buildMcpStatus(suites: Suite[], diagnostics: McpDiagnostic[], observed: readonly McpToolSnapshot[], overrides?: Map<string, McpSuiteOverrides>, localization?: Localization, demanded?: readonly McpDemandedMount[]): McpStatusPayload;
|
|
@@ -24,6 +24,18 @@ export declare class McpService {
|
|
|
24
24
|
* the previous override; the two files are not a cross-file transaction.
|
|
25
25
|
*/
|
|
26
26
|
addServer(name: string, server: unknown, policy?: unknown): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Remove one user-owned server from the declaration file and drop everything
|
|
29
|
+
* the plugin stored about it: the override record (edited config, enablement,
|
|
30
|
+
* policy) and the OAuth grant. Only the user's own declaration may be removed
|
|
31
|
+
* — a suite's `mcp.json` is source-owned, so a refresh would restore a server
|
|
32
|
+
* deleted from it, and disabling is the supported operation there.
|
|
33
|
+
*
|
|
34
|
+
* The declaration is written first. An interruption can then leave an inert
|
|
35
|
+
* override or grant record behind, never a live server whose stored policy was
|
|
36
|
+
* silently dropped.
|
|
37
|
+
*/
|
|
38
|
+
removeServer(suiteKey: string, serverKey: string): Promise<void>;
|
|
27
39
|
/** Read one MCP service without credential literals, or an unsaved creation template. */
|
|
28
40
|
serverConfig(id: string, create?: boolean): Promise<ServerConfigPayload>;
|
|
29
41
|
/**
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
* @module runtime/mcp-client/connection
|
|
22
22
|
*/
|
|
23
|
+
import type { BrowserOpener } from './oauth.js';
|
|
23
24
|
import type { ToolHost } from './tools.js';
|
|
24
25
|
import type { Config } from '../../../application/mcp/mcp-bridge-config.js';
|
|
25
26
|
/** Automatic reconnect policy for one MCP server connection. */
|
|
@@ -69,6 +70,8 @@ export interface ConnectionHandle {
|
|
|
69
70
|
* @param policy - Resolved reconnect policy from {@link resolveReconnectPolicy}.
|
|
70
71
|
* @param credentials - The composition's credential store, when mounted; OAuth
|
|
71
72
|
* state persists only when it is.
|
|
73
|
+
* @param opener - Browser opener for the OAuth human leg; the platform browser
|
|
74
|
+
* by default.
|
|
72
75
|
* @returns Handle with a `ready` promise for startup-await and a `dispose` for teardown.
|
|
73
76
|
*/
|
|
74
|
-
export declare function startConnection(host: ToolHost, config: Config, policy: ResolvedReconnectPolicy, credentials?: unknown): ConnectionHandle;
|
|
77
|
+
export declare function startConnection(host: ToolHost, config: Config, policy: ResolvedReconnectPolicy, credentials?: unknown, opener?: BrowserOpener): ConnectionHandle;
|
|
@@ -67,6 +67,14 @@ export declare class LoopbackOAuthClientProvider implements OAuthClientProvider
|
|
|
67
67
|
private expectedState;
|
|
68
68
|
/** Resolves `true` when the leg saved tokens, `false` when it failed; undefined while no leg is open. */
|
|
69
69
|
private browserLegSettled;
|
|
70
|
+
/**
|
|
71
|
+
* Outcome of the most recently finished browser leg, `true` when the code
|
|
72
|
+
* exchange saved tokens. The pending-leg promise disappears when the leg
|
|
73
|
+
* settles, and a leg that finishes inside the `connect` that raised the
|
|
74
|
+
* `401` leaves nothing pending for a supervisor to observe — this field is
|
|
75
|
+
* how the supervisor still learns that the authorization succeeded.
|
|
76
|
+
*/
|
|
77
|
+
private lastLegOutcome;
|
|
70
78
|
private callbackServer;
|
|
71
79
|
private callbackTimer;
|
|
72
80
|
private boundPort;
|
|
@@ -98,6 +106,12 @@ export declare class LoopbackOAuthClientProvider implements OAuthClientProvider
|
|
|
98
106
|
* settles.
|
|
99
107
|
*/
|
|
100
108
|
get browserLeg(): Promise<boolean> | undefined;
|
|
109
|
+
/**
|
|
110
|
+
* The most recent finished leg's outcome; `undefined` until one finishes.
|
|
111
|
+
* The supervisor reads it after a failed attempt: a leg that settled inside
|
|
112
|
+
* the attempt itself leaves no pending {@link browserLeg} behind.
|
|
113
|
+
*/
|
|
114
|
+
get lastBrowserLegOutcome(): boolean | undefined;
|
|
101
115
|
/**
|
|
102
116
|
* Register the transport generation whose `finishAuth` completes the flow.
|
|
103
117
|
* The transport calls `redirectToAuthorization` during `connect`; binding
|