dsh-agent-plugins-market 0.6.2 → 0.7.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.
Files changed (136) hide show
  1. package/README.md +117 -111
  2. package/README.zh.md +112 -98
  3. package/client/client.js +547 -540
  4. package/client/client.js.map +1 -1
  5. package/lib/application/catalog-context.js +181 -0
  6. package/lib/application/catalog.js +217 -1110
  7. package/lib/application/details.js +6 -6
  8. package/lib/application/install-store.js +95 -0
  9. package/lib/application/lsp-service.js +97 -0
  10. package/lib/application/mcp-service.js +246 -0
  11. package/lib/application/panel-resources.js +29 -12
  12. package/lib/application/ports.js +21 -0
  13. package/lib/application/project-agent-roles.js +1 -1
  14. package/lib/application/snapshot-cache.js +160 -0
  15. package/lib/application/source-store.js +521 -0
  16. package/lib/catalog/archive.js +34 -29
  17. package/lib/catalog/component-files.js +14 -6
  18. package/lib/catalog/fs-probes.js +43 -0
  19. package/lib/catalog/manifests.js +53 -92
  20. package/lib/catalog/native-project.js +2 -2
  21. package/lib/catalog/paths.js +66 -55
  22. package/lib/catalog/project-config.js +2 -1
  23. package/lib/catalog/project-hooks.js +9 -1
  24. package/lib/catalog/scan-resolvers.js +29 -45
  25. package/lib/catalog/skills-parse.js +14 -10
  26. package/lib/catalog/source-catalog.js +3 -2
  27. package/lib/catalog/suite-components.js +5 -5
  28. package/lib/catalog/suite-scanner.js +3 -1
  29. package/lib/catalog/surfaces.js +5 -26
  30. package/lib/catalog/validate.js +7 -11
  31. package/lib/contracts/market.js +1 -0
  32. package/lib/index.js +91 -208
  33. package/lib/model/types.js +26 -3
  34. package/lib/routes.js +45 -15
  35. package/lib/runtime/agent-role-names.js +70 -0
  36. package/lib/runtime/agent-role-router.js +116 -102
  37. package/lib/runtime/commands-mounts.js +9 -6
  38. package/lib/runtime/deadline.js +28 -0
  39. package/lib/runtime/feedback-tool.js +94 -43
  40. package/lib/runtime/hooks-mounts.js +9 -11
  41. package/lib/runtime/host-locale.js +10 -21
  42. package/lib/runtime/lsp-direct-config.js +19 -19
  43. package/lib/runtime/lsp-mounts.js +159 -68
  44. package/lib/runtime/lsp-server-state.js +4 -4
  45. package/lib/runtime/lsp-status.js +1 -1
  46. package/lib/runtime/mcp-auth-record.js +42 -0
  47. package/lib/runtime/mcp-backend.js +6 -5
  48. package/lib/runtime/mcp-client/config.js +0 -1
  49. package/lib/runtime/mcp-client/connection.js +32 -37
  50. package/lib/runtime/mcp-client/host-contract.js +10 -0
  51. package/lib/runtime/mcp-client/oauth.js +5 -7
  52. package/lib/runtime/mcp-client/plugin-identity.js +28 -0
  53. package/lib/runtime/mcp-client/projection.js +202 -0
  54. package/lib/runtime/mcp-client/tools.js +4 -189
  55. package/lib/runtime/mcp-config.js +29 -10
  56. package/lib/runtime/mcp-direct-config.js +33 -16
  57. package/lib/runtime/mcp-mounts.js +35 -75
  58. package/lib/runtime/mcp-overrides.js +5 -5
  59. package/lib/runtime/mcp-redaction.js +3 -2
  60. package/lib/runtime/mcp-status.js +7 -8
  61. package/lib/runtime/mount-lifecycle.js +73 -0
  62. package/lib/runtime/profile-seam.js +321 -0
  63. package/lib/runtime/project-runtime.js +11 -8
  64. package/lib/runtime/reconcile-scheduler.js +99 -0
  65. package/lib/runtime/reconciler.js +4 -0
  66. package/lib/runtime/server-config.js +6 -4
  67. package/lib/runtime/settings-namespace.js +136 -0
  68. package/lib/runtime/skills-provider.js +5 -3
  69. package/lib/runtime/source-auto-update.js +70 -0
  70. package/lib/{model/state.js → runtime/state-store.js} +9 -11
  71. package/lib/runtime/storage-migration.js +35 -18
  72. package/lib/runtime/subagent-catalog.js +5 -4
  73. package/lib/runtime/user-commands.js +2 -2
  74. package/lib/runtime/user-panels.js +31 -21
  75. package/lib/runtime/user-store.js +12 -13
  76. package/lib/types/application/catalog-context.d.ts +118 -0
  77. package/lib/types/application/catalog.d.ts +99 -304
  78. package/lib/types/application/install-store.d.ts +21 -0
  79. package/lib/types/application/lsp-service.d.ts +36 -0
  80. package/lib/types/application/mcp-service.d.ts +71 -0
  81. package/lib/types/application/panel-resources.d.ts +8 -0
  82. package/lib/types/application/ports.d.ts +72 -0
  83. package/lib/types/application/queries.d.ts +13 -26
  84. package/lib/types/application/snapshot-cache.d.ts +51 -0
  85. package/lib/types/application/source-store.d.ts +100 -0
  86. package/lib/types/catalog/archive.d.ts +3 -11
  87. package/lib/types/catalog/component-files.d.ts +4 -0
  88. package/lib/types/catalog/fs-probes.d.ts +10 -0
  89. package/lib/types/catalog/manifests.d.ts +17 -16
  90. package/lib/types/catalog/native-project.d.ts +2 -2
  91. package/lib/types/catalog/paths.d.ts +47 -15
  92. package/lib/types/catalog/scan-pipeline.d.ts +3 -3
  93. package/lib/types/catalog/scan-resolvers.d.ts +3 -9
  94. package/lib/types/catalog/skills-parse.d.ts +0 -7
  95. package/lib/types/catalog/source-catalog.d.ts +3 -3
  96. package/lib/types/catalog/suite-scanner.d.ts +8 -6
  97. package/lib/types/catalog/validate.d.ts +0 -2
  98. package/lib/types/contracts/lsp-status.d.ts +35 -0
  99. package/lib/types/contracts/market.d.ts +3 -2
  100. package/lib/types/contracts/mcp-status.d.ts +4 -0
  101. package/lib/types/model/types.d.ts +29 -5
  102. package/lib/types/runtime/agent-role-names.d.ts +6 -0
  103. package/lib/types/runtime/agent-role-router.d.ts +54 -51
  104. package/lib/types/runtime/deadline.d.ts +18 -0
  105. package/lib/types/runtime/feedback-tool.d.ts +33 -11
  106. package/lib/types/runtime/hooks-mounts.d.ts +1 -1
  107. package/lib/types/runtime/host-locale.d.ts +1 -1
  108. package/lib/types/runtime/lsp-direct-config.d.ts +3 -3
  109. package/lib/types/runtime/lsp-mounts.d.ts +62 -19
  110. package/lib/types/runtime/lsp-status.d.ts +2 -1
  111. package/lib/types/runtime/mcp-auth-record.d.ts +29 -0
  112. package/lib/types/runtime/mcp-backend.d.ts +7 -4
  113. package/lib/types/runtime/mcp-client/config.d.ts +0 -2
  114. package/lib/types/runtime/mcp-client/host-contract.d.ts +78 -0
  115. package/lib/types/runtime/mcp-client/oauth.d.ts +1 -1
  116. package/lib/types/runtime/mcp-client/plugin-identity.d.ts +16 -0
  117. package/lib/types/runtime/mcp-client/projection.d.ts +45 -0
  118. package/lib/types/runtime/mcp-client/tools.d.ts +5 -72
  119. package/lib/types/runtime/mcp-config.d.ts +25 -4
  120. package/lib/types/runtime/mcp-direct-config.d.ts +18 -4
  121. package/lib/types/runtime/mcp-mounts.d.ts +10 -15
  122. package/lib/types/runtime/mcp-overrides.d.ts +1 -1
  123. package/lib/types/runtime/mcp-status.d.ts +1 -1
  124. package/lib/types/runtime/mount-lifecycle.d.ts +53 -0
  125. package/lib/types/runtime/profile-seam.d.ts +64 -0
  126. package/lib/types/runtime/reconcile-scheduler.d.ts +45 -0
  127. package/lib/types/runtime/reconciler.d.ts +2 -0
  128. package/lib/types/runtime/settings-namespace.d.ts +76 -0
  129. package/lib/types/runtime/source-auto-update.d.ts +33 -0
  130. package/lib/types/{model/state.d.ts → runtime/state-store.d.ts} +2 -2
  131. package/lib/types/runtime/storage-migration.d.ts +5 -3
  132. package/lib/types/runtime/subagent-catalog.d.ts +2 -0
  133. package/lib/types/runtime/user-panels.d.ts +8 -11
  134. package/lib/types/runtime/user-store.d.ts +2 -4
  135. package/package.json +48 -21
  136. package/schemas/README.md +1 -1
@@ -10,8 +10,6 @@
10
10
  */
11
11
  import type { ReconnectConfig } from './connection.js';
12
12
  import type { McpServerPolicy } from '../../model/types.js';
13
- export type { ReconnectConfig, ResolvedReconnectPolicy } from './connection.js';
14
- export { RECONNECT_DEFAULTS } from './connection.js';
15
13
  /** Default timeout for individual MCP tool calls (ms). */
16
14
  export declare const DEFAULT_TOOL_CALL_TIMEOUT_MS = 60000;
17
15
  /** Valid `serverName`, kept below the public tool-name budget. */
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Structural mirrors of the harness tool-registration surface the bridge
3
+ * registers against: the runtime accepts the same object shape the upstream
4
+ * `@deepseek-ai/dsh-tools` bridge registers, so the model-facing behavior is
5
+ * identical. Mirrored rather than imported so the market never loads a second
6
+ * copy of the host runtime.
7
+ *
8
+ * @module runtime/mcp-client/host-contract
9
+ */
10
+ import type { ContentBlock } from '@deepseek-ai/dsh-llm';
11
+ import type { JsonSchemaNode } from './json-schema-subset.js';
12
+ /** Lossless JSON value vocabulary the bridge exchanges with the registry. */
13
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
14
+ [key: string]: JsonValue;
15
+ };
16
+ /** Canonical MCP result exposed to Code Mode without discarding protocol blocks. */
17
+ export type McpResult<Structured extends JsonValue = JsonValue> = {
18
+ content: JsonValue[];
19
+ structuredContent?: Structured;
20
+ };
21
+ /** The agent on whose behalf a tool call runs (structural mirror). */
22
+ interface ToolExecutionAgent {
23
+ session?: {
24
+ requestHeader?: () => {
25
+ config?: {
26
+ provider?: string;
27
+ model?: string;
28
+ };
29
+ } | undefined;
30
+ };
31
+ options?: {
32
+ provider?: string;
33
+ model?: string;
34
+ };
35
+ }
36
+ /** Execution identity handed to a tool body (structural mirror). */
37
+ export interface ToolExecution {
38
+ /** Caller-owned cancellation for this invocation. */
39
+ readonly signal: AbortSignal;
40
+ /** The calling agent, when the call came from one. */
41
+ readonly agent?: ToolExecutionAgent;
42
+ }
43
+ /** A settled tool outcome (structural mirror). */
44
+ export interface ToolExecutionResult {
45
+ readonly value: JsonValue;
46
+ readonly content: ContentBlock[];
47
+ readonly isError: boolean;
48
+ }
49
+ /** One registered tool (structural mirror of the host registration shape). */
50
+ export interface ToolDefinition {
51
+ readonly name: string;
52
+ readonly description: string;
53
+ readonly parameters: Record<string, unknown>;
54
+ readonly output: {
55
+ readonly schema: JsonSchemaNode;
56
+ render(args: unknown, value: JsonValue): ContentBlock[];
57
+ };
58
+ execute(args: unknown, exec: ToolExecution): Promise<unknown>;
59
+ finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined;
60
+ }
61
+ /**
62
+ * The host surface the bridge needs: a tool registry, a logger, and the two
63
+ * optional services image projection consults. Structural — the cordis
64
+ * context is adapted onto this shape by the bridge shell.
65
+ */
66
+ export interface ToolHost {
67
+ readonly logger: {
68
+ error(message: string): void;
69
+ warn(message: string): void;
70
+ info(message: string): void;
71
+ };
72
+ readonly tools: {
73
+ register(definition: ToolDefinition): () => void;
74
+ };
75
+ /** Optional service lookup; image projection degrades when absent. */
76
+ getService?(name: 'attachments' | 'llm'): unknown;
77
+ }
78
+ export {};
@@ -22,7 +22,7 @@
22
22
  * then last exactly as long as the process.
23
23
  *
24
24
  * Ported from the harness `dsh-mcp-client` OAuth implementation (upstream
25
- * commits archived at `docs/upstream-proposal/patches/`); the record format is
25
+ * commits archived at `docs/developer/upstream-proposal/patches/`); the record format is
26
26
  * byte-compatible, so grants written by either implementation are interchangeable.
27
27
  *
28
28
  * @module runtime/mcp-client/oauth
@@ -0,0 +1,16 @@
1
+ /**
2
+ * This plugin's own identity as advertised to the servers it talks to: the MCP
3
+ * client implementation name and version, and the RFC 7591 `software_id` /
4
+ * `software_version` carried in OAuth dynamic-registration metadata.
5
+ *
6
+ * The version is read from the installed manifest through Node's package
7
+ * self-reference, so a release bumps exactly one number — `package.json`. A
8
+ * copy that can no longer resolve its own manifest (a bundle that inlined this
9
+ * module) reports {@link UNKNOWN_VERSION} rather than failing the connection.
10
+ *
11
+ * @module runtime/mcp-client/plugin-identity
12
+ */
13
+ /** Name npm and the harness know this plugin by. */
14
+ export declare const PLUGIN_NAME = "dsh-agent-plugins-market";
15
+ /** This plugin's release version. */
16
+ export declare const PLUGIN_VERSION: string;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * MCP result projection: turn the content blocks an MCP server returns into
3
+ * the host content vocabulary the model sees.
4
+ *
5
+ * Text-like blocks collapse into newline-joined text runs; declared images are
6
+ * decoded, admitted against the live model route, and saved to the durable
7
+ * attachment store. Every refusal — an unsupported media type, a model that
8
+ * does not declare image input, a storage fault — projects the block as text
9
+ * explaining why, and the canonical raw value stays available to programmatic
10
+ * callers in the tool result.
11
+ *
12
+ * Everything here reads an untrusted network payload: the MCP server is an
13
+ * external process and the SDK's declared-required fields may be absent at
14
+ * runtime, so each block is narrowed field by field.
15
+ *
16
+ * @module runtime/mcp-client/projection
17
+ */
18
+ import type { ContentBlock } from '@deepseek-ai/dsh-llm';
19
+ import type { JsonValue, McpResult, ToolExecution, ToolHost } from './host-contract.js';
20
+ /** Async rich projection staged for one exact execution. */
21
+ export interface PreparedProjection {
22
+ /** Canonical MCP value returned by execute before registry materialization. */
23
+ value: McpResult;
24
+ /** Synchronous output.render projection expected before finalization. */
25
+ fallback: ContentBlock[];
26
+ /** Image-enriched or explicit-refusal projection prepared during execute. */
27
+ content: ContentBlock[];
28
+ }
29
+ /** Whether an untrusted MCP content array contains a declared image block. */
30
+ export declare function containsImage(content: JsonValue[]): boolean;
31
+ /**
32
+ * Decode, preflight, and durably save one MCP result's ordered image batch.
33
+ * Any refusal projects every image as text while retaining the canonical raw
34
+ * value for programmatic callers.
35
+ */
36
+ export declare function prepareImageProjection(host: ToolHost, exec: ToolExecution, content: JsonValue[], toolName: string): Promise<ContentBlock[]>;
37
+ /**
38
+ * Extract text from an MCP content array into a single string.
39
+ * - text blocks: join with '\n'
40
+ * - image/audio/resource blocks: replaced with a placeholder
41
+ *
42
+ * Defensive: fields that the MCP spec declares required (mimeType, text) are
43
+ * guarded with fallbacks because this is a network trust boundary.
44
+ */
45
+ export declare function extractText(mcpContent: JsonValue[], toolName: string): string;
@@ -9,81 +9,15 @@
9
9
  * to the DeepSeek function-name constraints. The raw name is only ever sent on
10
10
  * the wire (`tools/call`); the public name is never parsed to recover it.
11
11
  *
12
- * The host contract below (`ToolDefinition`, `ToolExecution`,
13
- * `ToolExecutionResult`) is a structural mirror of `@deepseek-ai/dsh-tools`
14
- * rc.2's registration surface — the runtime accepts the same object shape the
15
- * upstream bridge registers, so the model-facing behavior is identical.
12
+ * The registration surface this module targets is mirrored by
13
+ * `./host-contract.js`; MCP results are mapped into host content blocks by
14
+ * `./projection.js`.
16
15
  *
17
16
  * @module runtime/mcp-client/tools
18
17
  */
19
18
  import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
20
- import type { ContentBlock } from '@deepseek-ai/dsh-llm';
21
- import type { JsonSchemaNode } from './json-schema-subset.js';
22
- /** Lossless JSON value vocabulary the bridge exchanges with the registry. */
23
- export type JsonValue = string | number | boolean | null | JsonValue[] | {
24
- [key: string]: JsonValue;
25
- };
26
- /** Canonical MCP result exposed to Code Mode without discarding protocol blocks. */
27
- export type McpResult<Structured extends JsonValue = JsonValue> = {
28
- content: JsonValue[];
29
- structuredContent?: Structured;
30
- };
31
- /** The agent on whose behalf a tool call runs (structural mirror). */
32
- interface ToolExecutionAgent {
33
- session?: {
34
- requestHeader?: () => {
35
- config?: {
36
- provider?: string;
37
- model?: string;
38
- };
39
- } | undefined;
40
- };
41
- options?: {
42
- provider?: string;
43
- model?: string;
44
- };
45
- } /** Execution identity handed to a tool body (structural mirror). */
46
- export interface ToolExecution {
47
- /** Caller-owned cancellation for this invocation. */
48
- readonly signal: AbortSignal;
49
- /** The calling agent, when the call came from one. */
50
- readonly agent?: ToolExecutionAgent;
51
- }
52
- /** A settled tool outcome (structural mirror). */
53
- export interface ToolExecutionResult {
54
- readonly value: JsonValue;
55
- readonly content: ContentBlock[];
56
- readonly isError: boolean;
57
- }
58
- /** One registered tool (structural mirror of the host registration shape). */
59
- export interface ToolDefinition {
60
- readonly name: string;
61
- readonly description: string;
62
- readonly parameters: Record<string, unknown>;
63
- readonly output: {
64
- readonly schema: JsonSchemaNode;
65
- render(args: unknown, value: JsonValue): ContentBlock[];
66
- };
67
- execute(args: unknown, exec: ToolExecution): Promise<unknown>;
68
- finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined;
69
- }
70
- /**
71
- * The host surface the bridge needs: a tool registry, a logger, and the two
72
- * optional services image projection consults. Structural — the cordis
73
- * context is adapted onto this shape by the bridge shell.
74
- */
75
- export interface ToolHost {
76
- readonly logger: {
77
- error(message: string): void;
78
- warn(message: string): void;
79
- info(message: string): void;
80
- };
81
- readonly tools: {
82
- register(definition: ToolDefinition): () => void;
83
- };
84
- /** Optional service lookup; image projection degrades when absent. */
85
- getService?(name: 'attachments' | 'llm'): unknown;
86
- }
19
+ import type { ToolHost } from './host-contract.js';
20
+ export type { JsonValue, McpResult, ToolDefinition, ToolExecution, ToolExecutionResult, ToolHost } from './host-contract.js';
87
21
  /** Resolved options relevant to tool bridging. */
88
22
  export interface ToolBridgeOptions {
89
23
  enabledTools?: string[];
@@ -136,4 +70,3 @@ export declare function publicToolName(serverName: string, rawName: string): str
136
70
  * disposers — the exact set of live registrations owned by this server.
137
71
  */
138
72
  export declare function syncTools(client: Client, host: ToolHost, opts: ToolBridgeOptions, previous: ToolDisposers): Promise<ToolDisposers>;
139
- export {};
@@ -1,11 +1,22 @@
1
1
  import type { Config } from './mcp-client/config.js';
2
- import { type McpSuiteOverrides } from './mcp-overrides.js';
3
- import type { McpServer, Suite } from '../model/types.js';
2
+ import { type McpServerOverride, type McpSuiteOverrides } from './mcp-overrides.js';
3
+ import type { McpServer, McpServerSse, McpServerStdio, McpServerStreamableHttp, Suite } from '../model/types.js';
4
4
  export interface McpMountRequest {
5
5
  suiteId: string;
6
6
  serverKey: string;
7
7
  config: Config;
8
8
  }
9
+ /** One `mcp.json` server as it will actually be used, with the user's override applied. */
10
+ export interface EffectiveMcpServer {
11
+ serverKey: string;
12
+ server: McpServerStdio | McpServerStreamableHttp | McpServerSse;
13
+ /** The override that produced this view; consumers render its state from it. */
14
+ override: McpServerOverride | undefined;
15
+ /** Whether the server is used at all — an override can disable a declaration without removing it. */
16
+ enabled: boolean;
17
+ /** External credential references the effective definition needs. */
18
+ credentialRefs: string[];
19
+ }
9
20
  export type McpMountFailureCode = 'unsupported-transport' | 'missing-credential' | 'credential-error' | 'unmount-failed' | 'mount-failed'
10
21
  /** The derived serverName's namespace is already mounted by another MCP client — informational, not a failure. */
11
22
  | 'foreign-mount'
@@ -24,6 +35,14 @@ export interface McpCredentialResolver {
24
35
  source?: string;
25
36
  } | undefined>;
26
37
  }
38
+ /**
39
+ * The effective view of one suite's `mcp.json` servers: the user's per-server
40
+ * overrides applied, every row flagged with whether it is used at all, and each
41
+ * row's credential references read off the effective shape. Mounting, status
42
+ * and detail rendering read this one projection, so a server cannot be listed
43
+ * as enabled while mounting something else.
44
+ */
45
+ export declare function effectiveMcpServers(suite: Suite, overrides?: McpSuiteOverrides): EffectiveMcpServer[];
27
46
  /**
28
47
  * Build one mount request per supported mcp.json server, resolving every
29
48
  * `${NAME}` through the credential resolver before a child process or HTTP
@@ -33,12 +52,14 @@ export interface McpCredentialResolver {
33
52
  * @param overrides user-owned per-server overrides (url/headers/env/args
34
53
  * replacement plus enable/disable); applied before mount. Disabled servers
35
54
  * are omitted entirely.
36
- * @returns mount requests plus per-server failures. serverName collisions are
37
- * reported by the mount registry, not here.
55
+ * @returns mount requests, per-server failures, and the credential references
56
+ * the effective servers need. serverName collisions are reported by the
57
+ * mount registry, not here.
38
58
  */
39
59
  export declare function toMcpMounts(suite: Suite, pluginDataRoot: string, overrides?: McpSuiteOverrides, resolver?: McpCredentialResolver): Promise<{
40
60
  mounts: McpMountRequest[];
41
61
  failures: McpMountFailure[];
62
+ credentialRefs: string[];
42
63
  }>;
43
64
  /** Find external credential references used by one MCP server definition. */
44
65
  export declare function credentialRefsInServer(server: McpServer): string[];
@@ -1,7 +1,21 @@
1
- import type { Suite } from '../model/types.js';
1
+ import { type McpSuiteConfig, type Suite } from '../model/types.js';
2
2
  export declare const USER_MCP_SOURCE = "@user-mcp";
3
3
  export declare const USER_MCP_SUITE = "user-mcp";
4
- /** User-created services reuse the owned bridge lifecycle without changing host configuration. */
5
- export declare function loadUserMcpSuite(dataRoot: string): Promise<Suite>;
4
+ /**
5
+ * User-created services reuse the owned bridge lifecycle without changing host configuration.
6
+ *
7
+ * The declarations live in the shared Agent layout root
8
+ * (`~/.agents/mcp.json`), so a service the user adds by hand is ordinary
9
+ * `mcpServers` JSON in the same place other Agent tools look.
10
+ *
11
+ * The suite always carries an `mcp` document — an absent or malformed
12
+ * `mcp.json` leaves an empty one — so callers read `.mcp.servers` directly
13
+ * instead of re-checking the optional field.
14
+ */
15
+ export declare function loadUserMcpSuite(agentsRoot: string): Promise<Suite & {
16
+ mcp: McpSuiteConfig;
17
+ }>;
6
18
  /** Add one service atomically; reject collisions and malformed config before writing. */
7
- export declare function addUserMcpServer(dataRoot: string, name: string, server: unknown): Promise<void>;
19
+ export declare function addUserMcpServer(agentsRoot: string, name: string, server: unknown): Promise<void>;
20
+ /** The user's hand-written MCP declaration file: `<agentsRoot>/mcp.json`. */
21
+ export declare function userMcpPath(agentsRoot: string): string;
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import type { Context } from '@deepseek-ai/cordis';
12
12
  import type { McpBackend } from './mcp-backend.js';
13
- import { type McpSuiteOverrides } from './mcp-overrides.js';
13
+ import type { McpSuiteOverrides } from './mcp-overrides.js';
14
14
  import { type McpMountFailureCode } from './mcp-config.js';
15
15
  import type { Suite } from '../model/types.js';
16
16
  export interface McpMountDiagnostic {
@@ -23,7 +23,6 @@ export interface McpMountDiagnostic {
23
23
  export declare class McpMountRegistry {
24
24
  private readonly ctx;
25
25
  private readonly pluginDataRoot;
26
- private readonly namespace?;
27
26
  private readonly live;
28
27
  private readonly names;
29
28
  private readonly credentialRefs;
@@ -33,11 +32,9 @@ export declare class McpMountRegistry {
33
32
  /** The active MCP mount backend ('builtin' bridge or host client compat mode). */
34
33
  private backendProvider;
35
34
  /** Serialize mount and unmount passes so a disable cannot race an in-flight spawn. */
36
- private reconcileQueue;
37
- /** Pending retry timers keyed by mount key, so a teardown can cancel them. */
35
+ private readonly passes;
36
+ /** Delayed re-attempts for mounts that are not live yet. */
38
37
  private readonly retries;
39
- /** Attempt count per mount key; reset whenever a retry succeeds. */
40
- private readonly attempts;
41
38
  /**
42
39
  * Mount keys flagged for an explicit rebuild (re-authorize): the next
43
40
  * reconcile must tear down and remount them even when the resolved config
@@ -46,7 +43,7 @@ export declare class McpMountRegistry {
46
43
  private readonly forcedRemounts;
47
44
  /** Snapshot of the last reconciled suite set, replayed by retry passes. */
48
45
  private lastEnabled;
49
- constructor(ctx: Context, pluginDataRoot: string, namespace?: string | undefined);
46
+ constructor(ctx: Context, pluginDataRoot: string);
50
47
  /** Install the per-suite overrides provider (suiteId -> overrides). */
51
48
  setOverridesProvider(provider: () => Promise<Map<string, McpSuiteOverrides>>): void;
52
49
  /**
@@ -66,6 +63,12 @@ export declare class McpMountRegistry {
66
63
  * its in-memory tokens) untouched.
67
64
  */
68
65
  forceRemount(suiteId: string, serverKey: string): void;
66
+ /**
67
+ * Flag every live mount for a rebuild on the next reconcile: the manual
68
+ * retry path. A bridge whose server died underneath it still matches its own
69
+ * config fingerprint, so nothing else would ever re-verify that mount.
70
+ */
71
+ forceRemountAll(): void;
69
72
  /** The mount key owning one derived serverName; undefined when not mounted here. */
70
73
  serverOwner(serverName: string): {
71
74
  suiteId: string;
@@ -75,14 +78,6 @@ export declare class McpMountRegistry {
75
78
  reconcile(enabledSuites: Suite[]): Promise<McpMountDiagnostic[]>;
76
79
  /** Mount/unmount MCP servers to match the enabled suites exactly. */
77
80
  private reconcileNow;
78
- /**
79
- * Schedule a delayed re-attempt for a mount that still is not live.
80
- *
81
- * Mounts are retried because a remote endpoint or a credential may become
82
- * available slightly after the suite is enabled. The schedule is bounded, and
83
- * a server that keeps failing simply stops retrying until the next reconcile.
84
- */
85
- private scheduleRetry;
86
81
  /** Dispose every live mount after queued reconciliation passes settle. */
87
82
  disposeAll(): Promise<void>;
88
83
  /** Mount one precomputed request (source config merged with overrides). */
@@ -29,7 +29,7 @@ export declare function overridesDir(dataRoot: string): string;
29
29
  export declare function suiteOverridePath(dataRoot: string, suiteId: string): string;
30
30
  /** Load one suite's persisted overrides; unreadable files yield none. */
31
31
  export declare function loadSuiteOverrides(dataRoot: string, suiteId: string): Promise<McpSuiteOverrides>;
32
- /** Persist one suite's overrides atomically-ish (single writer, small file). */
32
+ /** Persist one suite's overrides through the harness atomic write. */
33
33
  export declare function saveSuiteOverrides(dataRoot: string, suiteId: string, overrides: McpSuiteOverrides): Promise<void>;
34
34
  /** Keep only recognized fields with correct shapes; drop everything else. */
35
35
  export declare function sanitizeOverrides(raw: unknown): McpSuiteOverrides;
@@ -1,4 +1,4 @@
1
- import { type McpSuiteOverrides } from './mcp-overrides.js';
1
+ import type { McpSuiteOverrides } from './mcp-overrides.js';
2
2
  import type { McpStatusPayload } from '../contracts/mcp-status.js';
3
3
  import { inspectToolRegistry, type McpToolSnapshot } from './tool-registry-observer.js';
4
4
  import type { Suite } from '../model/types.js';
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Shared mount lifecycle: the pass queue and the bounded retry schedule that
3
+ * every surface registry runs on top of its own mount/unmount semantics. Which
4
+ * failures are worth retrying stays the surface's own decision; only the timing
5
+ * and the bookkeeping live here.
6
+ */
7
+ /** Structural `ctx.plugin` handle for one dynamically mounted plugin. */
8
+ export interface MountPluginHandle {
9
+ await(): Promise<unknown>;
10
+ dispose(): void | Promise<void>;
11
+ }
12
+ /** Structural `ctx.plugin` surface for mounting one plugin instance. */
13
+ export interface PluginMountContext {
14
+ plugin(plugin: unknown, config: unknown): MountPluginHandle;
15
+ }
16
+ /** One failed mount offered to the retry schedule. */
17
+ export interface RetryTarget {
18
+ /** Mount key; also the retry identity, so a key holds at most one pending timer. */
19
+ key: string;
20
+ /** How the mount is named in log lines, e.g. `suite/server`. */
21
+ label: string;
22
+ /** Why the mount failed; omitted when the failure must not be retried. */
23
+ reason?: string;
24
+ }
25
+ /** Serializes reconcile passes: each pass starts after the previous one settles. */
26
+ export declare class SerialPassQueue {
27
+ private tail;
28
+ /** Queue one pass behind the in-flight one, so a disable cannot race an in-flight spawn. */
29
+ run<T>(pass: () => Promise<T>): Promise<T>;
30
+ }
31
+ export interface RetrySchedulerOptions {
32
+ /** Re-run the surface's reconcile against its last known input. */
33
+ replay(): Promise<unknown>;
34
+ /** Write one lifecycle log line through the surface's own logger. */
35
+ log(message: string): void;
36
+ }
37
+ /** Delayed re-attempts for a mount that is not live yet, keyed by mount key. */
38
+ export declare class RetryScheduler {
39
+ private readonly options;
40
+ /** Pending retry timers keyed by mount key, so a teardown can cancel them. */
41
+ private readonly timers;
42
+ /** Attempt count per mount key, dropped when its target stops being retryable. */
43
+ private readonly attempts;
44
+ constructor(options: RetrySchedulerOptions);
45
+ /**
46
+ * Arm the retry for one failed mount, replacing any timer already pending for
47
+ * its key. A target carrying no reason is a deterministic skip rather than a
48
+ * transient failure: it drops that key's budget and cancels its timer.
49
+ */
50
+ schedule(target: RetryTarget): void;
51
+ /** Cancel every pending retry and drop all attempt budgets. */
52
+ clear(): void;
53
+ }
@@ -0,0 +1,64 @@
1
+ /** Every package the plugin provisions for LSP, in declaration order. */
2
+ export declare const LSP_PACKAGE_SPECIFIERS: readonly ["@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-tool-lsp"];
3
+ /** Whether the launcher watches the profile patch file after boot. */
4
+ export type PatchReload = 'live' | 'startup';
5
+ /** One `insert` row that claims a seam the plugin owns. */
6
+ export interface LegacySeamRow {
7
+ /** The row's `id`, or an empty string when the user wrote none. */
8
+ id: string;
9
+ /** The package name the row inserts. */
10
+ name: string;
11
+ }
12
+ /** A profile carrying a legacy LSP layer that must be removed. */
13
+ export interface LegacyLspSeam {
14
+ /** Profile name, i.e. its directory basename. */
15
+ profile: string;
16
+ /** Absolute profile directory. */
17
+ dir: string;
18
+ /** Absolute path of the profile's patch file. */
19
+ patchPath: string;
20
+ /** The conflicting `insert` rows, in file order. */
21
+ rows: LegacySeamRow[];
22
+ /** `dsh.profile.bundles` entries naming one of {@link LSP_PACKAGE_SPECIFIERS}. */
23
+ bundles: string[];
24
+ /** Declared dependency names drawn from {@link LSP_PACKAGE_SPECIFIERS}. */
25
+ dependencies: string[];
26
+ /** Whether the host hot-reloads the patch file, i.e. whether a restart is needed. */
27
+ patchReload: PatchReload;
28
+ }
29
+ /** Outcome of removing one profile's legacy LSP layer. */
30
+ export interface SeamMigrationResult {
31
+ profile: string;
32
+ patchPath: string;
33
+ /** Where the pre-edit content was kept. */
34
+ backupPath: string;
35
+ /** The rows that were removed. */
36
+ removed: LegacySeamRow[];
37
+ /** True when the host must restart before the removal takes effect. */
38
+ restartRequired: boolean;
39
+ }
40
+ /**
41
+ * Find every profile that still registers a legacy LSP seam.
42
+ *
43
+ * Best effort by design: profile files are the user's, so a directory that
44
+ * cannot be read or a patch file that does not parse is skipped rather than
45
+ * failing the status surface that calls this.
46
+ * @param home - the Harness home; defaults to `$DSH_HOME` or `~/.dsh`.
47
+ * @returns each matching profile, ordered by profile name.
48
+ */
49
+ export declare function findLegacyLspSeams(home?: string): Promise<LegacyLspSeam[]>;
50
+ /**
51
+ * Remove one profile's legacy LSP rows.
52
+ *
53
+ * The file is re-read here rather than trusting the caller's snapshot, so a
54
+ * migration always edits the current on-disk content. The rewrite is verified
55
+ * before it lands and the pre-edit content is written beside it first, so a
56
+ * failure leaves the profile exactly as it was.
57
+ * @param seam - a profile previously returned by {@link findLegacyLspSeams}.
58
+ * @param now - timestamp used in the backup filename; injectable for tests.
59
+ * @returns what was removed and where the backup went.
60
+ * @throws Error when the profile no longer carries a removable row, or the rewrite does not verify.
61
+ */
62
+ export declare function migrateLegacyLspSeam(seam: LegacyLspSeam, now?: Date): Promise<SeamMigrationResult>;
63
+ /** One-line remediation shared by the mount diagnostic and the status payload. */
64
+ export declare function describeLegacySeam(seam: LegacyLspSeam): string;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Coalesced runtime reconciliation for the enabled suite set.
3
+ *
4
+ * Settings, credential and catalog events arrive in bursts and can land while
5
+ * a slow MCP connection is still opening. One pass is kept in flight and at
6
+ * most one fresh follow-up is remembered, so a burst collapses into a single
7
+ * reconcile instead of a queue of stale snapshots that each reconnect the
8
+ * same servers.
9
+ *
10
+ * @module runtime/reconcile-scheduler
11
+ */
12
+ import type { Context } from '@deepseek-ai/cordis';
13
+ import type { Suite } from '../model/types.js';
14
+ import type { McpMountDiagnostic } from './mcp-mounts.js';
15
+ import type { RuntimeReconciler } from './reconciler.js';
16
+ /** The catalog surface one reconcile pass reads from and publishes to. */
17
+ export interface ReconcileHost {
18
+ /** Enabled user-dimension suites to reconcile. */
19
+ enabledSuites(): Promise<Suite[]>;
20
+ /** Publish the pass's MCP mount diagnostics for the status surface. */
21
+ publishMcpDiagnostics(diagnostics: McpMountDiagnostic[]): void;
22
+ }
23
+ export declare class ReconcileScheduler {
24
+ private readonly ctx;
25
+ private readonly runtime;
26
+ private readonly host;
27
+ private disposed;
28
+ private requested;
29
+ private pass;
30
+ private readonly pendingCredentialRefs;
31
+ private credentialFlush;
32
+ private readonly releaseCredentialUpdates;
33
+ constructor(ctx: Context, runtime: RuntimeReconciler, host: ReconcileHost);
34
+ /** Reconcile now, joining the pass in flight and remembering one follow-up for it. */
35
+ request(): Promise<void>;
36
+ /** Stop scheduling; later events are dropped. */
37
+ dispose(): void;
38
+ private reconcileOnce;
39
+ /**
40
+ * Coalesce credential updates: the debounce collapses a burst into one pass,
41
+ * and the pending refs are kept until that pass runs, so an update arriving
42
+ * mid-reconcile is never dropped.
43
+ */
44
+ private flushCredentialUpdates;
45
+ }
@@ -44,6 +44,8 @@ export declare class RuntimeReconciler {
44
44
  usesCredential(ref: string): boolean;
45
45
  /** Flag one MCP mount for an explicit rebuild on the next reconcile. */
46
46
  forceMcpRemount(suiteId: string, serverKey: string): void;
47
+ /** Flag every live MCP mount for a rebuild on the next reconcile. */
48
+ forceMcpRemountAll(): void;
47
49
  /** The mount key owning one derived serverName; undefined when not mounted here. */
48
50
  mcpServerOwner(serverName: string): {
49
51
  suiteId: string;