dsh-agent-plugins-market 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/client/client.js +1591 -1726
- package/client/client.js.map +1 -1
- package/lib/application/agent-roles.js +53 -0
- package/lib/application/catalog-context.js +2 -2
- package/lib/application/catalog.js +4 -8
- package/lib/{runtime → application}/deadline.js +1 -1
- package/lib/application/details.js +3 -3
- package/lib/application/json-file.js +43 -0
- package/lib/{runtime → application/lsp}/lsp-direct-config.js +9 -7
- package/lib/application/lsp/lsp-server-state.js +17 -0
- package/lib/{runtime → application/lsp}/lsp-status.js +22 -7
- package/lib/{runtime → application/lsp}/profile-seam.js +1 -1
- package/lib/application/lsp-service.js +5 -5
- package/lib/{runtime → application/mcp}/mcp-backend.js +3 -19
- package/lib/{runtime → application/mcp}/mcp-config.js +4 -4
- package/lib/{runtime → application/mcp}/mcp-direct-config.js +8 -49
- package/lib/{runtime → application/mcp}/mcp-overrides.js +3 -5
- package/lib/{runtime → application/mcp}/mcp-status.js +13 -7
- package/lib/application/mcp-service.js +11 -35
- package/lib/application/panel-resources.js +48 -11
- package/lib/{runtime → application/panels}/user-hooks.js +2 -2
- package/lib/{runtime → application/panels}/user-store.js +2 -2
- package/lib/application/ports.js +1 -0
- package/lib/application/project-agent-roles.js +1 -1
- package/lib/{runtime → application}/server-config.js +7 -17
- package/lib/application/snapshot-cache.js +1 -1
- package/lib/application/source-store.js +3 -4
- package/lib/{runtime → application/state}/state-store.js +5 -7
- package/lib/{runtime → application/state}/storage-migration.js +4 -4
- package/lib/catalog/source-catalog.js +0 -4
- package/lib/contracts/lsp.js +1 -0
- package/lib/contracts/market.js +0 -1
- package/lib/contracts/mcp.js +1 -0
- package/lib/index.js +42 -33
- package/lib/routes.js +2 -9
- package/lib/runtime/{agent-role-router.js → agents/agent-role-router.js} +7 -53
- package/lib/runtime/{subagent-catalog.js → agents/subagent-catalog.js} +24 -16
- package/lib/runtime/core/context.js +31 -0
- package/lib/runtime/{reconciler.js → core/reconciler.js} +5 -5
- package/lib/runtime/host/failure-detail.js +58 -0
- package/lib/runtime/{feedback-tool.js → host/feedback-tool.js} +1 -1
- package/lib/runtime/host/host-locale.js +83 -0
- package/lib/runtime/{settings-namespace.js → host/settings-namespace.js} +1 -1
- package/lib/runtime/host/shell-path.js +196 -0
- package/lib/runtime/{tool-registry-observer.js → host/tool-registry-observer.js} +2 -1
- package/lib/runtime/{lsp-mounts.js → lsp/lsp-mounts.js} +69 -20
- package/lib/runtime/{mcp-client → mcp/bridge}/bridge.js +52 -22
- package/lib/runtime/{mcp-client → mcp/bridge}/connection.js +2 -2
- package/lib/runtime/{mcp-client → mcp/bridge}/oauth.js +1 -1
- package/lib/runtime/{mcp-client → mcp/bridge}/tools.js +2 -2
- package/lib/runtime/{mcp-auth-record.js → mcp/mcp-auth-record.js} +1 -1
- package/lib/runtime/{mcp-mounts.js → mcp/mcp-mounts.js} +14 -7
- package/lib/runtime/{user-commands.js → panels/user-commands.js} +3 -3
- package/lib/runtime/{user-panels.js → panels/user-panels.js} +2 -2
- package/lib/runtime/{commands-mounts.js → surfaces/commands-mounts.js} +7 -7
- package/lib/runtime/{hooks-mounts.js → surfaces/hooks-mounts.js} +2 -2
- package/lib/runtime/{project-runtime.js → surfaces/project-runtime.js} +5 -5
- package/lib/runtime/{skills-provider.js → surfaces/skills-provider.js} +4 -4
- package/lib/types/application/agent-roles.d.ts +30 -0
- package/lib/types/application/catalog.d.ts +4 -7
- package/lib/types/application/details.d.ts +1 -1
- package/lib/types/application/json-file.d.ts +12 -0
- package/lib/types/{runtime → application/lsp}/lsp-direct-config.d.ts +1 -1
- package/lib/types/{runtime → application/lsp}/lsp-status.d.ts +3 -3
- package/lib/types/{runtime → application/mcp}/mcp-backend.d.ts +4 -9
- package/lib/types/{runtime/mcp-client/config.d.ts → application/mcp/mcp-bridge-config.d.ts} +11 -1
- package/lib/types/{runtime → application/mcp}/mcp-config.d.ts +6 -7
- package/lib/types/application/mcp/mcp-direct-config.d.ts +17 -0
- package/lib/types/{runtime → application/mcp}/mcp-overrides.d.ts +1 -1
- package/lib/types/{runtime → application/mcp}/mcp-status.d.ts +7 -7
- package/lib/types/application/mcp-service.d.ts +4 -11
- package/lib/types/application/panel-resources.d.ts +9 -2
- package/lib/types/{runtime → application/panels}/user-hooks.d.ts +1 -1
- package/lib/types/{runtime → application/panels}/user-store.d.ts +1 -1
- package/lib/types/application/ports.d.ts +17 -3
- package/lib/types/application/project-agent-roles.d.ts +1 -1
- package/lib/types/application/queries.d.ts +3 -6
- package/lib/types/{runtime → application}/regions.d.ts +1 -1
- package/lib/types/{runtime → application/state}/state-store.d.ts +2 -2
- package/lib/types/catalog/source-catalog.d.ts +0 -2
- package/lib/types/contracts/lsp-status.d.ts +6 -0
- package/lib/types/contracts/lsp.d.ts +16 -0
- package/lib/types/contracts/market.d.ts +1 -4
- package/lib/types/contracts/mcp-status.d.ts +21 -2
- package/lib/types/contracts/mcp.d.ts +20 -0
- package/lib/types/model/types.d.ts +0 -14
- package/lib/types/runtime/{agent-role-router.d.ts → agents/agent-role-router.d.ts} +3 -28
- package/lib/types/runtime/{subagent-catalog.d.ts → agents/subagent-catalog.d.ts} +6 -6
- package/lib/types/runtime/core/context.d.ts +14 -0
- package/lib/types/runtime/{reconcile-scheduler.d.ts → core/reconcile-scheduler.d.ts} +2 -2
- package/lib/types/runtime/{reconciler.d.ts → core/reconciler.d.ts} +8 -8
- package/lib/types/runtime/host/failure-detail.d.ts +6 -0
- package/lib/types/runtime/host/host-locale.d.ts +66 -0
- package/lib/types/runtime/{model-catalog.d.ts → host/model-catalog.d.ts} +1 -1
- package/lib/types/runtime/{settings-namespace.d.ts → host/settings-namespace.d.ts} +2 -2
- package/lib/types/runtime/host/shell-path.d.ts +70 -0
- package/lib/types/runtime/{lsp-mounts.d.ts → lsp/lsp-mounts.d.ts} +17 -11
- package/lib/types/runtime/{mcp-client → mcp/bridge}/bridge.d.ts +1 -1
- package/lib/types/runtime/{mcp-client → mcp/bridge}/connection.d.ts +3 -11
- package/lib/types/runtime/{mcp-client → mcp/bridge}/host-contract.d.ts +1 -1
- package/lib/types/runtime/{mcp-client → mcp/bridge}/oauth.d.ts +1 -1
- package/lib/types/runtime/{mcp-client → mcp/bridge}/transport.d.ts +1 -1
- package/lib/types/runtime/{mcp-auth-record.d.ts → mcp/mcp-auth-record.d.ts} +1 -1
- package/lib/types/runtime/{mcp-credentials.d.ts → mcp/mcp-credentials.d.ts} +1 -1
- package/lib/types/runtime/{mcp-mounts.d.ts → mcp/mcp-mounts.d.ts} +5 -11
- package/lib/types/runtime/{user-commands.d.ts → panels/user-commands.d.ts} +1 -1
- package/lib/types/runtime/{user-panels.d.ts → panels/user-panels.d.ts} +1 -1
- package/lib/types/runtime/{commands-mounts.d.ts → surfaces/commands-mounts.d.ts} +2 -2
- package/lib/types/runtime/{hooks-mounts.d.ts → surfaces/hooks-mounts.d.ts} +1 -1
- package/lib/types/runtime/{project-runtime.d.ts → surfaces/project-runtime.d.ts} +3 -3
- package/lib/types/runtime/{skills-provider.d.ts → surfaces/skills-provider.d.ts} +1 -1
- package/package.json +35 -32
- package/lib/runtime/host-locale.js +0 -89
- package/lib/runtime/legacy-root-migration.js +0 -12
- package/lib/runtime/lsp-server-state.js +0 -19
- package/lib/runtime/mcp-client/json-schema-subset.js +0 -181
- package/lib/types/runtime/host-locale.d.ts +0 -31
- package/lib/types/runtime/legacy-root-migration.d.ts +0 -2
- package/lib/types/runtime/mcp-client/json-schema-subset.d.ts +0 -58
- package/lib/types/runtime/mcp-direct-config.d.ts +0 -39
- /package/lib/{runtime/mcp-client/config.js → application/mcp/mcp-bridge-config.js} +0 -0
- /package/lib/{runtime → application/mcp}/mcp-redaction.js +0 -0
- /package/lib/{runtime → application}/regions.js +0 -0
- /package/lib/runtime/{agent-role-names.js → agents/agent-role-names.js} +0 -0
- /package/lib/runtime/{mount-lifecycle.js → core/mount-lifecycle.js} +0 -0
- /package/lib/runtime/{reconcile-scheduler.js → core/reconcile-scheduler.js} +0 -0
- /package/lib/runtime/{source-auto-update.js → core/source-auto-update.js} +0 -0
- /package/lib/runtime/{timer-seat.js → core/timer-seat.js} +0 -0
- /package/lib/runtime/{model-catalog.js → host/model-catalog.js} +0 -0
- /package/lib/runtime/{plugin-message-source.js → host/plugin-message-source.js} +0 -0
- /package/lib/runtime/{mcp-client → mcp/bridge}/host-contract.js +0 -0
- /package/lib/runtime/{mcp-client → mcp/bridge}/host-seams.js +0 -0
- /package/lib/runtime/{mcp-client → mcp/bridge}/plugin-identity.js +0 -0
- /package/lib/runtime/{mcp-client → mcp/bridge}/projection.js +0 -0
- /package/lib/runtime/{mcp-client → mcp/bridge}/transport.js +0 -0
- /package/lib/runtime/{mcp-credentials.js → mcp/mcp-credentials.js} +0 -0
- /package/lib/runtime/{dynamic-context.js → surfaces/dynamic-context.js} +0 -0
- /package/lib/types/{runtime → application}/deadline.d.ts +0 -0
- /package/lib/types/{runtime → application/lsp}/lsp-server-state.d.ts +0 -0
- /package/lib/types/{runtime → application/lsp}/profile-seam.d.ts +0 -0
- /package/lib/types/{runtime → application/mcp}/mcp-redaction.d.ts +0 -0
- /package/lib/types/{runtime → application}/server-config.d.ts +0 -0
- /package/lib/types/{runtime → application/state}/storage-migration.d.ts +0 -0
- /package/lib/types/runtime/{agent-role-names.d.ts → agents/agent-role-names.d.ts} +0 -0
- /package/lib/types/runtime/{mount-lifecycle.d.ts → core/mount-lifecycle.d.ts} +0 -0
- /package/lib/types/runtime/{source-auto-update.d.ts → core/source-auto-update.d.ts} +0 -0
- /package/lib/types/runtime/{timer-seat.d.ts → core/timer-seat.d.ts} +0 -0
- /package/lib/types/runtime/{feedback-tool.d.ts → host/feedback-tool.d.ts} +0 -0
- /package/lib/types/runtime/{plugin-message-source.d.ts → host/plugin-message-source.d.ts} +0 -0
- /package/lib/types/runtime/{tool-registry-observer.d.ts → host/tool-registry-observer.d.ts} +0 -0
- /package/lib/types/runtime/{mcp-client → mcp/bridge}/host-seams.d.ts +0 -0
- /package/lib/types/runtime/{mcp-client → mcp/bridge}/plugin-identity.d.ts +0 -0
- /package/lib/types/runtime/{mcp-client → mcp/bridge}/projection.d.ts +0 -0
- /package/lib/types/runtime/{mcp-client → mcp/bridge}/tools.d.ts +0 -0
- /package/lib/types/runtime/{dynamic-context.d.ts → surfaces/dynamic-context.d.ts} +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** Installed suite and user resources share one inventory; paths never come from HTTP callers. */
|
|
2
|
-
import { realpath, stat
|
|
2
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
3
|
+
import { isDeepStrictEqual as deepEqual } from 'node:util';
|
|
3
4
|
import { writeFileAtomic } from '@deepseek-ai/dsh-atomic-write';
|
|
4
5
|
import { defaultMarkdownResources, resourceText } from '../catalog/component-files.js';
|
|
5
6
|
import { pluginRootOf } from '../catalog/plugin-variables.js';
|
|
6
7
|
import { isWithin, suiteDataDir } from '../catalog/paths.js';
|
|
7
8
|
import { stripFrontmatter } from '../catalog/skills-parse.js';
|
|
8
|
-
import { parseFrontmatterRecord } from '
|
|
9
|
+
import { parseFrontmatterRecord } from './panels/user-store.js';
|
|
9
10
|
/**
|
|
10
11
|
* Id of one panel entry backed by a file inside an installed suite's checkout.
|
|
11
12
|
* The leading `[` is what distinguishes a plugin entry from a user-authored
|
|
@@ -18,6 +19,16 @@ export function pluginResourceId(sourceId, suiteId, kind, name) {
|
|
|
18
19
|
export function isPluginResourceId(id) {
|
|
19
20
|
return id.startsWith('[');
|
|
20
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Frontmatter keys beyond the enable switch that a panel control may diff on a
|
|
24
|
+
* plugin document, per kind: the harness invocation pair on skills, and the
|
|
25
|
+
* model-routing keys the persona form edits on agents.
|
|
26
|
+
*/
|
|
27
|
+
const FLIPPABLE_KEYS = {
|
|
28
|
+
skills: ['disable-model-invocation', 'user-invocable'],
|
|
29
|
+
commands: [],
|
|
30
|
+
agents: ['model', 'provider', 'reasoning_effort', 'reasoningEffort']
|
|
31
|
+
};
|
|
21
32
|
export function createPanelResources(catalog, users) {
|
|
22
33
|
return {
|
|
23
34
|
skills: new PanelResources(catalog, users.skills, 'skills'),
|
|
@@ -96,11 +107,12 @@ class PanelResources {
|
|
|
96
107
|
* A plugin resource is writable only when it sits inside the user dimension
|
|
97
108
|
* root; suite checkouts elsewhere on disk stay read-only. The user-panel
|
|
98
109
|
* store lives in the shared Agent layout root instead, so containment is
|
|
99
|
-
* measured against the catalog's root, not the panel directory.
|
|
110
|
+
* measured against the catalog's root, not the panel directory. Callers pass
|
|
111
|
+
* the entry they already fetched: a full inventory scan per call would read
|
|
112
|
+
* every suite document twice per write.
|
|
100
113
|
*/
|
|
101
|
-
async pluginPath(
|
|
102
|
-
|
|
103
|
-
if (entry?.origin !== 'plugin')
|
|
114
|
+
async pluginPath(entry) {
|
|
115
|
+
if (entry.origin !== 'plugin')
|
|
104
116
|
throw new Error('Unknown installed plugin resource');
|
|
105
117
|
if (entry.path.endsWith('.json'))
|
|
106
118
|
throw new Error('Inline manifest resources are read-only; edit their source manifest');
|
|
@@ -113,12 +125,37 @@ class PanelResources {
|
|
|
113
125
|
async update(id, text) {
|
|
114
126
|
if (!isPluginResourceId(id))
|
|
115
127
|
return this.users.update(id, text);
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
const previous = await this.get(id);
|
|
129
|
+
if (previous === undefined)
|
|
130
|
+
throw new Error('Unknown installed plugin resource');
|
|
131
|
+
this.assertStateFlipOnly(previous.rawText, text);
|
|
132
|
+
await writeFileAtomic(await this.pluginPath(previous), text, { mode: 0o644 });
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A suite owns its files' content: the panel may only flip state frontmatter.
|
|
136
|
+
* The document body must stay byte-identical, and the frontmatter diff is
|
|
137
|
+
* confined to `disabled` everywhere, plus {@link FLIPPABLE_KEYS}'s per-kind
|
|
138
|
+
* control keys — the harness invocation pair on skills, model routing on
|
|
139
|
+
* agents.
|
|
140
|
+
*/
|
|
141
|
+
assertStateFlipOnly(previousText, text) {
|
|
142
|
+
if (stripFrontmatter(previousText) !== stripFrontmatter(text))
|
|
143
|
+
throw new Error('plugin resources are read-only; the suite owns their content');
|
|
144
|
+
const before = parseFrontmatterRecord(previousText);
|
|
145
|
+
const after = parseFrontmatterRecord(text);
|
|
146
|
+
const flippable = new Set(['disabled', ...FLIPPABLE_KEYS[this.kind]]);
|
|
147
|
+
for (const key of new Set([...Object.keys(before), ...Object.keys(after)])) {
|
|
148
|
+
if (flippable.has(key))
|
|
149
|
+
continue;
|
|
150
|
+
if (!deepEqual(before[key], after[key])) {
|
|
151
|
+
throw new Error('plugin resources are read-only; only the enable state can be changed');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
118
154
|
}
|
|
119
155
|
async remove(id) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
156
|
+
// Plugin files belong to their suite checkout; install/uninstall manages them.
|
|
157
|
+
if (isPluginResourceId(id))
|
|
158
|
+
throw new Error('plugin resources are managed by their suite; uninstall the suite instead');
|
|
159
|
+
return this.users.remove(id);
|
|
123
160
|
}
|
|
124
161
|
}
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { readFile } from 'node:fs/promises';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
-
import { normalizeHookDocuments, standaloneHookDocument } from '
|
|
15
|
-
import { effectiveSurfaces } from '
|
|
14
|
+
import { normalizeHookDocuments, standaloneHookDocument } from '../../catalog/project-hooks.js';
|
|
15
|
+
import { effectiveSurfaces } from '../../model/types.js';
|
|
16
16
|
export const USER_HOOKS_SOURCE = '@user-hooks';
|
|
17
17
|
export const USER_HOOKS_SUITE = 'user-hooks';
|
|
18
18
|
/**
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
* to that directory. Skills also accept the cross-tool directory spelling
|
|
12
12
|
* (`<name>/SKILL.md`) that other Agent tools and the harness's own reader of
|
|
13
13
|
* `~/.agents/skills` author, and read it from the top level only.
|
|
14
|
-
* @module
|
|
14
|
+
* @module application/user-store
|
|
15
15
|
*/
|
|
16
16
|
import { readdir, readFile, rmdir, rm, stat } from 'node:fs/promises';
|
|
17
17
|
import { dirname, join } from 'node:path';
|
|
18
18
|
import { writeFileAtomic } from '@deepseek-ai/dsh-atomic-write';
|
|
19
19
|
import { parseDocument, stringify } from 'yaml';
|
|
20
|
-
import { parseSkillFrontmatter, stripFrontmatter } from '
|
|
20
|
+
import { parseSkillFrontmatter, stripFrontmatter } from '../../catalog/skills-parse.js';
|
|
21
21
|
/** Where user panel entries persist: `<agentsRoot>/<kind>/`. */
|
|
22
22
|
export function userEntryDir(agentsRoot, kind) {
|
|
23
23
|
return join(agentsRoot, kind);
|
package/lib/application/ports.js
CHANGED
|
@@ -10,6 +10,7 @@ export const defaultCatalogPorts = {
|
|
|
10
10
|
mcpServerOwner: () => undefined,
|
|
11
11
|
lspStatusSource: { diagnosticsSnapshot: () => new Map(), hasLiveMounts: () => false },
|
|
12
12
|
mcpBackend: async () => 'builtin',
|
|
13
|
+
localePreference: () => 'zh',
|
|
13
14
|
setMcpBackend: async () => {
|
|
14
15
|
throw new Error('the settings service is not mounted');
|
|
15
16
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { defaultMarkdownResources, resourceText } from '../catalog/component-files.js';
|
|
3
3
|
import { pluginRootOf } from '../catalog/plugin-variables.js';
|
|
4
4
|
import { suiteDataDir } from '../catalog/paths.js';
|
|
5
|
-
import { parseAgentRole } from '
|
|
5
|
+
import { parseAgentRole } from './agent-roles.js';
|
|
6
6
|
export async function projectAgentRoles(catalog, parent) {
|
|
7
7
|
const cwd = parent?.session?.header?.cwd;
|
|
8
8
|
if (typeof cwd !== 'string' || cwd === '')
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
1
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
2
|
+
import { readJsonFile, writeJsonDocument } from './json-file.js';
|
|
4
3
|
import { MCP_SCHEMA_ID, validateAgainstSchema, validateMcpJson } from '../catalog/validate.js';
|
|
5
4
|
import { parseLspServers } from '../catalog/lsp-spec.js';
|
|
6
5
|
import { qualifiedSuiteId } from '../catalog/paths.js';
|
|
7
|
-
import { redactMcpConfig } from './mcp-redaction.js';
|
|
6
|
+
import { redactMcpConfig } from './mcp/mcp-redaction.js';
|
|
8
7
|
/**
|
|
9
8
|
* The errors the closed-set package shape produces. A user-owned service does
|
|
10
9
|
* not obey that shape, and the per-transport checks still require what it needs.
|
|
@@ -85,24 +84,15 @@ export function lspConfig(spec) {
|
|
|
85
84
|
return Object.fromEntries(Object.entries(spec).filter(([key]) => key !== 'key'));
|
|
86
85
|
}
|
|
87
86
|
export async function loadLspOverrides(root) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return
|
|
91
|
-
|
|
92
|
-
catch (error) {
|
|
93
|
-
if (error.code === 'ENOENT')
|
|
94
|
-
return {};
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
87
|
+
const raw = await readJsonFile(join(root, 'lsp-overrides.json'));
|
|
88
|
+
if (typeof raw !== 'object' || raw === null)
|
|
89
|
+
return {};
|
|
90
|
+
return Object.fromEntries(Object.entries(raw).map(([id, config]) => [id, validateServerLsp(id.slice(id.lastIndexOf('/') + 1), config)]));
|
|
97
91
|
}
|
|
98
92
|
export async function saveLspOverride(root, id, config) {
|
|
99
93
|
const overrides = await loadLspOverrides(root);
|
|
100
94
|
overrides[id] = config;
|
|
101
|
-
|
|
102
|
-
await writeFileAtomic(path, JSON.stringify(Object.fromEntries(Object.entries(overrides).map(([key, value]) => [key, lspConfig(value)])), null, 2), {
|
|
103
|
-
mode: 0o600,
|
|
104
|
-
dirMode: 0o700
|
|
105
|
-
});
|
|
95
|
+
await writeJsonDocument(join(root, 'lsp-overrides.json'), Object.fromEntries(Object.entries(overrides).map(([key, value]) => [key, lspConfig(value)])));
|
|
106
96
|
}
|
|
107
97
|
export async function applyLspOverrides(root, suites) {
|
|
108
98
|
const overrides = await loadLspOverrides(root);
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { join, resolve } from 'node:path';
|
|
13
13
|
import { resolveProjectRoot, STATE_FILE_NAME } from '../catalog/paths.js';
|
|
14
14
|
import { discoverSourceListWithNotes } from '../catalog/source-catalog.js';
|
|
15
|
-
import { loadState } from '
|
|
15
|
+
import { loadState } from './state/state-store.js';
|
|
16
16
|
export const SCAN_CACHE_TTL_MS = 30_000;
|
|
17
17
|
const SCAN_CACHE_MAX_ENTRIES = 8;
|
|
18
18
|
export class SnapshotCache {
|
|
@@ -15,8 +15,7 @@ import { repoName } from '../catalog/manifests.js';
|
|
|
15
15
|
import { deriveSourceIdCandidates, expandHome, sanitizeId, sourceCheckoutDir, sourcesDir } from '../catalog/paths.js';
|
|
16
16
|
import { isDirectory, pathExists } from '../catalog/fs-probes.js';
|
|
17
17
|
import { canonicalGitUrl } from '../catalog/scan-resolvers.js';
|
|
18
|
-
import {
|
|
19
|
-
import { githubCloneUrl, resolveRegion } from '../runtime/regions.js';
|
|
18
|
+
import { githubCloneUrl, resolveRegion } from './regions.js';
|
|
20
19
|
import { resolveSourceKind } from '../model/types.js';
|
|
21
20
|
export class SourceStore {
|
|
22
21
|
context;
|
|
@@ -280,7 +279,7 @@ export class SourceStore {
|
|
|
280
279
|
// source.url) are re-pointed — a foreign remote is left untouched.
|
|
281
280
|
if (source.kind === 'git' || (source.kind === undefined && source.local !== true)) {
|
|
282
281
|
try {
|
|
283
|
-
const region = resolveRegion(await this.ports.downloadRegion(),
|
|
282
|
+
const region = resolveRegion(await this.ports.downloadRegion(), this.ports.localePreference());
|
|
284
283
|
const routed = githubCloneUrl(region, source.url);
|
|
285
284
|
const origin = await gitRemoteUrl(checkout);
|
|
286
285
|
if (origin !== routed && (origin === source.url || origin === githubCloneUrl('china', source.url))) {
|
|
@@ -401,7 +400,7 @@ export class SourceStore {
|
|
|
401
400
|
try {
|
|
402
401
|
// The region routes github.com clones through the China mirror prefix;
|
|
403
402
|
// the proxied URL becomes `origin`, so refreshes follow the same route.
|
|
404
|
-
const region = resolveRegion(await this.ports.downloadRegion(),
|
|
403
|
+
const region = resolveRegion(await this.ports.downloadRegion(), this.ports.localePreference());
|
|
405
404
|
await gitClone(githubCloneUrl(region, source.url), source.branch, checkout, this.context.git);
|
|
406
405
|
return undefined;
|
|
407
406
|
}
|
|
@@ -7,15 +7,13 @@
|
|
|
7
7
|
* like the market profile state this pattern follows. The record shapes live
|
|
8
8
|
* in `src/model/types.ts`; only the reading and writing live here.
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { SUITE_SURFACE_KEYS } from '../model/types.js';
|
|
10
|
+
import { readJsonFile, writeJsonDocument } from '../json-file.js';
|
|
11
|
+
import { SUITE_SURFACE_KEYS } from '../../model/types.js';
|
|
13
12
|
export const EMPTY_STATE = { version: 1, sources: [], installed: {} };
|
|
14
13
|
/** Parse persisted state; unreadable or wrong-version files yield a contained empty state. */
|
|
15
14
|
export async function loadState(statePath) {
|
|
16
15
|
try {
|
|
17
|
-
const
|
|
18
|
-
const parsed = JSON.parse(text);
|
|
16
|
+
const parsed = await readJsonFile(statePath);
|
|
19
17
|
if (typeof parsed !== 'object' || parsed === null)
|
|
20
18
|
return EMPTY_STATE;
|
|
21
19
|
const record = parsed;
|
|
@@ -87,7 +85,7 @@ function parseSurfaceOverrides(raw) {
|
|
|
87
85
|
}
|
|
88
86
|
return hasAny ? { surfaces: overrides } : {};
|
|
89
87
|
}
|
|
90
|
-
/** Persist state atomically through the
|
|
88
|
+
/** Persist state atomically through the shared JSON document writer. */
|
|
91
89
|
export async function saveState(statePath, state) {
|
|
92
|
-
await
|
|
90
|
+
await writeJsonDocument(statePath, state);
|
|
93
91
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { constants } from 'node:fs';
|
|
3
3
|
import { copyFile, lstat, mkdir, readdir, readFile, rmdir, unlink } from 'node:fs/promises';
|
|
4
4
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
|
-
import {
|
|
6
|
-
import { expandHome, isWithin, resolveAgentsRoot, resolveDataRoot, resolveDshHome, resolveUserRoot } from '
|
|
5
|
+
import { readJsonFile, writeJsonDocument } from '../json-file.js';
|
|
6
|
+
import { expandHome, isWithin, resolveAgentsRoot, resolveDataRoot, resolveDshHome, resolveUserRoot } from '../../catalog/paths.js';
|
|
7
7
|
async function info(path) {
|
|
8
8
|
try {
|
|
9
9
|
return await lstat(path);
|
|
@@ -156,7 +156,7 @@ async function relocateManagedSourceUrls(userRoot, legacyUserRoot) {
|
|
|
156
156
|
const path = join(userRoot, 'state.json');
|
|
157
157
|
if ((await info(path)) === undefined)
|
|
158
158
|
return;
|
|
159
|
-
const state =
|
|
159
|
+
const state = await readJsonFile(path);
|
|
160
160
|
if (typeof state !== 'object' || state === null || !('sources' in state) || !Array.isArray(state.sources))
|
|
161
161
|
return;
|
|
162
162
|
let changed = false;
|
|
@@ -170,7 +170,7 @@ async function relocateManagedSourceUrls(userRoot, legacyUserRoot) {
|
|
|
170
170
|
changed = true;
|
|
171
171
|
}
|
|
172
172
|
if (changed) {
|
|
173
|
-
await
|
|
173
|
+
await writeJsonDocument(path, state);
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
async function collectUnsupportedEntries(path, conflicts) {
|
|
@@ -13,10 +13,6 @@ import { expandHome, sourcesDir } from './paths.js';
|
|
|
13
13
|
import { isDirectory } from './fs-probes.js';
|
|
14
14
|
import { scanSource as scanSourceWithNotes } from './suite-scanner.js';
|
|
15
15
|
import { discoverNativeProjectSuites } from './native-project.js';
|
|
16
|
-
/** Discover suites from the selected configured or project checkouts. */
|
|
17
|
-
export async function discoverSourceList(sources, dimension, dimensionRoot) {
|
|
18
|
-
return (await discoverSourceListWithNotes(sources, dimension, dimensionRoot, true)).suites;
|
|
19
|
-
}
|
|
20
16
|
/**
|
|
21
17
|
* Discover suites plus per-source scan diagnostics for one dimension.
|
|
22
18
|
* @param scanProjectLayouts - whether the project's own native Agent directories
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/contracts/market.js
CHANGED
|
@@ -6,7 +6,6 @@ export const MARKET_ROUTES = {
|
|
|
6
6
|
overview: `${MARKET_API_PREFIX}overview`,
|
|
7
7
|
mcpStatus: `${MARKET_API_PREFIX}mcp-status`,
|
|
8
8
|
addMcpServer: `${MARKET_API_PREFIX}mcp-servers/add`,
|
|
9
|
-
importMcpServers: `${MARKET_API_PREFIX}mcp-servers/import`,
|
|
10
9
|
lspStatus: `${MARKET_API_PREFIX}lsp-status`,
|
|
11
10
|
lspServers: `${MARKET_API_PREFIX}lsp-servers`,
|
|
12
11
|
addLspServer: `${MARKET_API_PREFIX}lsp-servers/add`,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/index.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import z from '@deepseek-ai/schemastery';
|
|
2
|
-
import { MarketSettingsFields } from './
|
|
2
|
+
import { MarketSettingsFields } from './application/mcp/mcp-backend.js';
|
|
3
3
|
import { Catalog } from './application/catalog.js';
|
|
4
|
-
import { settlesWithin } from './
|
|
5
|
-
import { RuntimeReconciler } from './runtime/reconciler.js';
|
|
6
|
-
import { ReconcileScheduler } from './runtime/reconcile-scheduler.js';
|
|
7
|
-
import { MarketSettingsNamespace } from './runtime/settings-namespace.js';
|
|
8
|
-
import { deleteMcpAuthGrant } from './runtime/mcp-auth-record.js';
|
|
9
|
-
import { inspectToolRegistry, toolsServiceOf } from './runtime/tool-registry-observer.js';
|
|
10
|
-
import { migratePluginStorage } from './
|
|
11
|
-
import { mountAgentRoleTool } from './runtime/agent-role-router.js';
|
|
4
|
+
import { settlesWithin } from './application/deadline.js';
|
|
5
|
+
import { RuntimeReconciler } from './runtime/core/reconciler.js';
|
|
6
|
+
import { ReconcileScheduler } from './runtime/core/reconcile-scheduler.js';
|
|
7
|
+
import { MarketSettingsNamespace } from './runtime/host/settings-namespace.js';
|
|
8
|
+
import { deleteMcpAuthGrant } from './runtime/mcp/mcp-auth-record.js';
|
|
9
|
+
import { inspectToolRegistry, toolsServiceOf } from './runtime/host/tool-registry-observer.js';
|
|
10
|
+
import { migratePluginStorage } from './application/state/storage-migration.js';
|
|
11
|
+
import { mountAgentRoleTool } from './runtime/agents/agent-role-router.js';
|
|
12
12
|
import { projectAgentRoles } from './application/project-agent-roles.js';
|
|
13
|
-
import { mountProjectCommands, mountProjectMcp, mountProjectHooks, mountSuiteInstructions } from './runtime/project-runtime.js';
|
|
13
|
+
import { mountProjectCommands, mountProjectMcp, mountProjectHooks, mountSuiteInstructions } from './runtime/surfaces/project-runtime.js';
|
|
14
14
|
import { createPanelResources } from './application/panel-resources.js';
|
|
15
15
|
import { resolveAgentsRoot, resolveDataRoot, resolveUserRoot } from './catalog/paths.js';
|
|
16
16
|
import { mountSuiteRoutes } from './routes.js';
|
|
17
|
-
import { SuiteSkillProvider } from './runtime/skills-provider.js';
|
|
18
|
-
import { shellSeamOf } from './runtime/dynamic-context.js';
|
|
19
|
-
import { loadLspServers } from './
|
|
20
|
-
import { loadDisabledLspServers } from './
|
|
21
|
-
import { bindHostLocale,
|
|
22
|
-
import { createUserPanelStores } from './runtime/user-panels.js';
|
|
23
|
-
import { SourceAutoUpdater } from './runtime/source-auto-update.js';
|
|
24
|
-
import { UserPanelSkillProvider } from './runtime/user-panels.js';
|
|
25
|
-
import { UserCommandMountRegistry } from './runtime/user-commands.js';
|
|
17
|
+
import { SuiteSkillProvider } from './runtime/surfaces/skills-provider.js';
|
|
18
|
+
import { shellSeamOf } from './runtime/surfaces/dynamic-context.js';
|
|
19
|
+
import { loadLspServers } from './application/lsp/lsp-direct-config.js';
|
|
20
|
+
import { loadDisabledLspServers } from './application/lsp/lsp-server-state.js';
|
|
21
|
+
import { bindHostLocale, LOCALE_SETTINGS_ENTRY, readHostLocalePreference, readLocalePreference, setHostLocaleSource } from './runtime/host/host-locale.js';
|
|
22
|
+
import { createUserPanelStores } from './runtime/panels/user-panels.js';
|
|
23
|
+
import { SourceAutoUpdater } from './runtime/core/source-auto-update.js';
|
|
24
|
+
import { UserPanelSkillProvider } from './runtime/panels/user-panels.js';
|
|
25
|
+
import { UserCommandMountRegistry } from './runtime/panels/user-commands.js';
|
|
26
26
|
import { presetSourceRef } from './model/preset-source.js';
|
|
27
27
|
export const name = 'dsh-agent-plugins-market';
|
|
28
28
|
export const inject = ['skills', 'commands'];
|
|
@@ -54,24 +54,32 @@ export async function apply(ctx, config = { mcpEnhanced: undefinedRef, scanProje
|
|
|
54
54
|
throw new Error(`Plugin storage migration conflicts (original files retained): ${migration.conflicts.join(', ')}`);
|
|
55
55
|
let providerControl;
|
|
56
56
|
let userPanelControl;
|
|
57
|
-
// Host runtime copy resolves from the harness `locale.preference` setting
|
|
58
|
-
// the async settings read lands before the first session starts in practice.
|
|
57
|
+
// Host runtime copy resolves from the harness `locale.preference` setting.
|
|
59
58
|
const hostLocale = { t: bindHostLocale(undefined) };
|
|
60
59
|
const refreshHostLocale = () => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
userPanelControl?.invalidate();
|
|
65
|
-
});
|
|
60
|
+
hostLocale.t = bindHostLocale(readLocalePreference());
|
|
61
|
+
providerControl?.invalidate();
|
|
62
|
+
userPanelControl?.invalidate();
|
|
66
63
|
};
|
|
67
|
-
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
// The preference is the `locale` entry's live configuration, projected by the
|
|
65
|
+
// host settings service. Resolve that service through the store from this
|
|
66
|
+
// entry's own context, and register the wiring as an effect so it lives
|
|
67
|
+
// exactly as long as this fiber: a context captured from an injection
|
|
68
|
+
// callback throws `cannot get required service "settings" in inactive
|
|
69
|
+
// context` once a profile reload disposes that fiber, and the unhandled
|
|
70
|
+
// rejection ends the host. Re-read once the service lands, because it may
|
|
71
|
+
// mount after this plugin.
|
|
72
|
+
ctx.effect(() => setHostLocaleSource(() => readHostLocalePreference(ctx.get('settings'))), 'dsh-agent-plugins-market: host locale source');
|
|
73
|
+
refreshHostLocale();
|
|
74
|
+
ctx.inject(['settings'], () => {
|
|
73
75
|
refreshHostLocale();
|
|
74
76
|
});
|
|
77
|
+
// A language switch writes the `locale` entry's form; re-read so the copy the
|
|
78
|
+
// market renders follows it without a reload.
|
|
79
|
+
ctx.effect(() => ctx.on('settings/document-updated', (ns) => {
|
|
80
|
+
if (ns === LOCALE_SETTINGS_ENTRY)
|
|
81
|
+
refreshHostLocale();
|
|
82
|
+
}), 'dsh-agent-plugins-market: host locale refresh');
|
|
75
83
|
const runtime = new RuntimeReconciler(ctx, dataRoot, key => hostLocale.t(key));
|
|
76
84
|
// User panel stores (skills / commands / agent personas) and their runtime
|
|
77
85
|
// contributions: one extra skill provider plus one command mount registry.
|
|
@@ -162,7 +170,8 @@ export async function apply(ctx, config = { mcpEnhanced: undefinedRef, scanProje
|
|
|
162
170
|
lspStatusSource: runtime.lsp,
|
|
163
171
|
mcpBackend: () => settings.backend(),
|
|
164
172
|
setMcpBackend: backend => settings.setBackend(backend),
|
|
165
|
-
downloadRegion: () => settings.downloadRegion()
|
|
173
|
+
downloadRegion: () => settings.downloadRegion(),
|
|
174
|
+
localePreference: () => readLocalePreference() ?? 'zh'
|
|
166
175
|
};
|
|
167
176
|
const catalog = new Catalog({ userRoot, dataRoot, agentsRoot, onChanged, ports, ...(config.git === undefined ? {} : { git: config.git }) });
|
|
168
177
|
await catalog.load();
|
package/lib/routes.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
import { isAbsolute } from 'node:path';
|
|
10
10
|
import { MARKET_ROUTES, userPanelRoute } from './contracts/market.js';
|
|
11
11
|
import { expandHome } from './catalog/paths.js';
|
|
12
|
-
import { sanitizeOverridePatch } from './
|
|
13
|
-
import { readModelCatalog } from './runtime/model-catalog.js';
|
|
12
|
+
import { sanitizeOverridePatch } from './application/mcp/mcp-overrides.js';
|
|
13
|
+
import { readModelCatalog } from './runtime/host/model-catalog.js';
|
|
14
14
|
const MAX_BODY_BYTES = 64 * 1024;
|
|
15
15
|
/** Mount every route; returns the disposer releasing them all. */
|
|
16
16
|
export function mountSuiteRoutes(hostCtx, manager, panels) {
|
|
@@ -93,13 +93,6 @@ export function mountSuiteRoutes(hostCtx, manager, panels) {
|
|
|
93
93
|
await manager.addMcpServer(body.name, body.config);
|
|
94
94
|
return {};
|
|
95
95
|
});
|
|
96
|
-
// Paste import: many services in one write. Each entry reports its own
|
|
97
|
-
// outcome, so one rejected definition never fails the whole paste.
|
|
98
|
-
post(MARKET_ROUTES.importMcpServers, async (body) => {
|
|
99
|
-
const overwrite = body['overwrite'] === true;
|
|
100
|
-
const result = await manager.importMcpServers(body['servers'], overwrite);
|
|
101
|
-
return { imported: result.imported, skipped: result.skipped };
|
|
102
|
-
});
|
|
103
96
|
get(MARKET_ROUTES.lspStatus, async (_request, response) => {
|
|
104
97
|
sendJson(response, 200, await manager.lspStatus());
|
|
105
98
|
});
|
|
@@ -1,62 +1,17 @@
|
|
|
1
1
|
/** Execute user and installed-suite agent cards through the host subagent service. */
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
4
|
-
import { parse as parseYaml } from 'yaml';
|
|
5
4
|
import { mountSubagentCatalog } from './subagent-catalog.js';
|
|
6
5
|
import { namedAgentRoles } from './agent-role-names.js';
|
|
7
|
-
import { expandPluginPaths } from '
|
|
6
|
+
import { expandPluginPaths } from '../../catalog/plugin-variables.js';
|
|
7
|
+
import { parseAgentRole } from '../../application/agent-roles.js';
|
|
8
|
+
export { parseAgentRole };
|
|
8
9
|
export const AGENT_ROLE_TOOL_NAME = 'subagent_role';
|
|
9
10
|
/**
|
|
10
11
|
* Absolute delegation-depth cap applied to every role child. The host default is
|
|
11
12
|
* the same value; keeping it explicit preserves the shipped recursion budget.
|
|
12
13
|
*/
|
|
13
14
|
const MAX_AGENT_ROLE_DEPTH = 3;
|
|
14
|
-
function optionalText(value, key) {
|
|
15
|
-
if (value === undefined)
|
|
16
|
-
return undefined;
|
|
17
|
-
if (typeof value !== 'string' || value.trim() === '')
|
|
18
|
-
throw new Error(`agent metadata ${key} must be a non-empty string`);
|
|
19
|
-
return value.trim();
|
|
20
|
-
}
|
|
21
|
-
/** Strict YAML parsing prevents malformed routing metadata from silently inheriting wider privileges. */
|
|
22
|
-
export function parseAgentRole(text) {
|
|
23
|
-
const match = /^(?:\uFEFF)?---\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(text);
|
|
24
|
-
if (match === null) {
|
|
25
|
-
if (/^(?:\uFEFF)?---(?:\r?\n|$)/.test(text))
|
|
26
|
-
throw new Error('agent frontmatter is not closed');
|
|
27
|
-
return { content: text, disabled: false };
|
|
28
|
-
}
|
|
29
|
-
// A matched block always carries its body; the check keeps the type honest.
|
|
30
|
-
const frontmatter = match[1];
|
|
31
|
-
if (frontmatter === undefined)
|
|
32
|
-
throw new Error('agent frontmatter is not closed');
|
|
33
|
-
const parsed = parseYaml(frontmatter);
|
|
34
|
-
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
35
|
-
throw new Error('agent frontmatter must be a YAML object');
|
|
36
|
-
const metadata = parsed;
|
|
37
|
-
if (metadata.disabled !== undefined && typeof metadata.disabled !== 'boolean')
|
|
38
|
-
throw new Error('agent metadata disabled must be a boolean');
|
|
39
|
-
const model = optionalText(metadata.model, 'model');
|
|
40
|
-
const provider = optionalText(metadata.provider, 'provider');
|
|
41
|
-
const effort = optionalText(metadata.reasoning_effort, 'reasoning_effort');
|
|
42
|
-
const camelEffort = optionalText(metadata.reasoningEffort, 'reasoningEffort');
|
|
43
|
-
if (effort !== undefined && camelEffort !== undefined && effort !== camelEffort)
|
|
44
|
-
throw new Error('agent metadata reasoning_effort and reasoningEffort conflict');
|
|
45
|
-
const reasoningEffort = effort ?? camelEffort;
|
|
46
|
-
const title = optionalText(metadata.name, 'name');
|
|
47
|
-
const description = optionalText(metadata.description, 'description');
|
|
48
|
-
if (model === 'inherit' && provider !== undefined)
|
|
49
|
-
throw new Error('agent model inherit cannot specify a provider');
|
|
50
|
-
return {
|
|
51
|
-
content: text.slice(match[0].length),
|
|
52
|
-
disabled: metadata.disabled === true,
|
|
53
|
-
...(model === undefined || model === 'inherit' ? {} : { model }),
|
|
54
|
-
...(provider === undefined ? {} : { provider }),
|
|
55
|
-
...(reasoningEffort === undefined ? {} : { reasoningEffort }),
|
|
56
|
-
...(title === undefined ? {} : { title }),
|
|
57
|
-
...(description === undefined ? {} : { description })
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
15
|
function parentRouteOf(parent) {
|
|
61
16
|
const agent = parent;
|
|
62
17
|
return agent?.session?.requestHeader?.()?.config ?? agent?.options ?? {};
|
|
@@ -287,11 +242,10 @@ export function mountAgentRoleTool(ctx, listRoles) {
|
|
|
287
242
|
const host = ctx;
|
|
288
243
|
const tool = defineTool({
|
|
289
244
|
name: AGENT_ROLE_TOOL_NAME,
|
|
290
|
-
description: "Delegate a task to a role child
|
|
291
|
-
'the background by default
|
|
292
|
-
'
|
|
293
|
-
|
|
294
|
-
"assertions against the files and relay the result to the user; the child's output is not visible to them. The current catalog lists the available roles and states the usage guidance; when no listed " +
|
|
245
|
+
description: "Delegate a task to a named role child from the session's role catalog: the role's own instructions and its configured model apply, and the child starts without this conversation. Use it proactively for self-contained work — codebase exploration whose raw reads would fill this context, a scoped implementation, a review, or an analysis you can brief in one prompt — and start independent children together in one message. The child runs in " +
|
|
246
|
+
'the background by default and returns a durable subagent id at once, keeping the child available for later turns; when the run settles, the runtime sends the parent a notice carrying its outcome and ' +
|
|
247
|
+
'final reply. A child usually runs for minutes, so continue with independent work instead of waiting. `prompt` must stand alone, since the child cannot see this conversation and a question it asks while it runs goes unanswered. When the notice arrives, verify its ' +
|
|
248
|
+
"assertions against the files and relay the result to the user; the child's output is not visible to them. The current catalog lists the available roles and the full usage guidance; when no listed " +
|
|
295
249
|
"role matches, use the host's own `subagent` tools instead.",
|
|
296
250
|
parameters: {
|
|
297
251
|
agent: { type: 'string', required: true, description: 'Exact callable role name from the current subagent catalog.' },
|
|
@@ -61,26 +61,34 @@ const CATALOG_UPDATED = 'The available subagent roles changed. This complete lis
|
|
|
61
61
|
const CATALOG_PROMPT = 'Writing the prompt: brief the child like a colleague who just walked into the room — it has not seen this conversation, does not know what you tried, and does not know why the task matters. Explain what you are trying to accomplish, describe what you learned and ruled out, and give enough context for the child to make judgment calls. The child cannot see this conversation, so prompt must be self-contained: the task itself, the relevant files and known findings, the output you expect, and the boundaries of the task (research only, or may edit files). Terse command-style prompts produce shallow, generic work.';
|
|
62
62
|
const CATALOG_USAGE = [
|
|
63
63
|
'Usage notes:',
|
|
64
|
-
|
|
64
|
+
"- Call subagent_role with the exact catalog name as agent. A child has its own context: it starts without this conversation, so it suits self-contained work that one briefing can state in full. If no listed role matches, do not substitute a similarly named one — delegate through one of the host's general delegation channels instead, and do not load these roles through skill or slash commands.",
|
|
65
65
|
'- By default the call returns a durable subagent id immediately and runs in the background without blocking you. A child usually runs for minutes: spend that time advancing independent work that does not depend on it, rather than idling — and do not poll it or re-check its progress. While it runs, send_message adds an instruction or more material and list_agents reports its status.',
|
|
66
|
-
|
|
67
|
-
"- run_in_background false runs one foreground child and returns its report as this call's result instead of a settlement notice. Use it only when your next action depends on the result.",
|
|
66
|
+
"- run_in_background true runs the same child as a tracked background job and returns a job id instead: collect it with job_output and stop it with job_kill. run_in_background false runs one foreground child and returns its report as this call's result — use it when your next action depends on the result and no independent work remains.",
|
|
68
67
|
'- A question a child asks while it runs goes unanswered: nothing will reply on your behalf. So state the prompt in full the first time, and a child should decide for itself, keep going, and list in its final reply which choices it made alone and what information it still lacked.',
|
|
69
|
-
"- A background child's output is not visible to the user
|
|
70
|
-
"- The child's settlement notice arrives as a user message, not as a tool result. Until it arrives, do not assume or predict its findings, and do not deliver anything that depends on them; act on them once it lands, by verifying or delegating further as needed.",
|
|
68
|
+
"- The settlement notice arrives as a user message, not as a tool result. Until it arrives, do not assume or predict its findings, and do not deliver anything that depends on them. A background child's output is not visible to the user: when it finishes, summarize the result to the user yourself.",
|
|
71
69
|
"- The child's final reply is its own report: check its assertions against the files themselves, and treat its statements about its own configuration the same way — a child cannot see how its role instructions were installed.",
|
|
72
|
-
|
|
73
|
-
'- Do not duplicate work a child is already doing.',
|
|
74
|
-
'- When several children run at once, give each its own git worktree and name the files it owns in prompt.'
|
|
70
|
+
'- Do not duplicate work a child is already doing. When several children run at once, give each its own git worktree and name the files it owns in prompt.'
|
|
75
71
|
].join('\n');
|
|
76
|
-
const CATALOG_WHEN_NOT_TO_USE = "When NOT to use a role child: no listed role matches; your next action needs the result; or another child is already doing this work. When no role matches, do not substitute a similarly named one — delegate through one of the host's general delegation channels instead. Do not load these roles through skill or slash commands.";
|
|
77
72
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
* The positive half of the delegation decision, published with every catalog
|
|
74
|
+
* ahead of the role list so the model reads the triggers before the names.
|
|
75
|
+
* Delegation had only negative framing before this block: every other paragraph
|
|
76
|
+
* states what must not happen, and none states when delegating is the default.
|
|
77
|
+
*/
|
|
78
|
+
const CATALOG_WHEN_TO_DELEGATE = [
|
|
79
|
+
'Delegate proactively: hand self-contained work to a role child by default instead of doing it inline. Delegate when any of these holds:',
|
|
80
|
+
'- The task would burn many tool calls or file reads whose raw output would flood this conversation — codebase exploration, tracing behavior across files, digesting long logs or reports. The child reads everything and returns only the distilled result.',
|
|
81
|
+
'- The task is a complete unit one briefing can state: a scoped implementation, a review, an analysis, a document.',
|
|
82
|
+
'- Two or more such tasks are independent: start all the children in one message and let them run in parallel.',
|
|
83
|
+
'Decide by the briefing test: if you can state the task in full and it needs no further input from the user, delegate it and keep working.'
|
|
84
|
+
].join('\n');
|
|
85
|
+
/**
|
|
86
|
+
* Model-facing catalog text: the delegation triggers, then the role list, then
|
|
87
|
+
* the same guidance on first publication and on every update, so a replacement
|
|
88
|
+
* never arrives without the rules that govern it. The guidance covers when a
|
|
89
|
+
* role child is the default, how to brief one, and how to work with one while
|
|
90
|
+
* it runs; the per-channel arbitration between the host's delegation tools
|
|
91
|
+
* stays in the tool descriptions the harness owns.
|
|
84
92
|
*
|
|
85
93
|
* Role lines carry a name and a description only. The configured route stays in
|
|
86
94
|
* the durable entries for execution and change detection; publishing it would
|
|
@@ -91,12 +99,12 @@ export function renderCatalogText(entries, update) {
|
|
|
91
99
|
return [
|
|
92
100
|
'<system-reminder>',
|
|
93
101
|
update ? CATALOG_UPDATED : CATALOG_INTRO,
|
|
102
|
+
CATALOG_WHEN_TO_DELEGATE,
|
|
94
103
|
'<available_subagents>',
|
|
95
104
|
...lines,
|
|
96
105
|
'</available_subagents>',
|
|
97
106
|
CATALOG_PROMPT,
|
|
98
107
|
CATALOG_USAGE,
|
|
99
|
-
CATALOG_WHEN_NOT_TO_USE,
|
|
100
108
|
'</system-reminder>'
|
|
101
109
|
].join('\n');
|
|
102
110
|
}
|