dsh-agent-plugins-market 0.6.2 → 0.7.1

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 (155) hide show
  1. package/README.md +117 -111
  2. package/README.zh.md +112 -98
  3. package/client/client.js +28533 -1511
  4. package/client/client.js.map +1 -1
  5. package/lib/application/catalog-context.js +188 -0
  6. package/lib/application/catalog.js +229 -1109
  7. package/lib/application/details.js +19 -8
  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 +263 -0
  11. package/lib/application/panel-resources.js +43 -14
  12. package/lib/application/ports.js +21 -0
  13. package/lib/application/project-agent-roles.js +8 -2
  14. package/lib/application/snapshot-cache.js +160 -0
  15. package/lib/application/source-store.js +521 -0
  16. package/lib/catalog/archive.js +35 -30
  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 +74 -55
  22. package/lib/catalog/plugin-variables.js +38 -0
  23. package/lib/catalog/project-config.js +2 -1
  24. package/lib/catalog/project-hooks.js +23 -2
  25. package/lib/catalog/scan-resolvers.js +29 -45
  26. package/lib/catalog/skills-parse.js +61 -10
  27. package/lib/catalog/source-catalog.js +10 -4
  28. package/lib/catalog/suite-components.js +7 -7
  29. package/lib/catalog/suite-scanner.js +3 -1
  30. package/lib/catalog/surfaces.js +5 -26
  31. package/lib/catalog/validate.js +7 -11
  32. package/lib/contracts/market.js +2 -0
  33. package/lib/contracts/settings.js +63 -0
  34. package/lib/index.js +115 -217
  35. package/lib/model/layouts.js +10 -1
  36. package/lib/model/types.js +26 -3
  37. package/lib/routes.js +57 -15
  38. package/lib/runtime/agent-role-names.js +70 -0
  39. package/lib/runtime/agent-role-router.js +142 -106
  40. package/lib/runtime/commands-mounts.js +55 -11
  41. package/lib/runtime/deadline.js +54 -0
  42. package/lib/runtime/dynamic-context.js +140 -0
  43. package/lib/runtime/feedback-tool.js +101 -46
  44. package/lib/runtime/hooks-mounts.js +9 -11
  45. package/lib/runtime/host-locale.js +49 -24
  46. package/lib/runtime/lsp-direct-config.js +19 -19
  47. package/lib/runtime/lsp-mounts.js +182 -70
  48. package/lib/runtime/lsp-server-state.js +4 -4
  49. package/lib/runtime/lsp-status.js +1 -1
  50. package/lib/runtime/mcp-auth-record.js +42 -0
  51. package/lib/runtime/mcp-backend.js +14 -14
  52. package/lib/runtime/mcp-client/config.js +8 -1
  53. package/lib/runtime/mcp-client/connection.js +38 -43
  54. package/lib/runtime/mcp-client/host-contract.js +10 -0
  55. package/lib/runtime/mcp-client/host-seams.js +8 -88
  56. package/lib/runtime/mcp-client/oauth.js +5 -7
  57. package/lib/runtime/mcp-client/plugin-identity.js +28 -0
  58. package/lib/runtime/mcp-client/projection.js +202 -0
  59. package/lib/runtime/mcp-client/tools.js +4 -189
  60. package/lib/runtime/mcp-config.js +36 -14
  61. package/lib/runtime/mcp-direct-config.js +33 -16
  62. package/lib/runtime/mcp-mounts.js +35 -75
  63. package/lib/runtime/mcp-overrides.js +5 -5
  64. package/lib/runtime/mcp-redaction.js +3 -2
  65. package/lib/runtime/mcp-status.js +7 -8
  66. package/lib/runtime/mount-lifecycle.js +73 -0
  67. package/lib/runtime/profile-seam.js +321 -0
  68. package/lib/runtime/project-runtime.js +34 -17
  69. package/lib/runtime/reconcile-scheduler.js +97 -0
  70. package/lib/runtime/reconciler.js +6 -1
  71. package/lib/runtime/regions.js +0 -19
  72. package/lib/runtime/server-config.js +6 -4
  73. package/lib/runtime/settings-namespace.js +144 -0
  74. package/lib/runtime/skills-provider.js +38 -14
  75. package/lib/runtime/source-auto-update.js +66 -0
  76. package/lib/{model/state.js → runtime/state-store.js} +9 -11
  77. package/lib/runtime/storage-migration.js +35 -18
  78. package/lib/runtime/subagent-catalog.js +5 -4
  79. package/lib/runtime/timer-seat.js +80 -0
  80. package/lib/runtime/tool-registry-observer.js +20 -20
  81. package/lib/runtime/user-commands.js +7 -3
  82. package/lib/runtime/user-panels.js +125 -46
  83. package/lib/runtime/user-store.js +124 -69
  84. package/lib/types/application/catalog-context.d.ts +124 -0
  85. package/lib/types/application/catalog.d.ts +108 -304
  86. package/lib/types/application/install-store.d.ts +21 -0
  87. package/lib/types/application/lsp-service.d.ts +36 -0
  88. package/lib/types/application/mcp-service.d.ts +77 -0
  89. package/lib/types/application/panel-resources.d.ts +8 -0
  90. package/lib/types/application/ports.d.ts +72 -0
  91. package/lib/types/application/queries.d.ts +15 -26
  92. package/lib/types/application/snapshot-cache.d.ts +51 -0
  93. package/lib/types/application/source-store.d.ts +100 -0
  94. package/lib/types/catalog/archive.d.ts +3 -11
  95. package/lib/types/catalog/component-files.d.ts +4 -0
  96. package/lib/types/catalog/fs-probes.d.ts +10 -0
  97. package/lib/types/catalog/manifests.d.ts +17 -16
  98. package/lib/types/catalog/native-project.d.ts +2 -2
  99. package/lib/types/catalog/paths.d.ts +53 -15
  100. package/lib/types/catalog/plugin-variables.d.ts +16 -0
  101. package/lib/types/catalog/scan-pipeline.d.ts +3 -3
  102. package/lib/types/catalog/scan-resolvers.d.ts +3 -9
  103. package/lib/types/catalog/skills-parse.d.ts +13 -7
  104. package/lib/types/catalog/source-catalog.d.ts +10 -5
  105. package/lib/types/catalog/suite-scanner.d.ts +8 -6
  106. package/lib/types/catalog/validate.d.ts +0 -2
  107. package/lib/types/contracts/lsp-status.d.ts +35 -0
  108. package/lib/types/contracts/market.d.ts +13 -2
  109. package/lib/types/contracts/mcp-status.d.ts +4 -0
  110. package/lib/types/contracts/settings.d.ts +59 -0
  111. package/lib/types/model/layouts.d.ts +10 -0
  112. package/lib/types/model/types.d.ts +29 -5
  113. package/lib/types/runtime/agent-role-names.d.ts +6 -0
  114. package/lib/types/runtime/agent-role-router.d.ts +59 -52
  115. package/lib/types/runtime/commands-mounts.d.ts +15 -7
  116. package/lib/types/runtime/deadline.d.ts +17 -0
  117. package/lib/types/runtime/dynamic-context.d.ts +81 -0
  118. package/lib/types/runtime/feedback-tool.d.ts +33 -11
  119. package/lib/types/runtime/hooks-mounts.d.ts +1 -1
  120. package/lib/types/runtime/host-locale.d.ts +20 -2
  121. package/lib/types/runtime/lsp-direct-config.d.ts +3 -3
  122. package/lib/types/runtime/lsp-mounts.d.ts +69 -19
  123. package/lib/types/runtime/lsp-status.d.ts +2 -1
  124. package/lib/types/runtime/mcp-auth-record.d.ts +29 -0
  125. package/lib/types/runtime/mcp-backend.d.ts +10 -14
  126. package/lib/types/runtime/mcp-client/config.d.ts +8 -2
  127. package/lib/types/runtime/mcp-client/host-contract.d.ts +78 -0
  128. package/lib/types/runtime/mcp-client/host-seams.d.ts +20 -64
  129. package/lib/types/runtime/mcp-client/oauth.d.ts +1 -1
  130. package/lib/types/runtime/mcp-client/plugin-identity.d.ts +16 -0
  131. package/lib/types/runtime/mcp-client/projection.d.ts +45 -0
  132. package/lib/types/runtime/mcp-client/tools.d.ts +5 -72
  133. package/lib/types/runtime/mcp-config.d.ts +25 -4
  134. package/lib/types/runtime/mcp-direct-config.d.ts +18 -4
  135. package/lib/types/runtime/mcp-mounts.d.ts +10 -15
  136. package/lib/types/runtime/mcp-overrides.d.ts +1 -1
  137. package/lib/types/runtime/mcp-status.d.ts +1 -1
  138. package/lib/types/runtime/mount-lifecycle.d.ts +53 -0
  139. package/lib/types/runtime/profile-seam.d.ts +64 -0
  140. package/lib/types/runtime/project-runtime.d.ts +10 -3
  141. package/lib/types/runtime/reconcile-scheduler.d.ts +45 -0
  142. package/lib/types/runtime/reconciler.d.ts +2 -0
  143. package/lib/types/runtime/regions.d.ts +1 -4
  144. package/lib/types/runtime/settings-namespace.d.ts +76 -0
  145. package/lib/types/runtime/skills-provider.d.ts +11 -2
  146. package/lib/types/runtime/source-auto-update.d.ts +32 -0
  147. package/lib/types/{model/state.d.ts → runtime/state-store.d.ts} +2 -2
  148. package/lib/types/runtime/storage-migration.d.ts +5 -3
  149. package/lib/types/runtime/subagent-catalog.d.ts +2 -0
  150. package/lib/types/runtime/timer-seat.d.ts +51 -0
  151. package/lib/types/runtime/tool-registry-observer.d.ts +12 -6
  152. package/lib/types/runtime/user-panels.d.ts +69 -19
  153. package/lib/types/runtime/user-store.d.ts +67 -20
  154. package/package.json +68 -24
  155. package/schemas/README.md +1 -1
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Claude Code dynamic context injection: `` !`command` `` in a command body or
3
+ * skill body runs the command and the placeholder becomes its output.
4
+ *
5
+ * One pass over the original text recognizes both spellings — the inline
6
+ * `` !`cmd` `` (only at a line start or after whitespace, matching the authoring
7
+ * rule) and the multi-line ```` ```! ```` fence — and every command runs
8
+ * sequentially in the calling session's directory. Outputs are inserted as
9
+ * plain text and never re-read, so a command cannot emit another placeholder.
10
+ *
11
+ * Failure is contained per invocation rather than per placeholder: the caller
12
+ * aborts the whole invocation with the command's own output, so a model never
13
+ * reads half-injected text. Exit code 1 counts as a result for the commands
14
+ * whose benign outcome it is (`grep`, `rg`, `find`, `diff`, `test`, …).
15
+ *
16
+ * The shell service is reached structurally: {@link shellSeamOf} resolves it
17
+ * from any context, and a profile without one keeps the author's literal text.
18
+ */
19
+ import type { Context } from '@deepseek-ai/cordis';
20
+ /** How long one injected command may run; the executor caps a larger request. */
21
+ export declare const DYNAMIC_CONTEXT_TIMEOUT_MS = 120000;
22
+ /** The requested forward cap for one injected command's stdout, in bytes. */
23
+ export declare const DYNAMIC_CONTEXT_MAX_OUTPUT_BYTES: number;
24
+ /** The slice of the host shell service this module uses. */
25
+ export interface ShellSeam {
26
+ resolve(request: {
27
+ command: string;
28
+ workdir?: string;
29
+ timeoutMs?: number;
30
+ stdoutMaxBytes?: number;
31
+ signal?: AbortSignal;
32
+ }): unknown;
33
+ run(spec: unknown): Promise<ShellOutcome>;
34
+ }
35
+ /** The fields of one shell run this module reads. */
36
+ export interface ShellOutcome {
37
+ exitCode: number | null;
38
+ timedOut: boolean;
39
+ aborted: boolean;
40
+ stdout: {
41
+ text: string;
42
+ truncated: boolean;
43
+ spillPath?: string;
44
+ };
45
+ stderr: {
46
+ text: string;
47
+ truncated: boolean;
48
+ spillPath?: string;
49
+ };
50
+ }
51
+ /**
52
+ * Resolve the host shell service from any context, whether or not the reading
53
+ * fiber injects `shell`.
54
+ *
55
+ * Cordis resolves `ctx.shell` by walking the reading fiber's ancestors, so a
56
+ * scope that does not inject the service throws `cannot get property "shell"
57
+ * without inject` even when a sibling fiber provides it. `ctx.get` reads the
58
+ * global service store instead and answers `undefined` in a profile that has
59
+ * no shell, which is what keeps the seam optional.
60
+ *
61
+ * @param ctx - the context the dynamic-context pass runs under.
62
+ * @returns the shell seam, or `undefined` when the profile provides none.
63
+ */
64
+ export declare function shellSeamOf(ctx: Context): ShellSeam | undefined;
65
+ export interface DynamicContextOptions {
66
+ shell: ShellSeam;
67
+ /** Session directory every injected command runs in. */
68
+ workdir?: string;
69
+ signal?: AbortSignal;
70
+ }
71
+ /**
72
+ * Run every placeholder's command and return the text the model receives.
73
+ *
74
+ * @param text - author text whose `${...}` variables and arguments are already resolved.
75
+ * @param options - the resolved shell seam, session directory and cancellation signal.
76
+ * @returns the text with each placeholder replaced by its command's output.
77
+ * @throws when a command fails, times out or is aborted, carrying the command and its output.
78
+ */
79
+ export declare function injectDynamicContext(text: string, options: DynamicContextOptions): Promise<string>;
80
+ /** Whether this command's exit status is a result rather than a failure. */
81
+ export declare function benignExit(exitCode: number | null, command: string): boolean;
@@ -5,13 +5,16 @@
5
5
  * default true), the model can call `report_market_issue` when it concludes a
6
6
  * problem it is looking at is caused by dsh-agent-plugins-market — a failed
7
7
  * install, a misdetected source, a market UI defect the user just described.
8
- * The tool files a GitHub issue on the plugin repository through the GitHub
9
- * REST API, or falls back to appending a local JSONL record under the plugin
10
- * data root when no repository host context is available.
8
+ *
9
+ * Filing happens in three steps, most preferred first: the `gh` CLI when it is
10
+ * installed and authenticated, the GitHub REST API when the process carries a
11
+ * `GITHUB_TOKEN` / `GH_TOKEN`, and otherwise nothing is filed — the caller
12
+ * gets the complete issue text plus a prefilled "new issue" URL that is opened
13
+ * in the browser and handed back to the user to submit.
11
14
  *
12
15
  * The tool is deliberately narrow: the title/body must describe the problem,
13
- * submissions are rate-limited, and the setting switch unregisters the tool
14
- * on the next reconcile pass.
16
+ * real submissions are rate-limited, and the setting switch unregisters the
17
+ * tool on the next reconcile pass.
15
18
  * @module runtime/feedback-tool
16
19
  */
17
20
  import type { Context } from '@deepseek-ai/cordis';
@@ -23,8 +26,13 @@ export declare const FEEDBACK_LABEL = "agent-feedback";
23
26
  /** One accepted or rejected submission outcome, mirrored to the model. */
24
27
  export interface FeedbackOutcome {
25
28
  ok: boolean;
26
- /** Where the report went: the issue URL, or the local fallback path label. */
29
+ /**
30
+ * The created issue URL, or — when nothing could file it — the prefilled
31
+ * "new issue" URL the user opens to submit the report themselves.
32
+ */
27
33
  location?: string;
34
+ /** The complete issue title and body; present when nothing was filed automatically. */
35
+ issueText?: string;
28
36
  reason?: string;
29
37
  }
30
38
  /** A structured experience report the model composes. */
@@ -36,14 +44,28 @@ export interface FeedbackReport {
36
44
  /** What actually happened (errors, wrong state, broken flow). */
37
45
  actual?: string;
38
46
  }
47
+ /** Injectable effects, so tests never reach the network or a real browser. */
48
+ export interface FeedbackPorts {
49
+ /** File the issue through the `gh` CLI; undefined when gh is missing or refused. */
50
+ ghIssue?: (title: string, body: string) => Promise<string | undefined>;
51
+ /** Open the prefilled issue page; best effort. */
52
+ openPage?: (url: string) => Promise<void>;
53
+ }
54
+ /** The public "new issue" page for one report, prefilled with title and body. */
55
+ export declare function newIssueUrl(title: string, body: string): string;
39
56
  /**
40
- * Submit one experience report. Tries the GitHub issue API first (works when
41
- * the deployment carries a GITHUB_TOKEN / GH_TOKEN with repo scope), then
42
- * falls back to the local JSONL spool under `<dataRoot>/feedback/`.
57
+ * Submit one experience report through `gh`, then the REST API, then a
58
+ * prefilled issue page the user submits themselves. Only a real submission
59
+ * advances the burst cooldown.
43
60
  */
44
- export declare function submitFeedback(dataRoot: string, report: FeedbackReport, now?: number): Promise<FeedbackOutcome>;
61
+ export declare function submitFeedback(dataRoot: string, report: FeedbackReport, now?: number, ports?: FeedbackPorts): Promise<FeedbackOutcome>;
45
62
  /** Render the model's structured report into a deterministic issue body. */
46
63
  export declare function renderFeedbackBody(report: FeedbackReport): string;
47
64
  export declare const FEEDBACK_TOOL_NAME: "report_market_issue";
48
- /** Register the feedback tool; returns the disposer. Undefined when the host has no tools service. */
65
+ /**
66
+ * Register the feedback tool; returns the disposer. Undefined when the host has
67
+ * no tools service to register on — the caller turns that into a mount-path
68
+ * diagnostic instead of an error, since the setting can simply have no effect
69
+ * on this host.
70
+ */
49
71
  export declare function mountFeedbackTool(hostCtx: Context, dataRoot: string, t: (key: string, params?: Record<string, string>) => string): (() => void) | undefined;
@@ -9,7 +9,7 @@ export declare class HooksMountRegistry {
9
9
  private readonly live;
10
10
  private readonly fingerprints;
11
11
  private readonly temporary;
12
- private queue;
12
+ private readonly passes;
13
13
  constructor(ctx: Context);
14
14
  /** Mount/unmount one bridge per suite to match the enabled suites exactly. */
15
15
  reconcile(enabledSuites: Suite[]): Promise<HooksMountDiagnostic[]>;
@@ -1,13 +1,31 @@
1
1
  /** Host runtime dictionary keys (mirrored for zh and en). */
2
- export type HostLocaleKey = 'subagentCatalogIntro' | 'subagentCatalogUpdated' | 'subagentCatalogEmpty' | 'subagentCatalogCall' | 'subagentCatalogInherit' | 'subagentCatalogDefaultEffort' | 'commandForwardTitle' | 'commandAcknowledged' | 'userCommandSourceLabel' | 'userCommandForwardTitle' | 'userCommandAcknowledged' | 'userSkillDescription' | 'feedbackToolCardTitle';
2
+ export type HostLocaleKey = 'subagentCatalogIntro' | 'subagentCatalogUpdated' | 'subagentCatalogEmpty' | 'subagentCatalogCall' | 'commandAcknowledged' | 'userCommandSourceLabel' | 'feedbackToolCardTitle';
3
3
  export type HostTranslate = (key: HostLocaleKey, params?: Record<string, string>) => string;
4
4
  interface ResolvedLocale {
5
5
  t: HostTranslate;
6
6
  }
7
7
  /** Resolve the active host language from settings; unknown values default to zh. */
8
8
  export declare function bindHostLocale(preference: string | undefined): HostTranslate;
9
- /** Read `locale.preference` from `$DSH_HOME/settings.yaml` (best effort). */
9
+ /**
10
+ * Wire the host settings service as the primary locale source, replacing the
11
+ * `settings.yaml` fallback.
12
+ * @param read - reads `locale.preference` from the settings service.
13
+ */
14
+ export declare function setHostLocaleSource(read: () => string | undefined): void;
15
+ /**
16
+ * The persisted locale preference: the host settings document when the service
17
+ * is composed, else `$DSH_HOME/settings.yaml` (best effort).
18
+ */
10
19
  export declare function readLocalePreference(): Promise<string | undefined>;
11
20
  /** Bind a host translator against the persisted locale preference. */
12
21
  export declare function loadHostLocale(): Promise<ResolvedLocale>;
22
+ /**
23
+ * Read `locale.preference` from the host settings service.
24
+ *
25
+ * The locale namespace is registered by the host's own client-locale plugin, so
26
+ * this reads `undefined` until that plugin mounts — which is why the caller
27
+ * re-binds when the service arrives rather than capturing the value once.
28
+ * @param settingsCtx - a context carrying the `settings` service.
29
+ */
30
+ export declare function readHostLocalePreference(settingsCtx: unknown): string | undefined;
13
31
  export {};
@@ -1,12 +1,12 @@
1
1
  import type { LspServerSpec } from '../model/types.js';
2
2
  /** The direct LSP server configuration file path. */
3
- export declare function lspServersPath(dataRoot: string): string;
3
+ export declare function lspServersPath(agentsRoot: string): string;
4
4
  /** Load the user-configured LSP servers; unreadable or invalid files yield an empty table. */
5
- export declare function loadLspServers(dataRoot: string): Promise<{
5
+ export declare function loadLspServers(agentsRoot: string): Promise<{
6
6
  servers: Record<string, LspServerSpec>;
7
7
  errors: string[];
8
8
  }>;
9
9
  /** Validate and persist the user-configured LSP servers; returns normalized specs. */
10
- export declare function saveLspServers(dataRoot: string, raw: unknown): Promise<{
10
+ export declare function saveLspServers(agentsRoot: string, raw: unknown): Promise<{
11
11
  servers: Record<string, LspServerSpec>;
12
12
  }>;
@@ -4,11 +4,22 @@
4
4
  *
5
5
  * Mounts reconcile against the enabled-suite set exactly like the MCP mounts:
6
6
  * reconcile() unmounts rows whose suite was disabled or removed and mounts
7
- * rows that appeared. The host LSP package is optional at runtime — without
8
- * it, suites keep every other surface and the manager reports a one-line
9
- * `host-missing` diagnostic that does not enter the retry schedule (a missing
10
- * package is not a transient failure). Mount failures that can resolve after
11
- * user action (installing the language server executable) do retry.
7
+ * rows that appeared. The first wanted server also mounts the capability seam
8
+ * this plugin owns end to end — `ctx.lsp` and the model-facing `lsp` tool —
9
+ * because the dsh installation does not carry the LSP packages; the last one
10
+ * releases it, so a profile that never enables an LSP suite never grows the
11
+ * `lsp` tool.
12
+ *
13
+ * Provisioning is a property of the plugin, not of the profile: both mounts
14
+ * are unconditional and the version comes from this package's dependency
15
+ * declaration. Nothing probes whether the profile already carries a seam — a
16
+ * seam another layer registered comes back as a `seam-conflict` diagnostic
17
+ * naming the layer to remove, so the aligned copy is the only one that can run.
18
+ *
19
+ * A failure the plugin cannot fix itself (a package that is genuinely absent
20
+ * from the profile) reports a one-line `host-missing` diagnostic that does not
21
+ * enter the retry schedule. Mount failures that can resolve after user action
22
+ * (installing the language server executable) do retry.
12
23
  *
13
24
  * Provider identity: one `${suiteId}/${serverKey}` provider id per server, so
14
25
  * two suites declaring the same server key mount independently. Extension
@@ -17,7 +28,9 @@
17
28
  * user resolves it through per-suite surface toggles.
18
29
  */
19
30
  import type { Context } from '@deepseek-ai/cordis';
20
- import type { Suite } from '../model/types.js';
31
+ import { type LegacyLspSeam } from './profile-seam.js';
32
+ import { type PluginPathContext } from '../catalog/plugin-variables.js';
33
+ import { type Suite } from '../model/types.js';
21
34
  export interface LspMountDiagnostic {
22
35
  suiteId: string;
23
36
  serverKey: string;
@@ -42,34 +55,77 @@ export declare function toLspServerConfig(spec: {
42
55
  initializationOptions?: unknown;
43
56
  configuration?: unknown;
44
57
  }): LspStdioServerConfig;
45
- /** Minimal structural shape of the dynamically imported `dsh-lsp-stdio` module. */
46
- interface LspStdioModule {
58
+ /** Resolve author-written paths in one declaration before the host spawns the server. */
59
+ export declare function expandLspServerConfig(config: LspStdioServerConfig, context: PluginPathContext): LspStdioServerConfig;
60
+ /** Minimal structural shape of a dynamically imported host plugin module. */
61
+ interface HostModule {
47
62
  default?: unknown;
48
63
  }
49
64
  export declare class LspMountRegistry {
50
65
  private readonly ctx;
51
66
  private readonly live;
52
67
  /** Serialize mount and unmount passes so a disable cannot race an in-flight spawn. */
53
- private reconcileQueue;
54
- /** Pending retry timers keyed by mount key, so a teardown can cancel them. */
68
+ private readonly passes;
69
+ /** Delayed re-attempts for mounts that are not live yet. */
55
70
  private readonly retries;
56
- /** Attempt count per mount key; reset whenever a retry succeeds. */
57
- private readonly attempts;
58
71
  /** Snapshot of the last reconciled suite set, replayed by retry passes. */
59
72
  private lastEnabled;
60
73
  /** Host module loader; overridable for tests. */
61
74
  private loadHost;
75
+ /**
76
+ * Reads the profile files when a seam conflict needs explaining. Injectable
77
+ * because it touches the machine's own `$DSH_HOME`, so a test must not
78
+ * inherit whatever the developer running it happens to have configured.
79
+ */
80
+ private readonly locateSeams;
81
+ /** `ctx.lsp` seam loader, mounted only when the profile supplies no service. */
82
+ private loadService;
83
+ /** `lsp` tool loader, mounted only when the profile publishes no such tool. */
84
+ private loadTool;
85
+ /** Capability plugins this registry mounted, in mount order, disposed with the last server. */
86
+ private readonly capability;
87
+ /** Whether the capability seam was already resolved for the current wanted set. */
88
+ private capabilityReady;
89
+ /** Why the capability seam is unavailable; re-attempted on the next reconcile pass. */
90
+ private capabilityFailure;
62
91
  /** Latest diagnostic per suite id; cleared when its suite mounts or leaves the wanted set. */
63
92
  private readonly lastDiagnostics;
93
+ /** Plugin storage root holding each suite's `${PLUGIN_DATA}` directory; unset leaves the variable verbatim. */
94
+ private pluginDataRoot?;
64
95
  /** Direct (user-configured) server provider; defaults to none. */
65
96
  private directProvider;
66
97
  private disabledProvider;
67
98
  private disabledSnapshot;
68
- constructor(ctx: Context, loadHost?: () => Promise<LspStdioModule | undefined>);
99
+ constructor(ctx: Context, loadHost?: () => Promise<HostModule | undefined>, loadService?: () => Promise<HostModule | undefined>, loadTool?: () => Promise<HostModule | undefined>, locateSeams?: () => Promise<LegacyLspSeam[]>);
69
100
  /** Queue one reconciliation behind any in-flight mount/unmount pass. */
70
101
  reconcile(enabledSuites: Suite[]): Promise<LspMountDiagnostic[]>;
71
102
  /** Mount/unmount LSP servers to match the enabled suites plus direct config exactly. */
72
103
  private reconcileNow;
104
+ /**
105
+ * Mount the capability seam this plugin delivers end to end: `ctx.lsp`, then the model-facing
106
+ * `lsp` tool.
107
+ *
108
+ * The plugin never asks what the profile already carries. Its dependency declaration decides
109
+ * the version, so deferring to a seam another layer registered would silently run whatever
110
+ * version that layer picked. Both mounts are therefore unconditional; a seam that is already
111
+ * taken comes back as a `seam-conflict` diagnostic naming the layer to remove, which is the
112
+ * only way the aligned copy can own it.
113
+ * @returns the failure, or undefined when the seam is ready.
114
+ */
115
+ private ensureCapability;
116
+ /** Mount one capability plugin and remember it for teardown with the last server. */
117
+ private mountCapability;
118
+ /**
119
+ * Explain a taken seam, naming the profile layer to remove when one can be
120
+ * found on disk. Releases before the plugin provisioned LSP itself told users
121
+ * to add that layer by hand, so the message points at the exact file and rows
122
+ * rather than at "the profile" in general.
123
+ */
124
+ private seamConflictReason;
125
+ /** Dispose the capability seam this registry mounted, once no wanted server needs it. */
126
+ private releaseCapability;
127
+ /** Install the plugin storage root per-suite data directories resolve against. */
128
+ setPluginDataRoot(root: string): void;
73
129
  /** Install the direct (user-configured) server provider used at reconcile time. */
74
130
  setDirectProvider(provider: () => Promise<Record<string, import('../model/types.js').LspServerSpec>>): void;
75
131
  setDisabledProvider(provider: () => Promise<Set<string>>): void;
@@ -81,12 +137,6 @@ export declare class LspMountRegistry {
81
137
  /** Mount one suite's full server table as a single `dsh-lsp-stdio` instance. */
82
138
  private mountWith;
83
139
  private unmount;
84
- /**
85
- * Schedule a delayed re-attempt for a mount that still is not live. A
86
- * `host-missing` failure passes no diagnostic and is never retried; the
87
- * schedule is bounded and resets with every reconcile pass.
88
- */
89
- private scheduleRetry;
90
140
  /** Dispose every live mount after queued reconciliation passes settle. */
91
141
  disposeAll(): Promise<void>;
92
142
  }
@@ -7,7 +7,8 @@
7
7
  * so a row's state is a declaration-and-diagnostic judgment: a suite whose
8
8
  * mount succeeded yields `mounted` (the seam guarantees registration), a
9
9
  * stored diagnostic yields its own state, and a declaration with neither
10
- * mounts nor diagnostics means the host packages are absent (`host-missing`).
10
+ * mounts nor diagnostics means the capability packages could not be loaded
11
+ * (`host-missing`).
11
12
  * Direct user-configured rows follow the same model under the sentinel suite
12
13
  * id `direct` (mirroring mcp-status's `plugin`/`direct` split).
13
14
  */
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The credential record one MCP server's OAuth grant lives under.
3
+ *
4
+ * `runtime/mcp-client/oauth.ts` writes those records; this module addresses
5
+ * the same key so a grant can be dropped without waking the transport that
6
+ * wrote it. The scope and the name fold must stay identical to that writer's,
7
+ * or the delete silently misses the record.
8
+ *
9
+ * @module runtime/mcp-auth-record
10
+ */
11
+ import { type CredentialKey } from './mcp-client/host-seams.js';
12
+ /** Credential-record scope for MCP OAuth state; per-server ids follow it. */
13
+ export declare const MCP_AUTH_RECORD_SCOPE = "mcp-auth";
14
+ /**
15
+ * Fold a `serverName` into a credential-key id segment. Server names carry
16
+ * `__` separators (e.g. `cloudflare__cloudflare-api`), and the key grammar
17
+ * admits only `[a-z0-9-]`, so non-conforming characters collapse to `-` —
18
+ * deterministic, so the same server always reads and writes one record.
19
+ */
20
+ export declare function mcpAuthRecordId(serverName: string): string;
21
+ /** The credential record key one MCP server's OAuth grant is stored under. */
22
+ export declare function mcpAuthRecordKey(serverName: string): CredentialKey;
23
+ /**
24
+ * Drop one MCP server's OAuth grant record so the next mount re-runs the
25
+ * browser authorization — the path for "I picked too narrow a scope".
26
+ * @param store - the host credentials service, when one is mounted.
27
+ * @throws when the credentials service is not mounted.
28
+ */
29
+ export declare function deleteMcpAuthGrant(store: unknown, serverName: string): Promise<void>;
@@ -4,8 +4,8 @@
4
4
  * and legacy SSE) or through the host's `@deepseek-ai/dsh-mcp-client`
5
5
  * (compatibility mode: no OAuth, no SSE, but the host-native implementation).
6
6
  *
7
- * The choice is a host settings namespace (`dsh-agent-plugins-market`,
8
- * `mcpEnhanced: boolean`, default true) registered by the plugin's node half:
7
+ * The choice is the `mcpEnhanced` field of the market's host settings
8
+ * namespace (see `contracts/settings.ts`) registered by the plugin's node half:
9
9
  * the registration is what makes the host 插件配置 tab serve our card, the
10
10
  * client card binds it for state, and the node half watches it to remount
11
11
  * servers when the switch flips. `readMcpBackend` remains only as the
@@ -16,28 +16,24 @@
16
16
  import z from '@deepseek-ai/schemastery';
17
17
  /** The MCP mount backend the market uses for suite servers. */
18
18
  export type McpBackend = 'builtin' | 'host';
19
- export declare const MCP_BACKENDS: readonly McpBackend[];
20
- /** Settings namespace this plugin registers; the key the plugin-config tab pairs our card by. */
21
- export declare const MCP_SETTINGS_NAMESPACE = "dsh-agent-plugins-market";
22
- /** Schema of the market settings namespace: the MCP enhancement switch, the download region, and the feedback tool switch. */
19
+ /**
20
+ * Schema of the market settings namespace: the switches this plugin's config
21
+ * card serves, with the defaults declared to the host. The values themselves
22
+ * belong to {@link MARKET_SETTINGS_DEFAULTS}; this schema states them, it does
23
+ * not own them.
24
+ */
23
25
  export declare const MarketSettingsSchema: z<Schemastery.ObjectS<{
24
- /** ON (default) = the built-in bridge with OAuth and SSE; OFF = host client compat mode. */
25
26
  mcpEnhanced: z<boolean, boolean>;
26
- /** Read native Agent layouts under the project root; default preserves existing discovery. */
27
27
  scanProjectLayouts: z<boolean, boolean>;
28
- /** Download region for GitHub acquisition; `auto` follows the interface language. */
29
28
  downloadRegion: z<"auto" | "global" | "china", "auto" | "global" | "china">;
30
- /** ON (default) = the `report_market_issue` model tool is registered; OFF = unregistered. */
31
29
  feedbackEnabled: z<boolean, boolean>;
30
+ autoUpdateSources: z<boolean, boolean>;
32
31
  }>, Schemastery.ObjectT<{
33
- /** ON (default) = the built-in bridge with OAuth and SSE; OFF = host client compat mode. */
34
32
  mcpEnhanced: z<boolean, boolean>;
35
- /** Read native Agent layouts under the project root; default preserves existing discovery. */
36
33
  scanProjectLayouts: z<boolean, boolean>;
37
- /** Download region for GitHub acquisition; `auto` follows the interface language. */
38
34
  downloadRegion: z<"auto" | "global" | "china", "auto" | "global" | "china">;
39
- /** ON (default) = the `report_market_issue` model tool is registered; OFF = unregistered. */
40
35
  feedbackEnabled: z<boolean, boolean>;
36
+ autoUpdateSources: z<boolean, boolean>;
41
37
  }>>;
42
38
  /** Path of the legacy persisted settings file under the plugin data root. */
43
39
  export declare function marketSettingsPath(dataRoot: string): string;
@@ -10,10 +10,16 @@
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;
15
+ /**
16
+ * How long one connection attempt may spend initializing (ms). A server that
17
+ * never answers would otherwise hold its process, transport and browser leg
18
+ * until the SDK's own 60-second request default expired; a working server
19
+ * handshakes well under a second, and a genuinely slow one can declare its own
20
+ * `startupTimeoutMs`.
21
+ */
22
+ export declare const DEFAULT_STARTUP_TIMEOUT_MS = 10000;
17
23
  /** Valid `serverName`, kept below the public tool-name budget. */
18
24
  export declare const SERVER_NAME_PATTERN: RegExp;
19
25
  /**
@@ -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 {};
@@ -1,55 +1,25 @@
1
1
  /**
2
- * Local ports of the tiny host seams the self-built MCP client needs.
2
+ * The host seams the self-built MCP client consumes, re-exported from the
3
+ * packages that own them.
3
4
  *
4
- * The market plugin must stay self-contained at runtime: it cannot import
5
- * host-internal packages that are not published at compatible versions, and
6
- * every one of these seams is a small pure function or constant. Each port
7
- * below is copied verbatim (semantics, not prose) from the harness source at
8
- * `0.1.1-rc.2` and carries the upstream reference it came from, so a future
9
- * host change can be re-diffed against `~/workspace/deepseek-harness`.
5
+ * These are small pure functions and constants, but each one encodes a rule
6
+ * this plugin must obey rather than a choice it gets to make: which credential
7
+ * keys the host accepts, how long a timer may be, which environment names never
8
+ * reach a child process, and which image failures a caller can correct. Reading
9
+ * them from the host keeps one owner per rule, so a host change reaches this
10
+ * plugin instead of leaving a copy behind.
10
11
  *
11
- * Anything larger than a handful of lines (the JSON Schema subset validator)
12
- * lives in its own module.
12
+ * The OAuth provider and attachment admission reach their services through the
13
+ * optional `credentials` / `attachments` seams at runtime, so the service types
14
+ * here stay structural — only the rules are imported.
13
15
  *
14
16
  * @module runtime/mcp-client/host-seams
15
17
  */
16
- /**
17
- * The largest delay `setTimeout` accepts without overflowing to a 1ms clamp
18
- * (2^31 - 1). Shared guard for reconnect and callback timers.
19
- */
20
- export declare const MAX_TIMER_DELAY_MS = 2147483647;
21
- /** `DSH_*` environment namespace owned by the harness. */
22
- export declare const DSH_ENV_PREFIX: "DSH_";
23
- /**
24
- * Credential-shaped environment names are NOT forwarded to MCP server child
25
- * processes (the harness's own `DEEPSEEK_API_KEY`/secrets must not leak into a
26
- * spawned server implicitly). Deliberately supplied env entries survive
27
- * because the config's explicit env merges after the scrub.
28
- */
29
- export declare const SENSITIVE_ENV_PATTERN: RegExp;
30
- /**
31
- * The ambient parent environment minus credential-shaped names and minus all
32
- * `DSH_*` names — the canonical base every harness child starts from. `PATH`,
33
- * `HOME`, locale, and proxy variables survive, so child CLIs run normally;
34
- * harness identity never leaks implicitly. Both scrubs match case-insensitively:
35
- * Windows environment names are case-insensitive, so a parent `dsh_*` entry
36
- * would otherwise survive and read back as `$env:DSH_*` in the child.
37
- * @returns a fresh environment object safe to hand to a child spawn.
38
- */
39
- export declare function scrubbedParentEnv(): Record<string, string>;
40
- /** A credential record key: `<scope>/<id>` with both segments in the grammar below. */
41
- declare const credentialKeyBrand: unique symbol;
42
- export type CredentialKey = string & {
43
- readonly [credentialKeyBrand]: true;
44
- };
45
- /**
46
- * Brand a scope and an id as a {@link CredentialKey}.
47
- * @param scope - the owning plugin's registered name (here: the `mcp-auth` record scope).
48
- * @param id - the plugin's own addressing unit (here: the folded serverName).
49
- * @returns the branded key.
50
- * @throws TypeError when either segment is not a lowercase hyphenated identifier.
51
- */
52
- export declare function credentialKey(scope: string, id: string): CredentialKey;
18
+ import type { CredentialKey } from '@deepseek-ai/dsh-credentials';
19
+ import type { SaveImageAttachment } from '@deepseek-ai/dsh-attachment';
20
+ export { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout';
21
+ export { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess';
22
+ export { credentialKey, type CredentialKey } from '@deepseek-ai/dsh-credentials';
53
23
  /**
54
24
  * Structural mirror of the host `CredentialProvider` record surface the OAuth
55
25
  * provider needs. Typed structurally because the store arrives via the
@@ -64,26 +34,12 @@ export interface CredentialRecordStore {
64
34
  readRecord(key: CredentialKey): Promise<unknown>;
65
35
  modifyRecord(key: CredentialKey, mutate: (current: unknown) => Promise<unknown>): Promise<unknown>;
66
36
  }
37
+ export { isImageAdmissionError, type ImageMediaType, type SaveImageAttachment } from '@deepseek-ai/dsh-attachment';
67
38
  /**
68
- * Distinguish caller-correctable image admission failures from storage faults,
69
- * so an admission refusal projects text diagnostics while a storage fault is
70
- * reported as such.
71
- */
72
- export declare function isImageAdmissionError(error: unknown): boolean;
73
- /** Raster formats supported by the durable attachment vocabulary. */
74
- export type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';
75
- /** One decoded image handed to the host attachment store. */
76
- export interface SaveImageAttachment {
77
- data: Buffer;
78
- mediaType: ImageMediaType;
79
- }
80
- /**
81
- * Structural mirror of the host `AttachmentStore.saveImages` surface. The
82
- * returned refs are opaque host values that ride inside `ContentBlock`
83
- * image blocks; typed `unknown` here and narrowed by the model content
84
- * contract at the projection site.
39
+ * Structural mirror of the host attachment store surface this plugin writes
40
+ * through. The service is resolved off the tool host at call time, so its
41
+ * concrete class is deliberately not imported.
85
42
  */
86
43
  export interface AttachmentStoreLike {
87
44
  saveImages(images: readonly SaveImageAttachment[]): Promise<readonly unknown[]>;
88
45
  }
89
- export {};