@khorsheed/dsh-capability-catalog 0.1.95

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 (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +286 -0
  4. package/README.zh.md +138 -0
  5. package/cordis.patch.yml +8 -0
  6. package/lib/client.js +10100 -0
  7. package/lib/index.js +4022 -0
  8. package/lib/invariant.js +25 -0
  9. package/lib/mcpConnector-C2TbkPBY.js +69 -0
  10. package/lib/typert.host.d.ts +3 -0
  11. package/lib/typert.host.js +1417 -0
  12. package/lib/typert.remote-client.d.ts +68 -0
  13. package/lib/typert.remote-client.js +1136 -0
  14. package/lib/types/capabilities.d.ts +103 -0
  15. package/lib/types/capabilities.js +116 -0
  16. package/lib/types/channels.d.ts +41 -0
  17. package/lib/types/channels.js +75 -0
  18. package/lib/types/client/AddMcpDialog.d.ts +13 -0
  19. package/lib/types/client/AddMcpDialog.js +79 -0
  20. package/lib/types/client/AddSkillModal.d.ts +17 -0
  21. package/lib/types/client/AddSkillModal.js +138 -0
  22. package/lib/types/client/BundleFileTree.d.ts +7 -0
  23. package/lib/types/client/BundleFileTree.js +74 -0
  24. package/lib/types/client/CapabilityCatalogCard.d.ts +57 -0
  25. package/lib/types/client/CapabilityCatalogCard.js +479 -0
  26. package/lib/types/client/CredentialField.d.ts +14 -0
  27. package/lib/types/client/CredentialField.js +9 -0
  28. package/lib/types/client/McpCards.d.ts +13 -0
  29. package/lib/types/client/McpCards.js +12 -0
  30. package/lib/types/client/McpServerManageModal.d.ts +18 -0
  31. package/lib/types/client/McpServerManageModal.js +63 -0
  32. package/lib/types/client/MetadataRow.d.ts +10 -0
  33. package/lib/types/client/MetadataRow.js +6 -0
  34. package/lib/types/client/ModalShell.d.ts +16 -0
  35. package/lib/types/client/ModalShell.js +27 -0
  36. package/lib/types/client/ModeChips.d.ts +38 -0
  37. package/lib/types/client/ModeChips.js +42 -0
  38. package/lib/types/client/SchemaView.d.ts +11 -0
  39. package/lib/types/client/SchemaView.js +143 -0
  40. package/lib/types/client/SkillCards.d.ts +28 -0
  41. package/lib/types/client/SkillCards.js +43 -0
  42. package/lib/types/client/SkillDetailModal.d.ts +69 -0
  43. package/lib/types/client/SkillDetailModal.js +167 -0
  44. package/lib/types/client/ToolCards.d.ts +28 -0
  45. package/lib/types/client/ToolCards.js +52 -0
  46. package/lib/types/client/ToolDetailModal.d.ts +12 -0
  47. package/lib/types/client/ToolDetailModal.js +29 -0
  48. package/lib/types/client/icons.d.ts +26 -0
  49. package/lib/types/client/icons.js +10 -0
  50. package/lib/types/client/index.d.ts +21 -0
  51. package/lib/types/client/index.js +196 -0
  52. package/lib/types/client/locales.d.ts +392 -0
  53. package/lib/types/client/locales.js +395 -0
  54. package/lib/types/client/mcp-model.d.ts +24 -0
  55. package/lib/types/client/mcp-model.js +87 -0
  56. package/lib/types/client/mode-model.d.ts +108 -0
  57. package/lib/types/client/mode-model.js +140 -0
  58. package/lib/types/client/preset-display.d.ts +21 -0
  59. package/lib/types/client/preset-display.js +20 -0
  60. package/lib/types/client/settle.d.ts +40 -0
  61. package/lib/types/client/settle.js +57 -0
  62. package/lib/types/client/slots.d.ts +86 -0
  63. package/lib/types/client/slots.js +1 -0
  64. package/lib/types/envHint.d.ts +22 -0
  65. package/lib/types/envHint.js +76 -0
  66. package/lib/types/import.d.ts +106 -0
  67. package/lib/types/import.js +635 -0
  68. package/lib/types/index.d.ts +216 -0
  69. package/lib/types/index.js +739 -0
  70. package/lib/types/invariant.d.ts +16 -0
  71. package/lib/types/invariant.js +23 -0
  72. package/lib/types/mcpConnector.d.ts +24 -0
  73. package/lib/types/mcpConnector.js +73 -0
  74. package/lib/types/mcpStore.d.ts +68 -0
  75. package/lib/types/mcpStore.js +235 -0
  76. package/lib/types/mcpTools.d.ts +45 -0
  77. package/lib/types/mcpTools.js +125 -0
  78. package/lib/types/mcps.d.ts +64 -0
  79. package/lib/types/mcps.js +157 -0
  80. package/lib/types/modes.d.ts +65 -0
  81. package/lib/types/modes.js +97 -0
  82. package/lib/types/namespace.d.ts +9 -0
  83. package/lib/types/namespace.js +8 -0
  84. package/lib/types/official-tools.d.ts +3 -0
  85. package/lib/types/official-tools.js +44 -0
  86. package/lib/types/preset-scope.d.ts +105 -0
  87. package/lib/types/preset-scope.js +117 -0
  88. package/lib/types/remote.d.ts +48 -0
  89. package/lib/types/remote.js +120 -0
  90. package/lib/types/scoped-delivery.d.ts +243 -0
  91. package/lib/types/scoped-delivery.js +686 -0
  92. package/lib/types/scoped-edits.d.ts +90 -0
  93. package/lib/types/scoped-edits.js +234 -0
  94. package/lib/types/settings.d.ts +28 -0
  95. package/lib/types/settings.js +28 -0
  96. package/lib/types/shellEnv.d.ts +65 -0
  97. package/lib/types/shellEnv.js +232 -0
  98. package/lib/types/skills.d.ts +103 -0
  99. package/lib/types/skills.js +303 -0
  100. package/lib/types/tool-origin.d.ts +29 -0
  101. package/lib/types/tool-origin.js +30 -0
  102. package/lib/types/types.d.ts +315 -0
  103. package/lib/types/types.js +8 -0
  104. package/lib/types/zip.d.ts +24 -0
  105. package/lib/types/zip.js +80 -0
  106. package/locale/en.json +6 -0
  107. package/locale/zh.json +6 -0
  108. package/package.json +119 -0
  109. package/scripts/gen-official-tools.mts +52 -0
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Resolving WHICH scope the catalog reads — and the one place where a listing
3
+ * and a fingerprint part company.
4
+ *
5
+ * The catalog reads the skill and tool registries at an agent preset's
6
+ * standing scope, because that is the view the model actually has. Resolving
7
+ * it can fail: the composition may mount no roster, the named preset may not
8
+ * exist, or its composition may refuse to mount.
9
+ *
10
+ * The roster face depends on the host line: 0.1.5 exposes a lease-free
11
+ * `standingKeyFor(id)`, while rc.1 replaced it with a LEASED
12
+ * `acquireScope(id)` — the resolved lease carries the key and a release on
13
+ * `Symbol.asyncDispose`, and a lease never released leaks a generation user,
14
+ * so the preset's scope is never collected. Both faces resolve through
15
+ * {@link acquireStandingScope}; callers that hold the key across reads own
16
+ * the release (see {@link ResolvedPresetScope.dispose}).
17
+ *
18
+ * A LISTING degrades on all three. The settings card must not go blank
19
+ * because a preset has a bad row, and the global layer is a useful, honestly
20
+ * unlabelled answer there.
21
+ *
22
+ * A FINGERPRINT must not. Falling back to the global layer and stamping the
23
+ * requested preset's name on the result hands back a hash that is not that
24
+ * preset's face — which is precisely the "claim with nothing behind it" the
25
+ * hash exists to eliminate. It was not hypothetical: on a real sub-dsh whose
26
+ * preset carried one invalid row, two scopes rostering two different presets
27
+ * produced one identical hash, silently, and the only way to notice was to
28
+ * already know the answer.
29
+ * @module @khorsheed/dsh-capability-catalog/preset-scope
30
+ */
31
+ /**
32
+ * Acquire one preset's standing scope key from whichever face the roster
33
+ * offers — 0.1.5's lease-free `standingKeyFor` first, rc.1's leased
34
+ * `acquireScope` when that is absent. Errors propagate unchanged, so the
35
+ * caller owns the strict/degrade policy; the caller also owns the release.
36
+ * @param roster - the agent-preset roster (already probed present).
37
+ * @param id - the preset to read, or undefined for the deployment default.
38
+ * @returns the key and the lease release, if the face leases.
39
+ */
40
+ export async function acquireStandingScope(roster, id) {
41
+ if (roster.standingKeyFor !== undefined)
42
+ return { key: await roster.standingKeyFor(id) };
43
+ if (roster.acquireScope === undefined)
44
+ return { key: undefined };
45
+ const lease = await roster.acquireScope(id);
46
+ const dispose = leaseRelease(lease);
47
+ return { key: leaseKey(lease), ...dispose === undefined ? {} : { dispose } };
48
+ }
49
+ /**
50
+ * Resolve the catalog's reading scope.
51
+ *
52
+ * Resolving a preset MOUNTS it (the roster's single-flight standing mount),
53
+ * so this is not a free read: asking for the capability face of a preset
54
+ * nothing has composed yet composes it.
55
+ * @param roster - the optional agent-preset roster (`ctx.get('agentPresets')`).
56
+ * @param presetId - the preset to read, or undefined for the deployment default.
57
+ * @param strict - fingerprint mode: throw rather than degrade to the global layer.
58
+ * @returns the scope to read at and the label it may carry.
59
+ * @throws in strict mode when the roster is absent for a named preset, or
60
+ * cannot resolve a standing scope.
61
+ */
62
+ export async function resolvePresetScope(roster, presetId, strict) {
63
+ if (roster?.standingKeyFor === undefined && roster?.acquireScope === undefined) {
64
+ // No roster at all. Naming a preset here is a caller error; asking for
65
+ // the default face is honest — there is no preset to misreport.
66
+ if (strict && presetId !== undefined) {
67
+ throw new Error(`capability-catalog: cannot fingerprint preset ${JSON.stringify(presetId)} — this composition mounts no agent-preset roster`);
68
+ }
69
+ return { scope: undefined, preset: undefined };
70
+ }
71
+ const wanted = presetId ?? roster?.defaultId;
72
+ let read;
73
+ try {
74
+ read = await acquireStandingScope(roster, wanted);
75
+ }
76
+ catch (error) {
77
+ if (strict) {
78
+ throw new Error(`capability-catalog: cannot fingerprint preset ${JSON.stringify(wanted)}: ${error instanceof Error ? error.message : String(error)}`);
79
+ }
80
+ return { scope: undefined, preset: undefined };
81
+ }
82
+ if (read.key === undefined) {
83
+ // No scope to hand out, so no caller will release the lease — do it here.
84
+ await read.dispose?.();
85
+ if (strict) {
86
+ throw new Error(`capability-catalog: the roster resolved no standing scope for preset ${JSON.stringify(wanted)}`);
87
+ }
88
+ return { scope: undefined, preset: undefined };
89
+ }
90
+ return {
91
+ scope: read.key,
92
+ preset: typeof wanted === 'string' && wanted !== '' ? wanted : undefined,
93
+ ...read.dispose === undefined ? {} : { dispose: read.dispose },
94
+ };
95
+ }
96
+ /** The lease's key, read structurally (`key` is opaque to the catalog). */
97
+ function leaseKey(lease) {
98
+ if (lease === null || (typeof lease !== 'object' && typeof lease !== 'function'))
99
+ return undefined;
100
+ return lease.key;
101
+ }
102
+ /**
103
+ * The lease's release, hung on `Symbol.asyncDispose` by the roster. The
104
+ * es2024 target does not type that symbol, so it is read through a cast —
105
+ * any runtime hosting an rc.1 roster defines it, because the roster's own
106
+ * lease literal is keyed by it. A lease without a callable release degrades
107
+ * to the 0.1.5 shape (nothing to dispose) rather than failing the read.
108
+ */
109
+ function leaseRelease(lease) {
110
+ if (lease === null || (typeof lease !== 'object' && typeof lease !== 'function'))
111
+ return undefined;
112
+ const asyncDispose = Symbol.asyncDispose;
113
+ const release = asyncDispose === undefined ? undefined : lease[asyncDispose];
114
+ if (typeof release !== 'function')
115
+ return undefined;
116
+ return async () => { await release.call(lease); };
117
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Catalog data layer: pure functions the Remote service calls. Kept separate
3
+ * from the service so the snapshot/add/detail/credential logic is unit-testable
4
+ * without a Cordis context and stays independent of the typert decorators.
5
+ * @module @khorsheed/dsh-capability-catalog/remote
6
+ */
7
+ import type { Context } from '@deepseek-ai/cordis';
8
+ import type { CapabilityCatalogSnapshot, CatalogAddSkillRequest, CatalogAddSkillResult, CatalogCredentialSetRequest, CatalogDeleteSkillResult, CatalogDirSkillInfo, CatalogJsonValue, CatalogSkillDetail, CatalogSkillFileRead, CatalogToolRow } from './types.ts';
9
+ import { type CredentialsSlice, type RegistrySlice, type SkillDefinitionLike } from './skills.ts';
10
+ import type { ToolOrigin } from './tool-origin.ts';
11
+ import { resolveSkillNameFromContent } from './import.ts';
12
+ export type { CredentialsSlice, RegistrySlice };
13
+ /** Project visible tools onto catalog rows with channel attribution. */
14
+ export declare function projectTools(schemas: readonly ToolSchemaLike[], mcpServers: readonly string[], appearedAfterApply: Set<string>, toolOrigins?: ReadonlyMap<string, ToolOrigin>): CatalogToolRow[];
15
+ /** Minimal ToolSchema shape (model-facing name/description/parameters). */
16
+ interface ToolSchemaLike {
17
+ readonly name: string;
18
+ readonly description?: string;
19
+ readonly parameters?: CatalogJsonValue;
20
+ }
21
+ /**
22
+ * Build the full catalog snapshot (skills + tools).
23
+ *
24
+ * `fingerprint` decides whether the snapshot is a LISTING or an IDENTITY: it
25
+ * loads every skill body (one registry read each) so the rows carry
26
+ * `bodySha`, and stamps `sha` — the capability hash. The browser card wants
27
+ * the listing; a condition's capability record wants the identity.
28
+ */
29
+ export declare function catalogSnapshot(workdir: string | undefined, registry: RegistrySlice, toolsSchemas: readonly ToolSchemaLike[], mcpServers: readonly string[], appearedAfterApply: ReadonlySet<string>, scopes?: readonly unknown[], toolOrigins?: ReadonlyMap<string, ToolOrigin>, options?: {
30
+ fingerprint?: boolean;
31
+ preset?: string;
32
+ }): Promise<CapabilityCatalogSnapshot>;
33
+ /** Load one skill detail. */
34
+ export declare function catalogDetail(ctx: Context, registry: RegistrySlice, name: string, workdir: string | undefined, scope?: unknown, fallback?: SkillDefinitionLike): Promise<CatalogSkillDetail | undefined>;
35
+ /** Read one skill-bundle file's text content on demand. */
36
+ export declare function catalogReadSkillFile(registry: RegistrySlice, name: string, filePath: string, workdir: string | undefined, scope?: unknown, fallback?: SkillDefinitionLike): Promise<CatalogSkillFileRead | undefined>;
37
+ /** Open the host's native directory chooser (the workspace "add" dialog). */
38
+ export declare function catalogPickDirectory(ctx: Context, signal?: AbortSignal): Promise<string | null>;
39
+ /** Delete a catalog-owned file skill (rejects built-in / plugin-provided). */
40
+ export declare function catalogDeleteSkill(registry: RegistrySlice, name: string, workdir: string | undefined, scope?: unknown): Promise<CatalogDeleteSkillResult>;
41
+ /** Set one declared credential value (never returned on the wire). */
42
+ export declare function catalogSetCredential(ctx: Context, request: CatalogCredentialSetRequest): Promise<boolean>;
43
+ /** Add a skill from a raw SKILL.md text payload into the managed root. */
44
+ export declare function catalogAddSkill(ctx: Context, request: CatalogAddSkillRequest, dshHome: string): Promise<CatalogAddSkillResult>;
45
+ /** List the skills inside a local container dir (for the add-skill chooser). */
46
+ export declare function catalogListDirSkills(dirPath: string): Promise<readonly CatalogDirSkillInfo[]>;
47
+ export { resolveSkillNameFromContent };
48
+ //# sourceMappingURL=remote.d.ts.map
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Catalog data layer: pure functions the Remote service calls. Kept separate
3
+ * from the service so the snapshot/add/detail/credential logic is unit-testable
4
+ * without a Cordis context and stays independent of the typert decorators.
5
+ * @module @khorsheed/dsh-capability-catalog/remote
6
+ */
7
+ import { collectSkills, loadSkillDetail, readSkillFileContent, deleteSkillDir, resolveServices, CREDENTIAL_REF_NAME, } from './skills.js';
8
+ import { attributeToolChannel } from './channels.js';
9
+ import { hashOf } from './capabilities.js';
10
+ import { addSkillFromPayload, commandInstall, listDirSkills, resolveSkillNameFromContent } from './import.js';
11
+ import { OFFICIAL_TOOLS } from './official-tools.js';
12
+ /** Load the cached official-tools names. */
13
+ function officialToolsSet() {
14
+ return new Set(OFFICIAL_TOOLS);
15
+ }
16
+ /** Project visible tools onto catalog rows with channel attribution. */
17
+ export function projectTools(schemas, mcpServers, appearedAfterApply, toolOrigins = new Map()) {
18
+ const official = officialToolsSet();
19
+ const rows = [];
20
+ for (const schema of schemas) {
21
+ const attributed = attributeToolChannel(schema.name, official, mcpServers, appearedAfterApply.has(schema.name), toolOrigins);
22
+ rows.push({
23
+ name: schema.name,
24
+ description: schema.description ?? '',
25
+ channel: attributed.channel,
26
+ confidence: attributed.confidence,
27
+ ...attributed.serverName !== undefined ? { serverName: attributed.serverName } : {},
28
+ ...attributed.owner !== undefined ? { owner: attributed.owner } : {},
29
+ ...schema.parameters !== undefined ? { parameters: schema.parameters } : {},
30
+ });
31
+ }
32
+ return rows;
33
+ }
34
+ /**
35
+ * Build the full catalog snapshot (skills + tools).
36
+ *
37
+ * `fingerprint` decides whether the snapshot is a LISTING or an IDENTITY: it
38
+ * loads every skill body (one registry read each) so the rows carry
39
+ * `bodySha`, and stamps `sha` — the capability hash. The browser card wants
40
+ * the listing; a condition's capability record wants the identity.
41
+ */
42
+ export async function catalogSnapshot(workdir, registry, toolsSchemas, mcpServers, appearedAfterApply, scopes = [undefined], toolOrigins = new Map(), options = {}) {
43
+ const fingerprint = options.fingerprint === true;
44
+ const base = await collectSkills(registry, workdir, scopes, { bodySha: fingerprint });
45
+ const tools = projectTools(toolsSchemas, mcpServers, appearedAfterApply, toolOrigins);
46
+ const snapshot = {
47
+ skills: base.skills,
48
+ tools,
49
+ mcpServers: mcpServers.map(name => ({ name, toolCount: tools.filter(t => t.serverName === name).length })),
50
+ channels: [
51
+ { channel: 'skill', count: base.skills.length },
52
+ { channel: 'tool', count: tools.length },
53
+ ],
54
+ ...options.preset !== undefined ? { preset: options.preset } : {},
55
+ };
56
+ return fingerprint ? { ...snapshot, sha: hashOf(snapshot) } : snapshot;
57
+ }
58
+ /** Load one skill detail. */
59
+ export async function catalogDetail(ctx, registry, name, workdir, scope = undefined, fallback) {
60
+ return loadSkillDetail(ctx, registry, name, workdir, scope, fallback);
61
+ }
62
+ /** Read one skill-bundle file's text content on demand. */
63
+ export async function catalogReadSkillFile(registry, name, filePath, workdir, scope = undefined, fallback) {
64
+ return readSkillFileContent(registry, name, filePath, workdir, scope, fallback);
65
+ }
66
+ /** Open the host's native directory chooser (the workspace "add" dialog). */
67
+ export async function catalogPickDirectory(ctx, signal) {
68
+ const picker = ctx.get?.('directoryPicker');
69
+ const cap = picker?.capability?.();
70
+ if (cap?.kind !== 'native' || cap.pick === undefined)
71
+ return null;
72
+ try {
73
+ return await cap.pick(signal ?? new AbortController().signal);
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ /** Delete a catalog-owned file skill (rejects built-in / plugin-provided). */
80
+ export async function catalogDeleteSkill(registry, name, workdir, scope = undefined) {
81
+ const base = workdir === undefined ? {} : { cwd: workdir };
82
+ const lookup = scope === undefined ? base : { ...base, scope };
83
+ const def = await registry.get(name, lookup);
84
+ if (def === undefined)
85
+ return { ok: false, error: 'skill not found' };
86
+ return deleteSkillDir(def);
87
+ }
88
+ /** Set one declared credential value (never returned on the wire). */
89
+ export async function catalogSetCredential(ctx, request) {
90
+ const { credentials } = resolveServices(ctx);
91
+ if (credentials === undefined)
92
+ return false;
93
+ try {
94
+ if (!CREDENTIAL_REF_NAME.test(request.key))
95
+ return false;
96
+ await credentials.set(request.key, request.value);
97
+ return true;
98
+ }
99
+ catch {
100
+ return false;
101
+ }
102
+ }
103
+ /** Add a skill from a raw SKILL.md text payload into the managed root. */
104
+ export async function catalogAddSkill(ctx, request, dshHome) {
105
+ const { registry } = resolveServices(ctx);
106
+ if (registry === undefined)
107
+ return { ok: false, error: 'skills service absent in this composition' };
108
+ if (request.channel === 'zip') {
109
+ return addSkillFromPayload(request, dshHome);
110
+ }
111
+ if (request.channel === 'command') {
112
+ return commandInstall(request, dshHome);
113
+ }
114
+ return { ok: false, error: 'github clone not wired in v1' };
115
+ }
116
+ /** List the skills inside a local container dir (for the add-skill chooser). */
117
+ export async function catalogListDirSkills(dirPath) {
118
+ return listDirSkills(dirPath);
119
+ }
120
+ export { resolveSkillNameFromContent };
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Preset-scoped skill delivery: a plugin-owned skills root whose entries are
3
+ * delivered **into one preset's scope layer** instead of into the global layer.
4
+ *
5
+ * The skill registry is layered by the scope a registration is made from, and a
6
+ * read merges the global layer with the viewing agent's scope chain. A provider
7
+ * registered through a context scoped to a preset's standing key is therefore
8
+ * consulted only for sessions on that preset — no read-time identity guessing,
9
+ * and no dependence on the borrowed lookup options.
10
+ *
11
+ * The per-skill policy lives in the skill's own frontmatter
12
+ * (`metadata.presetScope: [<preset-id>, …]`); an absent or empty list means the
13
+ * entry is not delivered by this module at all. A managed name that ALSO exists
14
+ * in a default skill root is refused delivery and reported: a leftover copy is
15
+ * served by the host provider in every preset, so silently delivering a second
16
+ * candidate would make the scope a lie.
17
+ *
18
+ * Scope of enforcement: this is a discovery/delivery policy for one Harness
19
+ * instance, not an authorization boundary. `ScopeKey` values are ordinary
20
+ * objects and the roster hands real ones to any caller, so a same-process plugin
21
+ * can claim membership. In-process agents and children joining the parent's
22
+ * composition are covered; native codex/claude-code and ACP backends start their
23
+ * own runtime and never consult this registry at all.
24
+ * @module @khorsheed/dsh-capability-catalog/scoped-delivery
25
+ */
26
+ import type { Context } from '@deepseek-ai/cordis';
27
+ import type { SkillCandidate, SkillDefinition, SkillProvider, SkillProviderControl } from '@deepseek-ai/dsh-skill';
28
+ import { type PresetRosterSlice } from './preset-scope.ts';
29
+ /**
30
+ * Resolve the preset-registry module by name. The 0.1.7-rc.1 host renamed
31
+ * `@deepseek-ai/dsh-agent-presets` to `@deepseek-ai/dsh-agent-preset-registry`
32
+ * (the livePresetMounts API carried over); 0.1.5/0.1.6 hosts install only the
33
+ * old name, and a host without preset composition installs neither — `null`,
34
+ * and {@link ScopedSkillDelivery.liveKeys} then reports nothing live, the same
35
+ * degrade the call's own catch encodes. A STATIC import of either name is a
36
+ * module-resolution failure at load on hosts installing only the other —
37
+ * pack-dist strips `dependencies`, so the specifier resolves against the
38
+ * host's install tree. Both specifiers stay static string literals so tsdown
39
+ * externalizes them and tsc type-checks them.
40
+ * @returns the module namespace, or null when neither name resolves.
41
+ */
42
+ export declare function loadPresetRegistry(): Promise<unknown>;
43
+ /** Test seam: drop the cached probe so a mocked resolution path re-runs. */
44
+ export declare function resetPresetRegistryProbeForTest(): void;
45
+ /** Provider label for managed entries; also the candidate's `provider` field. */
46
+ export declare const SCOPED_PROVIDER_NAME = "capability-catalog";
47
+ /** Candidate rank, beside the filesystem provider's `custom` roots. */
48
+ export declare const MANAGED_SKILL_RANK = 300;
49
+ /** Directory under the harness home that holds preset-scoped skills. */
50
+ export declare const SCOPED_SKILLS_DIR: string;
51
+ /** One managed skill read from the plugin-owned root. */
52
+ export interface ManagedSkill {
53
+ readonly name: string;
54
+ readonly description: string;
55
+ readonly whenToUse?: string;
56
+ readonly modelInvocable: boolean;
57
+ readonly userInvocable: boolean;
58
+ /** Preset ids this skill is delivered to; absent/empty means "not scoped". */
59
+ readonly presetScope: readonly string[] | undefined;
60
+ /** The skill's own directory (its `resourceBase`). */
61
+ readonly dir: string;
62
+ /** Absolute `SKILL.md` path. */
63
+ readonly path: string;
64
+ readonly content: string;
65
+ }
66
+ /** The managed root for one harness home. */
67
+ export declare function scopedSkillsRoot(dshHome: string): string;
68
+ /** One skill's delivery state, as the settings surface reads it. */
69
+ export interface ScopedSkillStatus {
70
+ readonly name: string;
71
+ readonly description: string;
72
+ readonly modelInvocable: boolean;
73
+ readonly userInvocable: boolean;
74
+ /** The managed `SKILL.md` path. */
75
+ readonly path: string;
76
+ readonly presets: readonly string[];
77
+ /** Whether a delivery provider currently serves this skill. */
78
+ readonly delivered: boolean;
79
+ /** Present when delivery is refused (the same name exists in a default root). */
80
+ readonly conflict?: string;
81
+ }
82
+ /** One preset's delivery state. */
83
+ export interface ScopedPresetStatus {
84
+ readonly presetId: string;
85
+ readonly skills: readonly string[];
86
+ /** Present when the preset's standing scope could not be resolved. */
87
+ readonly error?: string;
88
+ }
89
+ /** Whole-delivery status for the settings surface and the model-facing tool. */
90
+ export interface ScopedDeliveryStatus {
91
+ readonly enabled: boolean;
92
+ /** Why delivery is off (or reduced) — absent when it is fully operational. */
93
+ readonly reason?: string;
94
+ readonly root: string;
95
+ readonly watching: boolean;
96
+ readonly skills: readonly ScopedSkillStatus[];
97
+ readonly presets: readonly ScopedPresetStatus[];
98
+ /** Default roots a duplicate could hide in but that cannot be enumerated. */
99
+ readonly customRootsUnverifiable: boolean;
100
+ }
101
+ /** The services this module borrows. Every one is optional. */
102
+ export interface ScopedDeliveryDeps {
103
+ /** The harness home the managed root lives under. */
104
+ readonly dshHome: () => string;
105
+ /** The agent-preset roster (absent in a rosterless composition). */
106
+ readonly roster: () => PresetRosterSlice | undefined;
107
+ /** The skill registry's registration face (absent without `dsh-skill`). */
108
+ readonly registry: () => ScopedDeliveryRegistry | undefined;
109
+ /** The session's workspace, for project-root duplicate detection. */
110
+ readonly workdir?: () => string | undefined;
111
+ /** Logger for degradation and watcher diagnostics. */
112
+ readonly log: (message: string) => void;
113
+ }
114
+ /** The registry slice this module registers through. */
115
+ export interface ScopedDeliveryRegistry {
116
+ readonly registerProvider?: (create: (control: SkillProviderControl) => SkillProvider) => () => void;
117
+ }
118
+ /**
119
+ * Read the `presetScope` list out of a frontmatter block.
120
+ *
121
+ * Accepted forms, at any indentation (the canonical one is
122
+ * `metadata.presetScope`):
123
+ *
124
+ * ```yaml
125
+ * metadata:
126
+ * presetScope: [dsh-writing, dev]
127
+ * presetScope:
128
+ * - dsh-writing
129
+ * ```
130
+ * @param front - the frontmatter text without its `---` fences.
131
+ * @returns the declared ids, or undefined when the key is absent.
132
+ */
133
+ export declare function parsePresetScopeFrontmatter(front: string): readonly string[] | undefined;
134
+ /** Whether a string is a usable skill / preset id. */
135
+ export declare function isKebabId(value: string): boolean;
136
+ /** Split a SKILL.md into its frontmatter block and body. */
137
+ export declare function splitFrontmatter(content: string): {
138
+ front: string;
139
+ body: string;
140
+ } | undefined;
141
+ /**
142
+ * Parse one managed `SKILL.md` into its delivery record.
143
+ * @param content - the file text.
144
+ * @param dir - the skill's directory (its resource base).
145
+ * @param path - the absolute `SKILL.md` path.
146
+ * @returns the record, or undefined when the frontmatter is unusable.
147
+ */
148
+ export declare function managedSkillFrom(content: string, dir: string, path: string): ManagedSkill | undefined;
149
+ /** Whether this skill asks to be delivered to a specific preset set. */
150
+ export declare function isScoped(skill: ManagedSkill): boolean;
151
+ /** Project one managed skill onto a provider candidate for one preset. */
152
+ export declare function candidateFor(skill: ManagedSkill): SkillCandidate;
153
+ /** Project one managed skill onto the complete definition the loader returns. */
154
+ export declare function definitionFor(skill: ManagedSkill): SkillDefinition;
155
+ /** The default roots a duplicate copy could live in. */
156
+ export declare function defaultSkillRoots(dshHome: string, workdir?: string | undefined): readonly string[];
157
+ /**
158
+ * Find managed names that a default root also supplies.
159
+ *
160
+ * A duplicate is what makes a scope claim false: the host provider delivers the
161
+ * default root's copy into every preset regardless of this module's decision.
162
+ * @param skills - the managed entries under consideration.
163
+ * @param dshHome - the harness home.
164
+ * @param workdir - the session workspace, when known.
165
+ * @returns skill name → the default root that already supplies it.
166
+ */
167
+ export declare function detectConflicts(skills: readonly ManagedSkill[], dshHome: string, workdir?: string | undefined): Map<string, string>;
168
+ /**
169
+ * Scan the managed root for skill directories.
170
+ * @param root - the managed skills root.
171
+ * @returns every readable, usable entry, sorted by name.
172
+ */
173
+ export declare function scanManagedSkills(root: string): Promise<readonly ManagedSkill[]>;
174
+ /**
175
+ * Delivers managed skills into the scope layers of the presets their
176
+ * frontmatter names.
177
+ *
178
+ * Lifecycle: {@link start} once (watcher + first reconcile), {@link reconcile}
179
+ * whenever policy or files may have changed, {@link dispose} when the owning
180
+ * plugin unloads. Reconciliation never fails loudly: every problem becomes a
181
+ * status field, because a catalog must not fail the boot of the instance it is
182
+ * describing.
183
+ */
184
+ export declare class ScopedSkillDelivery {
185
+ private readonly ctx;
186
+ private readonly deps;
187
+ private readonly entries;
188
+ private managed;
189
+ private conflicts;
190
+ private presetErrors;
191
+ private watcher;
192
+ private timer;
193
+ private watching;
194
+ private failure;
195
+ private started;
196
+ private disposed;
197
+ /** The probed registry's mount list (undefined until start()'s probe resolves). */
198
+ private presetMounts;
199
+ /**
200
+ * @param ctx - the host context the delivery scopes are minted under.
201
+ * @param deps - borrowed services and paths, all optional at runtime.
202
+ */
203
+ constructor(ctx: Context, deps: ScopedDeliveryDeps);
204
+ /** Start watching the managed root and deliver what it currently holds. */
205
+ start(): Promise<void>;
206
+ /** Re-read policy and files, then converge the delivery registrations. */
207
+ reconcile(): Promise<void>;
208
+ /** The current status, for the settings surface and the model-facing tool. */
209
+ status(): ScopedDeliveryStatus;
210
+ /** Stop watching and dispose every delivery registration. */
211
+ dispose(): Promise<void>;
212
+ /** The managed root under the current harness home. */
213
+ private root;
214
+ /** Create the managed root if it is still absent. */
215
+ private ensureRoot;
216
+ /** Watch the managed root, degrading to on-demand rescans when it cannot. */
217
+ private watchRoot;
218
+ /** Coalesce a burst of filesystem events into one reconcile. */
219
+ private scheduleRescan;
220
+ /** The standing keys that still have a live mount in this process. */
221
+ private liveKeys;
222
+ /** Resolve one preset's standing key, remembering why it failed. */
223
+ private resolveKey;
224
+ /** Register one delivery provider into a preset's layer. */
225
+ private register;
226
+ /** Register the provider that serves every preset (the unscoped skills). */
227
+ private registerGlobal;
228
+ /**
229
+ * The provider serving one preset. It reads only its own preset's assignment
230
+ * and re-reads the files, so a policy or content edit needs no re-registration
231
+ * beyond the invalidation reconcile already performs.
232
+ */
233
+ private providerFor;
234
+ /** The managed skills one delivery currently serves. */
235
+ private skillsFor;
236
+ /** Invalidate every registration's completed catalogs. */
237
+ private invalidateAll;
238
+ /** Dispose every registration. */
239
+ private disposeEntries;
240
+ /** Dispose one registration and forget it. */
241
+ private dropEntry;
242
+ }
243
+ //# sourceMappingURL=scoped-delivery.d.ts.map