context-mode 1.0.143 → 1.0.145
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +1 -11
- package/build/adapters/base.d.ts +34 -0
- package/build/adapters/base.js +50 -2
- package/build/adapters/claude-code/index.js +9 -1
- package/build/adapters/codex/index.js +16 -2
- package/build/adapters/omp/plugin.js +34 -8
- package/build/adapters/openclaw/plugin.js +10 -1
- package/build/adapters/opencode/index.js +9 -2
- package/build/adapters/pi/extension.js +36 -8
- package/build/adapters/pi/mcp-bridge.js +94 -14
- package/build/adapters/vscode-copilot/index.js +11 -0
- package/build/server.d.ts +35 -0
- package/build/server.js +49 -1
- package/build/store.js +17 -5
- package/cli.bundle.mjs +138 -138
- package/hooks/pretooluse.mjs +9 -1
- package/hooks/sessionstart.mjs +8 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +101 -101
|
@@ -13,6 +13,7 @@ import { readFileSync, mkdirSync, accessSync, existsSync, constants, } from "nod
|
|
|
13
13
|
import { resolve, join } from "node:path";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { CopilotBaseAdapter } from "../copilot-base.js";
|
|
16
|
+
import { resolveContextModeDataRoot } from "../base.js";
|
|
16
17
|
// ─────────────────────────────────────────────────────────
|
|
17
18
|
// Hook constants (re-exported from hooks.ts)
|
|
18
19
|
// ─────────────────────────────────────────────────────────
|
|
@@ -45,6 +46,16 @@ export class VSCodeCopilotAdapter extends CopilotBaseAdapter {
|
|
|
45
46
|
return process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
46
47
|
}
|
|
47
48
|
getSessionDir() {
|
|
49
|
+
// Issue #649: CONTEXT_MODE_DATA_DIR wins over both the .github project
|
|
50
|
+
// dir and the ~/.vscode fallback so dev-container/CI users can pin
|
|
51
|
+
// storage to a writable volume regardless of whether a .github tree
|
|
52
|
+
// happens to exist in cwd.
|
|
53
|
+
const override = resolveContextModeDataRoot();
|
|
54
|
+
if (override) {
|
|
55
|
+
const overrideDir = join(override, "context-mode", "sessions");
|
|
56
|
+
mkdirSync(overrideDir, { recursive: true });
|
|
57
|
+
return overrideDir;
|
|
58
|
+
}
|
|
48
59
|
// Prefer .github/context-mode/sessions/ if .github exists,
|
|
49
60
|
// otherwise fall back to ~/.vscode/context-mode/sessions/
|
|
50
61
|
const githubDir = resolve(".github", "context-mode", "sessions");
|
package/build/server.d.ts
CHANGED
|
@@ -21,6 +21,41 @@ export declare function emitSuppressionDiagnostic(opts?: {
|
|
|
21
21
|
}): void;
|
|
22
22
|
/** Test-only: reset the one-shot emission flag so suites can re-exercise. */
|
|
23
23
|
export declare function __resetSuppressionDiagnosticForTests(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Issue #637 — register an explicit empty `tools/list` handler on the McpServer.
|
|
26
|
+
*
|
|
27
|
+
* Background: when `suppressMcpToolsForNativePluginHost` is true, every
|
|
28
|
+
* `server.registerTool()` call is short-circuited (returns `undefined` above).
|
|
29
|
+
* The MCP SDK only installs the SDK-default `tools/list` handler when at least
|
|
30
|
+
* one `registerTool()` reaches `setToolRequestHandlers()` internally
|
|
31
|
+
* (mcp.js:56-67). Suppressing every registration leaves `tools/list`
|
|
32
|
+
* unregistered, and the framework's RPC layer answers it with
|
|
33
|
+
* `-32601 "Method not found"`.
|
|
34
|
+
*
|
|
35
|
+
* The reporter of #637 (SquirrelRat) inspected the suppressed child via
|
|
36
|
+
* `tools/list` and read the JSON-RPC error as "the plugin never registers any
|
|
37
|
+
* ctx_* tools" — when in fact the plugin DOES register all 11 tools natively
|
|
38
|
+
* (verified at `src/adapters/opencode/plugin.ts:469` and
|
|
39
|
+
* `tests/opencode-plugin.test.ts:88`). The misleading -32601 is the seed of
|
|
40
|
+
* the #637 perception.
|
|
41
|
+
*
|
|
42
|
+
* This helper installs an explicit handler that returns `{tools: []}` — a
|
|
43
|
+
* spec-compliant empty list. Paired with the existing #623 stderr diagnostic,
|
|
44
|
+
* an operator now sees:
|
|
45
|
+
* - wire response: `{tools: []}` (matches expectation, no JSON-RPC error)
|
|
46
|
+
* - stderr: `[context-mode] ctx_* tools/list intentionally empty… (#623)`
|
|
47
|
+
*
|
|
48
|
+
* Idempotent: throws inside SDK if called twice on the same server because
|
|
49
|
+
* `assertCanSetRequestHandler` (mcp.js:60) rejects duplicate registrations;
|
|
50
|
+
* we therefore install the SDK's default tool handlers FIRST (via a no-op
|
|
51
|
+
* registerTool of a fake tool, immediately removed) only if needed. To keep
|
|
52
|
+
* the public surface minimal, we just call `server.server.setRequestHandler`
|
|
53
|
+
* directly — that is the same low-level call used for prompts/resources at
|
|
54
|
+
* server.ts:259-261 and avoids the SDK guard entirely.
|
|
55
|
+
*
|
|
56
|
+
* Exported for test (#637 in-memory regression guard).
|
|
57
|
+
*/
|
|
58
|
+
export declare function registerEmptyToolsListHandler(target?: McpServer): void;
|
|
24
59
|
type ToolContextOverride = {
|
|
25
60
|
projectDir: string;
|
|
26
61
|
sessionId?: string;
|
package/build/server.js
CHANGED
|
@@ -186,6 +186,44 @@ export function emitSuppressionDiagnostic(opts = {}) {
|
|
|
186
186
|
export function __resetSuppressionDiagnosticForTests() {
|
|
187
187
|
__suppressionDiagnosticEmitted = false;
|
|
188
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Issue #637 — register an explicit empty `tools/list` handler on the McpServer.
|
|
191
|
+
*
|
|
192
|
+
* Background: when `suppressMcpToolsForNativePluginHost` is true, every
|
|
193
|
+
* `server.registerTool()` call is short-circuited (returns `undefined` above).
|
|
194
|
+
* The MCP SDK only installs the SDK-default `tools/list` handler when at least
|
|
195
|
+
* one `registerTool()` reaches `setToolRequestHandlers()` internally
|
|
196
|
+
* (mcp.js:56-67). Suppressing every registration leaves `tools/list`
|
|
197
|
+
* unregistered, and the framework's RPC layer answers it with
|
|
198
|
+
* `-32601 "Method not found"`.
|
|
199
|
+
*
|
|
200
|
+
* The reporter of #637 (SquirrelRat) inspected the suppressed child via
|
|
201
|
+
* `tools/list` and read the JSON-RPC error as "the plugin never registers any
|
|
202
|
+
* ctx_* tools" — when in fact the plugin DOES register all 11 tools natively
|
|
203
|
+
* (verified at `src/adapters/opencode/plugin.ts:469` and
|
|
204
|
+
* `tests/opencode-plugin.test.ts:88`). The misleading -32601 is the seed of
|
|
205
|
+
* the #637 perception.
|
|
206
|
+
*
|
|
207
|
+
* This helper installs an explicit handler that returns `{tools: []}` — a
|
|
208
|
+
* spec-compliant empty list. Paired with the existing #623 stderr diagnostic,
|
|
209
|
+
* an operator now sees:
|
|
210
|
+
* - wire response: `{tools: []}` (matches expectation, no JSON-RPC error)
|
|
211
|
+
* - stderr: `[context-mode] ctx_* tools/list intentionally empty… (#623)`
|
|
212
|
+
*
|
|
213
|
+
* Idempotent: throws inside SDK if called twice on the same server because
|
|
214
|
+
* `assertCanSetRequestHandler` (mcp.js:60) rejects duplicate registrations;
|
|
215
|
+
* we therefore install the SDK's default tool handlers FIRST (via a no-op
|
|
216
|
+
* registerTool of a fake tool, immediately removed) only if needed. To keep
|
|
217
|
+
* the public surface minimal, we just call `server.server.setRequestHandler`
|
|
218
|
+
* directly — that is the same low-level call used for prompts/resources at
|
|
219
|
+
* server.ts:259-261 and avoids the SDK guard entirely.
|
|
220
|
+
*
|
|
221
|
+
* Exported for test (#637 in-memory regression guard).
|
|
222
|
+
*/
|
|
223
|
+
export function registerEmptyToolsListHandler(target = server) {
|
|
224
|
+
target.server.registerCapabilities({ tools: { listChanged: false } });
|
|
225
|
+
target.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [] }));
|
|
226
|
+
}
|
|
189
227
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
190
228
|
server.registerTool = (...args) => {
|
|
191
229
|
const [name, config, handler] = args;
|
|
@@ -196,6 +234,16 @@ server.registerTool = (...args) => {
|
|
|
196
234
|
REGISTERED_CTX_TOOLS.push({ name, config, handler });
|
|
197
235
|
return originalRegisterTool(...args);
|
|
198
236
|
};
|
|
237
|
+
// Issue #637 — when suppression is active, install the empty tools/list handler
|
|
238
|
+
// once at module-init time so the suppressed MCP child responds with
|
|
239
|
+
// `{tools: []}` instead of JSON-RPC `-32601 Method not found`. Pair with the
|
|
240
|
+
// #623 stderr diagnostic that explains WHY the list is empty. Skipped for the
|
|
241
|
+
// embedded plugin-import path because the embedded process is not the stdio
|
|
242
|
+
// MCP child an operator would inspect — it lives inside the OpenCode/Kilo
|
|
243
|
+
// host and never speaks JSON-RPC over stdio.
|
|
244
|
+
if (suppressMcpToolsForNativePluginHost && process.env.CONTEXT_MODE_EMBEDDED_PLUGIN_TOOLS !== "1") {
|
|
245
|
+
registerEmptyToolsListHandler(server);
|
|
246
|
+
}
|
|
199
247
|
const projectDirOverride = new AsyncLocalStorage();
|
|
200
248
|
export async function withProjectDirOverride(projectDir, fn) {
|
|
201
249
|
const ctx = typeof projectDir === "string" ? { projectDir } : projectDir;
|
|
@@ -204,7 +252,7 @@ export async function withProjectDirOverride(projectDir, fn) {
|
|
|
204
252
|
// Register empty prompts/resources handlers so MCP clients don't get -32601 (#168).
|
|
205
253
|
// OpenCode calls listPrompts()/listResources() unconditionally — the error can poison
|
|
206
254
|
// the SDK transport layer, causing subsequent listTools() calls to fail permanently.
|
|
207
|
-
import { ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
255
|
+
import { ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
208
256
|
server.server.registerCapabilities({ prompts: { listChanged: false }, resources: { listChanged: false } });
|
|
209
257
|
server.server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
|
|
210
258
|
server.server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [] }));
|
package/build/store.js
CHANGED
|
@@ -524,7 +524,7 @@ export class ContentStore {
|
|
|
524
524
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
525
525
|
FROM chunks
|
|
526
526
|
JOIN sources ON sources.id = chunks.source_id
|
|
527
|
-
WHERE chunks MATCH ? AND sources.label LIKE ?
|
|
527
|
+
WHERE chunks MATCH ? AND sources.label LIKE ? ESCAPE '\\'
|
|
528
528
|
ORDER BY rank
|
|
529
529
|
LIMIT ?
|
|
530
530
|
`);
|
|
@@ -569,7 +569,7 @@ export class ContentStore {
|
|
|
569
569
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
570
570
|
FROM chunks_trigram
|
|
571
571
|
JOIN sources ON sources.id = chunks_trigram.source_id
|
|
572
|
-
WHERE chunks_trigram MATCH ? AND sources.label LIKE ?
|
|
572
|
+
WHERE chunks_trigram MATCH ? AND sources.label LIKE ? ESCAPE '\\'
|
|
573
573
|
ORDER BY rank
|
|
574
574
|
LIMIT ?
|
|
575
575
|
`);
|
|
@@ -615,7 +615,7 @@ export class ContentStore {
|
|
|
615
615
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
616
616
|
FROM chunks
|
|
617
617
|
JOIN sources ON sources.id = chunks.source_id
|
|
618
|
-
WHERE chunks MATCH ? AND sources.label LIKE ? AND chunks.content_type = ?
|
|
618
|
+
WHERE chunks MATCH ? AND sources.label LIKE ? ESCAPE '\\' AND chunks.content_type = ?
|
|
619
619
|
ORDER BY rank
|
|
620
620
|
LIMIT ?
|
|
621
621
|
`);
|
|
@@ -660,7 +660,7 @@ export class ContentStore {
|
|
|
660
660
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
661
661
|
FROM chunks_trigram
|
|
662
662
|
JOIN sources ON sources.id = chunks_trigram.source_id
|
|
663
|
-
WHERE chunks_trigram MATCH ? AND sources.label LIKE ? AND chunks_trigram.content_type = ?
|
|
663
|
+
WHERE chunks_trigram MATCH ? AND sources.label LIKE ? ESCAPE '\\' AND chunks_trigram.content_type = ?
|
|
664
664
|
ORDER BY rank
|
|
665
665
|
LIMIT ?
|
|
666
666
|
`);
|
|
@@ -859,7 +859,19 @@ export class ContentStore {
|
|
|
859
859
|
}));
|
|
860
860
|
}
|
|
861
861
|
#sourceFilterParam(source, sourceMatchMode) {
|
|
862
|
-
|
|
862
|
+
if (sourceMatchMode === "exact")
|
|
863
|
+
return source;
|
|
864
|
+
// Escape SQLite LIKE metacharacters so user-supplied source labels
|
|
865
|
+
// containing `_`, `%`, or `\` are matched literally rather than as
|
|
866
|
+
// wildcards. Backslash must be replaced first (otherwise subsequent
|
|
867
|
+
// escapes would themselves be re-escaped). Paired with `ESCAPE '\'`
|
|
868
|
+
// in the four prepared LIKE statements (#stmtSearchPorter*,
|
|
869
|
+
// #stmtSearchTrigram*). Regression: #646.
|
|
870
|
+
const escaped = source
|
|
871
|
+
.replace(/\\/g, "\\\\")
|
|
872
|
+
.replace(/%/g, "\\%")
|
|
873
|
+
.replace(/_/g, "\\_");
|
|
874
|
+
return `%${escaped}%`;
|
|
863
875
|
}
|
|
864
876
|
search(query, limit = 3, source, mode = "AND", contentType, sourceMatchMode = "like") {
|
|
865
877
|
const sanitized = sanitizeQuery(query, mode);
|