dsh-agy-cli 0.12.1 → 0.12.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/lib/index.js +33 -2
- package/lib/provider/tool-protocol.js +19 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -20,6 +20,12 @@ export const name = 'dsh-agy-cli';
|
|
|
20
20
|
* and the host does not provide one.
|
|
21
21
|
*/
|
|
22
22
|
export const inject = ['llm', 'sessions', 'sandboxPolicy', 'permissionPresets', 'approval'];
|
|
23
|
+
/**
|
|
24
|
+
* How long the MCP bridge waits for `webServer` and `tools` to mount before it
|
|
25
|
+
* gives up and reports the carrier as unavailable. Host startup binds the HTTP
|
|
26
|
+
* carrier within a few seconds; the ceiling only bounds a broken composition.
|
|
27
|
+
*/
|
|
28
|
+
const BRIDGE_SERVICE_WAIT_MS = 20_000;
|
|
23
29
|
/** Programmatic library default: enabled=false, toolPolicy=reject. */
|
|
24
30
|
export const Config = ConfigSchema;
|
|
25
31
|
export { AgyAdapter, MockAdapter };
|
|
@@ -188,12 +194,37 @@ export function apply(ctx, config) {
|
|
|
188
194
|
const wantsBridge = activeConfig.mcpBridge !== false
|
|
189
195
|
&& activeConfig.toolPolicy === 'dsh-owned';
|
|
190
196
|
if (wantsBridge) {
|
|
197
|
+
/**
|
|
198
|
+
* Both carrier services mount after this plugin under the shipped desktop
|
|
199
|
+
* composition, so a `ctx.get()` sampled while the plugin applies can read
|
|
200
|
+
* `undefined` and latch "host webServer service is unavailable" for the
|
|
201
|
+
* whole host lifetime. Wait for them instead: a late bridge retries what a
|
|
202
|
+
* latch can never undo. `sessionController` already resolves late for the
|
|
203
|
+
* same reason; only the two carriers were sampled eagerly.
|
|
204
|
+
*/
|
|
205
|
+
const waitForService = async (lookup, timeoutMs) => {
|
|
206
|
+
const deadline = Date.now() + timeoutMs;
|
|
207
|
+
for (;;) {
|
|
208
|
+
const value = lookup();
|
|
209
|
+
if (value !== undefined)
|
|
210
|
+
return value;
|
|
211
|
+
if (Date.now() >= deadline)
|
|
212
|
+
return undefined;
|
|
213
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
214
|
+
}
|
|
215
|
+
};
|
|
191
216
|
ctx.effect(() => {
|
|
192
217
|
let cancelled = false;
|
|
193
218
|
void (async () => {
|
|
219
|
+
const [webServer, tools] = await Promise.all([
|
|
220
|
+
waitForService(() => ctx.get('webServer'), BRIDGE_SERVICE_WAIT_MS),
|
|
221
|
+
waitForService(() => ctx.get('tools'), BRIDGE_SERVICE_WAIT_MS),
|
|
222
|
+
]);
|
|
223
|
+
if (cancelled)
|
|
224
|
+
return;
|
|
194
225
|
const bridge = await startDshMcpBridge({
|
|
195
|
-
webServer:
|
|
196
|
-
tools:
|
|
226
|
+
webServer: webServer,
|
|
227
|
+
tools: tools,
|
|
197
228
|
// Late-bound on purpose: this plugin loads before the Session
|
|
198
229
|
// controller bundle finishes mounting, and a value captured here
|
|
199
230
|
// stays undefined for the life of the host.
|
|
@@ -78,6 +78,23 @@ const SUPPORTED_SCHEMA_KEYS = new Set([
|
|
|
78
78
|
'type',
|
|
79
79
|
'uniqueItems',
|
|
80
80
|
]);
|
|
81
|
+
/**
|
|
82
|
+
* JSON Schema keywords that carry no validation semantics. A dialect
|
|
83
|
+
* declaration such as `$schema` is a compatibility detail of whoever published
|
|
84
|
+
* the schema (the Context7 MCP server emits draft 2020-12 on every tool), so
|
|
85
|
+
* dropping these keeps the fail-closed policy for real keywords — anything the
|
|
86
|
+
* validator would act on still has to be supported — while a well-formed tool
|
|
87
|
+
* catalogue no longer fails the whole request over metadata.
|
|
88
|
+
*/
|
|
89
|
+
const IGNORED_METADATA_KEYS = new Set([
|
|
90
|
+
'$comment',
|
|
91
|
+
'$id',
|
|
92
|
+
'$schema',
|
|
93
|
+
'deprecated',
|
|
94
|
+
'examples',
|
|
95
|
+
'readOnly',
|
|
96
|
+
'writeOnly',
|
|
97
|
+
]);
|
|
81
98
|
function isRecord(value) {
|
|
82
99
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
83
100
|
}
|
|
@@ -176,6 +193,8 @@ function sanitizeSchemaNode(value, depth, path) {
|
|
|
176
193
|
if (!isSafeObjectKey(key)) {
|
|
177
194
|
throw new ToolProtocolError(TOOL_PROTOCOL_SCHEMA_INVALID_CODE, 'unsafe object key');
|
|
178
195
|
}
|
|
196
|
+
if (IGNORED_METADATA_KEYS.has(key))
|
|
197
|
+
continue;
|
|
179
198
|
if (!SUPPORTED_SCHEMA_KEYS.has(key)) {
|
|
180
199
|
throw new ToolProtocolError(TOOL_PROTOCOL_SCHEMA_INVALID_CODE, `unsupported keyword ${key}`);
|
|
181
200
|
}
|