context-mode 1.0.128 → 1.0.129
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/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/build/adapters/detect.d.ts +30 -2
- package/build/adapters/detect.js +31 -0
- package/build/adapters/pi/mcp-bridge.js +19 -4
- package/build/db-base.js +39 -12
- package/cli.bundle.mjs +98 -98
- package/hooks/session-db.bundle.mjs +2 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +78 -78
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code plugins by Mert Koseoğlu",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.129"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "context-mode",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
16
|
-
"version": "1.0.
|
|
16
|
+
"version": "1.0.129",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Mert Koseoğlu"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.129",
|
|
4
4
|
"description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Context Mode",
|
|
4
4
|
"kind": "tool",
|
|
5
5
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.129",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.129",
|
|
4
4
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -34,12 +34,20 @@ export declare function __seedClaudeCodePluginCacheMissForTests(): void;
|
|
|
34
34
|
* `resolveProjectDir({ strictPlatform })` to form the candidate list,
|
|
35
35
|
* and by Pi's bridge to scrub foreign workspace vars on child spawn.
|
|
36
36
|
* - `identification`: env var only signals which host is running; carries
|
|
37
|
-
* no project path.
|
|
38
|
-
*
|
|
37
|
+
* no project path. PRESERVED in normal operation (some are load-bearing
|
|
38
|
+
* for hook integrations on the host that owns them, e.g. CLAUDE_PLUGIN_ROOT
|
|
39
|
+
* for Claude Code's hook context).
|
|
39
40
|
*
|
|
40
41
|
* Issue #545 — algorithmic env-leak fix. The split allows resolveProjectDir
|
|
41
42
|
* to derive ALLOW (own workspace vars) and BAN (other platforms' workspace
|
|
42
43
|
* vars) sets from a single registry, satisfying MUST-3 (15 adapters equal).
|
|
44
|
+
*
|
|
45
|
+
* Issue #561 — FOREIGN identification vars MUST be scrubbed when spawning a
|
|
46
|
+
* child under a different host (e.g. Pi spawning context-mode child must
|
|
47
|
+
* scrub Claude Code identification vars CLAUDE_CODE_ENTRYPOINT /
|
|
48
|
+
* CLAUDE_PLUGIN_ROOT to prevent detectPlatform() in the child from
|
|
49
|
+
* misidentifying the host as claude-code and writing Pi's data into
|
|
50
|
+
* ~/.claude/context-mode/). See `foreignIdentificationEnv()` below.
|
|
43
51
|
*/
|
|
44
52
|
export type EnvVarRole = "workspace" | "identification";
|
|
45
53
|
export interface PlatformEnvEntry {
|
|
@@ -78,6 +86,26 @@ export declare function workspaceEnvVarsFor(platform: PlatformId): string[];
|
|
|
78
86
|
* workspace vars from spawned MCP child) and by the matrix regression test.
|
|
79
87
|
*/
|
|
80
88
|
export declare function foreignWorkspaceEnv(platform: PlatformId): Set<string>;
|
|
89
|
+
/**
|
|
90
|
+
* Issue #561 — return the union of identification env vars from ALL
|
|
91
|
+
* platforms EXCEPT the given one. Sibling of `foreignWorkspaceEnv`,
|
|
92
|
+
* filtered on `role === "identification"` instead of "workspace".
|
|
93
|
+
*
|
|
94
|
+
* Consumed by Pi's bridge env scrub: when Pi spawns the context-mode
|
|
95
|
+
* MCP child, the child inherits the host shell env including any
|
|
96
|
+
* identification vars set by a co-resident Claude Code session
|
|
97
|
+
* (CLAUDE_CODE_ENTRYPOINT / CLAUDE_PLUGIN_ROOT). Without scrubbing,
|
|
98
|
+
* `detectPlatform()` in the child falls through env priority order and
|
|
99
|
+
* resolves to claude-code first — Pi's session data then writes into
|
|
100
|
+
* `~/.claude/context-mode/` instead of Pi's own dir. Scrubbing FOREIGN
|
|
101
|
+
* identification vars (everyone else's) preserves Pi's OWN identification
|
|
102
|
+
* vars (PI_CONFIG_DIR / PI_SESSION_FILE / PI_COMPILED) so the child still
|
|
103
|
+
* detects pi correctly.
|
|
104
|
+
*
|
|
105
|
+
* Algorithmic, registry-driven — adding adapter #16 grows the scrub
|
|
106
|
+
* automatically (no edit to mcp-bridge.ts).
|
|
107
|
+
*/
|
|
108
|
+
export declare function foreignIdentificationEnv(platform: PlatformId): Set<string>;
|
|
81
109
|
/**
|
|
82
110
|
* Sync map from platform identifier → home-relative path segments where that
|
|
83
111
|
* platform stores its config. Mirrors the `super([...])` argument passed by
|
package/build/adapters/detect.js
CHANGED
|
@@ -222,6 +222,37 @@ export function foreignWorkspaceEnv(platform) {
|
|
|
222
222
|
}
|
|
223
223
|
return ban;
|
|
224
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Issue #561 — return the union of identification env vars from ALL
|
|
227
|
+
* platforms EXCEPT the given one. Sibling of `foreignWorkspaceEnv`,
|
|
228
|
+
* filtered on `role === "identification"` instead of "workspace".
|
|
229
|
+
*
|
|
230
|
+
* Consumed by Pi's bridge env scrub: when Pi spawns the context-mode
|
|
231
|
+
* MCP child, the child inherits the host shell env including any
|
|
232
|
+
* identification vars set by a co-resident Claude Code session
|
|
233
|
+
* (CLAUDE_CODE_ENTRYPOINT / CLAUDE_PLUGIN_ROOT). Without scrubbing,
|
|
234
|
+
* `detectPlatform()` in the child falls through env priority order and
|
|
235
|
+
* resolves to claude-code first — Pi's session data then writes into
|
|
236
|
+
* `~/.claude/context-mode/` instead of Pi's own dir. Scrubbing FOREIGN
|
|
237
|
+
* identification vars (everyone else's) preserves Pi's OWN identification
|
|
238
|
+
* vars (PI_CONFIG_DIR / PI_SESSION_FILE / PI_COMPILED) so the child still
|
|
239
|
+
* detects pi correctly.
|
|
240
|
+
*
|
|
241
|
+
* Algorithmic, registry-driven — adding adapter #16 grows the scrub
|
|
242
|
+
* automatically (no edit to mcp-bridge.ts).
|
|
243
|
+
*/
|
|
244
|
+
export function foreignIdentificationEnv(platform) {
|
|
245
|
+
const ban = new Set();
|
|
246
|
+
for (const [p, vars] of PLATFORM_ENV_VARS) {
|
|
247
|
+
if (p === platform)
|
|
248
|
+
continue;
|
|
249
|
+
for (const v of vars) {
|
|
250
|
+
if (v.role === "identification")
|
|
251
|
+
ban.add(v.name);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return ban;
|
|
255
|
+
}
|
|
225
256
|
/**
|
|
226
257
|
* Sync map from platform identifier → home-relative path segments where that
|
|
227
258
|
* platform stores its config. Mirrors the `super([...])` argument passed by
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { spawn, execSync } from "node:child_process";
|
|
24
24
|
import { detectRuntimes } from "../../runtime.js";
|
|
25
|
-
import { foreignWorkspaceEnv } from "../detect.js";
|
|
25
|
+
import { foreignWorkspaceEnv, foreignIdentificationEnv } from "../detect.js";
|
|
26
26
|
// ── Fork-bomb prevention (#516) ──────────────────────────────────────
|
|
27
27
|
//
|
|
28
28
|
// Original bug: `spawn(process.execPath, [serverScript])` recursively
|
|
@@ -154,12 +154,27 @@ export class MCPStdioClient {
|
|
|
154
154
|
// and Pi's sessions write into the wrong project. The ban list is
|
|
155
155
|
// derived ALGORITHMICALLY from PLATFORM_ENV_VARS (every other adapter's
|
|
156
156
|
// workspace-role vars), so adding adapter #16 grows the scrub
|
|
157
|
-
// automatically — no edit to this file.
|
|
158
|
-
//
|
|
159
|
-
//
|
|
157
|
+
// automatically — no edit to this file. Pi's own workspace vars and
|
|
158
|
+
// the universal escape hatch (CONTEXT_MODE_PROJECT_DIR) are NEVER
|
|
159
|
+
// scrubbed.
|
|
160
160
|
for (const banned of foreignWorkspaceEnv("pi")) {
|
|
161
161
|
delete childEnv[banned];
|
|
162
162
|
}
|
|
163
|
+
// Issue #561 — scrub foreign IDENTIFICATION env vars before spawn.
|
|
164
|
+
//
|
|
165
|
+
// Foreign identification vars hijack detectPlatform() — must scrub
|
|
166
|
+
// when spawning child under a different host (#561). When Pi runs
|
|
167
|
+
// co-resident with Claude Code, the inherited shell env carries
|
|
168
|
+
// CLAUDE_CODE_ENTRYPOINT and CLAUDE_PLUGIN_ROOT; the spawned MCP
|
|
169
|
+
// child's detectPlatform() then walks PLATFORM_ENV_VARS in priority
|
|
170
|
+
// order (claude-code first), returns claude-code, and Pi's session
|
|
171
|
+
// data lands in ~/.claude/context-mode/ instead of Pi's own dir.
|
|
172
|
+
// Pi's OWN identification vars (PI_CONFIG_DIR / PI_SESSION_FILE /
|
|
173
|
+
// PI_COMPILED) are excluded from the ban set so the child still
|
|
174
|
+
// detects pi correctly.
|
|
175
|
+
for (const banned of foreignIdentificationEnv("pi")) {
|
|
176
|
+
delete childEnv[banned];
|
|
177
|
+
}
|
|
163
178
|
this._spawnEnv = childEnv;
|
|
164
179
|
this.child = spawn(runtime, [this.serverScript], {
|
|
165
180
|
// Pipe stderr (#472 round-3): swallowing it via "ignore" hides
|
package/build/db-base.js
CHANGED
|
@@ -316,18 +316,15 @@ export function applyWALPragmas(db) {
|
|
|
316
316
|
db.pragma("mmap_size = 268435456");
|
|
317
317
|
}
|
|
318
318
|
catch { /* unsupported runtime */ }
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
db.pragma("locking_mode = EXCLUSIVE");
|
|
329
|
-
}
|
|
330
|
-
catch { /* unsupported runtime */ }
|
|
319
|
+
// NOTE: `locking_mode = EXCLUSIVE` is intentionally NOT applied here.
|
|
320
|
+
// Multi-writer scenarios are valid: `ContentStore` (FTS5 shared
|
|
321
|
+
// knowledge base) is opened concurrently from multiple sessions on the
|
|
322
|
+
// SAME db file by design — applying EXCLUSIVE here would deadlock the
|
|
323
|
+
// second instance and break documented `withRetry`-based BUSY handling.
|
|
324
|
+
// Single-writer enforcement (lockfile + EXCLUSIVE) lives in
|
|
325
|
+
// `SQLiteBase` ctor which is opted into by per-project DBs (SessionDB).
|
|
326
|
+
// Different DB paths from different worktrees/sessions ARE concurrent
|
|
327
|
+
// by design — the lockfile is per-dbPath, not per-process.
|
|
331
328
|
}
|
|
332
329
|
// ─────────────────────────────────────────────────────────
|
|
333
330
|
// DB file helpers
|
|
@@ -507,11 +504,35 @@ export class SQLiteBase {
|
|
|
507
504
|
// tmpdir-prefix check inside the helper — defaultDBPath() output
|
|
508
505
|
// (per-process tmp DBs) does not contend, so it never claims a lock.
|
|
509
506
|
acquireDbLock({ dbPath });
|
|
507
|
+
// v1.0.129 — single source of truth for skip decision. Both lockfile
|
|
508
|
+
// (above) and EXCLUSIVE pragma (below) MUST share the same skip-gate:
|
|
509
|
+
// tests open SessionDB twice on the same tmpdir path to exercise
|
|
510
|
+
// multi-writer scenarios; if EXCLUSIVE fires here when lockfile didn't,
|
|
511
|
+
// the second open hits SQLITE_BUSY on its FIRST pragma call inside
|
|
512
|
+
// applyWALPragmas — caused 82 test failures during v1.0.128 verification.
|
|
513
|
+
const skipExclusive = dbPath.startsWith(tmpdir());
|
|
510
514
|
cleanOrphanedWALFiles(dbPath);
|
|
511
515
|
let db;
|
|
512
516
|
try {
|
|
513
517
|
db = new Database(dbPath, { timeout: 30000 });
|
|
514
518
|
applyWALPragmas(db);
|
|
519
|
+
// v1.0.128 — Issue #560 SECONDARY defense for SQLiteBase callers (single-writer
|
|
520
|
+
// DBs like SessionDB). The .lock file (acquireDbLock above) is PRIMARY — it
|
|
521
|
+
// surfaces the conflicting PID with a clear UX message. EXCLUSIVE locking
|
|
522
|
+
// closes the narrow race window between lockfile claim + the actual
|
|
523
|
+
// `new Database(...)` open: a parallel process passing the lockfile
|
|
524
|
+
// check would still get SQLITE_BUSY from this pragma. Wrapped in try/catch —
|
|
525
|
+
// backends that don't expose locking_mode (or pragma at all) still get the
|
|
526
|
+
// lockfile floor. NOTE: NOT applied in `applyWALPragmas` because multi-writer
|
|
527
|
+
// callers (ContentStore — FTS5 shared knowledge base across sessions) rely
|
|
528
|
+
// on the default SHARED locking mode + withRetry to handle SQLITE_BUSY.
|
|
529
|
+
// Skip on tmpdir paths to match acquireDbLock's skip-gate.
|
|
530
|
+
if (!skipExclusive) {
|
|
531
|
+
try {
|
|
532
|
+
db.pragma("locking_mode = EXCLUSIVE");
|
|
533
|
+
}
|
|
534
|
+
catch { /* unsupported runtime */ }
|
|
535
|
+
}
|
|
515
536
|
}
|
|
516
537
|
catch (err) {
|
|
517
538
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -521,6 +542,12 @@ export class SQLiteBase {
|
|
|
521
542
|
try {
|
|
522
543
|
db = new Database(dbPath, { timeout: 30000 });
|
|
523
544
|
applyWALPragmas(db);
|
|
545
|
+
if (!skipExclusive) {
|
|
546
|
+
try {
|
|
547
|
+
db.pragma("locking_mode = EXCLUSIVE");
|
|
548
|
+
}
|
|
549
|
+
catch { /* unsupported runtime */ }
|
|
550
|
+
}
|
|
524
551
|
}
|
|
525
552
|
catch (retryErr) {
|
|
526
553
|
// Free the lock before bubbling — caller can never reach
|