context-mode 1.0.136 → 1.0.137
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 +2 -2
- 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 +4 -23
- package/build/adapters/codex/index.js +24 -3
- package/build/adapters/opencode/index.d.ts +1 -0
- package/build/adapters/opencode/index.js +25 -0
- package/build/adapters/opencode/plugin.d.ts +22 -0
- package/build/adapters/opencode/plugin.js +52 -0
- package/build/adapters/pi/extension.js +20 -4
- package/build/adapters/pi/mcp-bridge.d.ts +2 -1
- package/build/adapters/pi/mcp-bridge.js +49 -3
- package/build/lifecycle.d.ts +2 -51
- package/build/lifecycle.js +3 -67
- package/build/server.d.ts +19 -0
- package/build/server.js +141 -58
- package/build/session/db.d.ts +6 -0
- package/build/session/db.js +17 -3
- package/build/util/sibling-mcp.d.ts +0 -40
- package/build/util/sibling-mcp.js +11 -116
- package/cli.bundle.mjs +131 -129
- package/configs/kilo/kilo.json +0 -11
- package/configs/opencode/opencode.json +0 -11
- package/hooks/normalize-hooks.mjs +101 -19
- package/hooks/session-db.bundle.mjs +3 -3
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +112 -110
package/configs/kilo/kilo.json
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://app.kilo.ai/config.json",
|
|
3
|
-
"mcp": {
|
|
4
|
-
"context-mode": {
|
|
5
|
-
"type": "local",
|
|
6
|
-
"command": [
|
|
7
|
-
"context-mode"
|
|
8
|
-
],
|
|
9
|
-
"environment": {
|
|
10
|
-
"CONTEXT_MODE_IDLE_TIMEOUT_MS": "900000"
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
3
|
"plugin": [
|
|
15
4
|
"context-mode"
|
|
16
5
|
]
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://opencode.ai/config.json",
|
|
3
|
-
"mcp": {
|
|
4
|
-
"context-mode": {
|
|
5
|
-
"type": "local",
|
|
6
|
-
"command": [
|
|
7
|
-
"context-mode"
|
|
8
|
-
],
|
|
9
|
-
"environment": {
|
|
10
|
-
"CONTEXT_MODE_IDLE_TIMEOUT_MS": "900000"
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
3
|
"plugin": [
|
|
15
4
|
"context-mode"
|
|
16
5
|
]
|
|
@@ -18,18 +18,70 @@ import { resolve } from "node:path";
|
|
|
18
18
|
|
|
19
19
|
const PLACEHOLDER = "${CLAUDE_PLUGIN_ROOT}";
|
|
20
20
|
|
|
21
|
+
// #604: matches a cache path segment `context-mode/context-mode/<version>`.
|
|
22
|
+
// Capture group is the X.Y.Z version. Used to detect command paths frozen on a
|
|
23
|
+
// previous-version dir that Claude Code's native plugin manager has since
|
|
24
|
+
// cleaned up. `/g` so a single content blob with multiple stale references is
|
|
25
|
+
// fully covered. Forward-slash only — callers convert beforehand.
|
|
26
|
+
const CACHE_VERSION_RE =
|
|
27
|
+
/context-mode\/context-mode\/([0-9]+\.[0-9]+\.[0-9]+)(?=\/)/g;
|
|
28
|
+
|
|
21
29
|
/** Convert any path string to forward slashes (MSYS-safe). */
|
|
22
30
|
function fwd(p) {
|
|
23
31
|
return String(p).replace(/\\/g, "/");
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
35
|
+
* Extract the X.Y.Z version segment from a pluginRoot under the context-mode
|
|
36
|
+
* cache layout. Returns null when running from npm-global, a dev checkout, or
|
|
37
|
+
* any layout that does not match the `<…>/context-mode/context-mode/<v>(/…)?`
|
|
38
|
+
* pattern — callers must treat null as "no stale-path check is possible".
|
|
39
|
+
*/
|
|
40
|
+
function pluginRootVersion(pluginRoot) {
|
|
41
|
+
if (!pluginRoot) return null;
|
|
42
|
+
const m =
|
|
43
|
+
/context-mode\/context-mode\/([0-9]+\.[0-9]+\.[0-9]+)(?:\/|$)/.exec(
|
|
44
|
+
fwd(pluginRoot),
|
|
45
|
+
);
|
|
46
|
+
return m ? m[1] : null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Does `content` reference any context-mode cache version segment that differs
|
|
51
|
+
* from `currentVersion`? Detects the #604 ratchet: already-normalized hooks.json
|
|
52
|
+
* / plugin.json carrying a previous version's absolute paths forward into a
|
|
53
|
+
* newer version's cache directory after Claude Code's auto-update.
|
|
54
|
+
*/
|
|
55
|
+
function hasStaleCacheVersionSegment(content, currentVersion) {
|
|
56
|
+
if (!currentVersion || !content || typeof content !== "string") return false;
|
|
57
|
+
const safe = fwd(content);
|
|
58
|
+
CACHE_VERSION_RE.lastIndex = 0;
|
|
59
|
+
let m;
|
|
60
|
+
while ((m = CACHE_VERSION_RE.exec(safe)) !== null) {
|
|
61
|
+
if (m[1] !== currentVersion) return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Pure detection: does this content need to be (re-)normalized?
|
|
68
|
+
*
|
|
69
|
+
* Two triggers:
|
|
70
|
+
* 1. Fresh content still containing the `${CLAUDE_PLUGIN_ROOT}` placeholder
|
|
71
|
+
* — the original #378 first-boot path on any host.
|
|
72
|
+
* 2. (#604) Already-resolved content whose absolute paths point at a
|
|
73
|
+
* different version of the context-mode cache than the current
|
|
74
|
+
* `pluginRoot`. Breaks the ratchet that previously froze stale paths
|
|
75
|
+
* after Claude Code's native plugin manager copied a previous version's
|
|
76
|
+
* hooks.json forward.
|
|
77
|
+
*
|
|
78
|
+
* `pluginRoot` is optional for backwards compatibility with single-arg
|
|
79
|
+
* callers; without it, only the placeholder check runs.
|
|
29
80
|
*/
|
|
30
|
-
export function needsHookNormalization(content) {
|
|
81
|
+
export function needsHookNormalization(content, pluginRoot) {
|
|
31
82
|
if (!content || typeof content !== "string") return false;
|
|
32
|
-
|
|
83
|
+
if (content.includes(PLACEHOLDER)) return true;
|
|
84
|
+
return hasStaleCacheVersionSegment(content, pluginRootVersion(pluginRoot));
|
|
33
85
|
}
|
|
34
86
|
|
|
35
87
|
/**
|
|
@@ -41,10 +93,11 @@ export function needsHookNormalization(content) {
|
|
|
41
93
|
* Idempotent — leaves already-normalized content unchanged.
|
|
42
94
|
*/
|
|
43
95
|
export function normalizeHooksJson(content, nodePath, pluginRoot) {
|
|
44
|
-
if (!needsHookNormalization(content)) return content;
|
|
96
|
+
if (!needsHookNormalization(content, pluginRoot)) return content;
|
|
45
97
|
|
|
46
98
|
const safeNode = fwd(nodePath);
|
|
47
99
|
const safeRoot = fwd(pluginRoot);
|
|
100
|
+
const currentVersion = pluginRootVersion(pluginRoot);
|
|
48
101
|
|
|
49
102
|
let parsed;
|
|
50
103
|
try {
|
|
@@ -65,12 +118,30 @@ export function normalizeHooksJson(content, nodePath, pluginRoot) {
|
|
|
65
118
|
if (!Array.isArray(inner)) continue;
|
|
66
119
|
for (const h of inner) {
|
|
67
120
|
if (typeof h?.command !== "string") continue;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
121
|
+
|
|
122
|
+
const hasPlaceholder = h.command.includes(PLACEHOLDER);
|
|
123
|
+
// #604: also rewrite when the command holds a stale absolute path under
|
|
124
|
+
// a previous-version cache dir (Claude Code's auto-update ratchet).
|
|
125
|
+
const hasStale = hasStaleCacheVersionSegment(h.command, currentVersion);
|
|
126
|
+
if (!hasPlaceholder && !hasStale) continue;
|
|
127
|
+
|
|
128
|
+
let next = h.command;
|
|
129
|
+
if (hasPlaceholder) {
|
|
130
|
+
// Replace placeholder with absolute root (forward-slash).
|
|
131
|
+
next = next.replaceAll(PLACEHOLDER, safeRoot);
|
|
132
|
+
// Replace bare `node ` prefix with quoted execPath. Match both
|
|
133
|
+
// `node ` and `node\t` at start, with optional surrounding whitespace.
|
|
134
|
+
next = next.replace(/^\s*node\s+/, `"${safeNode}" `);
|
|
135
|
+
}
|
|
136
|
+
if (hasStale) {
|
|
137
|
+
// Re-point every `context-mode/context-mode/<old-version>/…` segment
|
|
138
|
+
// to the current pluginRoot's version. Operates on the forward-slash
|
|
139
|
+
// form so MSYS-mangled paths heal as well.
|
|
140
|
+
next = fwd(next).replace(
|
|
141
|
+
CACHE_VERSION_RE,
|
|
142
|
+
`context-mode/context-mode/${currentVersion}`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
74
145
|
h.command = next;
|
|
75
146
|
mutated = true;
|
|
76
147
|
}
|
|
@@ -92,10 +163,11 @@ export function normalizeHooksJson(content, nodePath, pluginRoot) {
|
|
|
92
163
|
* Idempotent.
|
|
93
164
|
*/
|
|
94
165
|
export function normalizePluginJson(content, nodePath, pluginRoot) {
|
|
95
|
-
if (!needsHookNormalization(content)) return content;
|
|
166
|
+
if (!needsHookNormalization(content, pluginRoot)) return content;
|
|
96
167
|
|
|
97
168
|
const safeNode = fwd(nodePath);
|
|
98
169
|
const safeRoot = fwd(pluginRoot);
|
|
170
|
+
const currentVersion = pluginRootVersion(pluginRoot);
|
|
99
171
|
|
|
100
172
|
let parsed;
|
|
101
173
|
try {
|
|
@@ -114,11 +186,21 @@ export function normalizePluginJson(content, nodePath, pluginRoot) {
|
|
|
114
186
|
|
|
115
187
|
if (Array.isArray(srv.args)) {
|
|
116
188
|
const before = srv.args;
|
|
117
|
-
const after = before.map((a) =>
|
|
118
|
-
typeof a
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
189
|
+
const after = before.map((a) => {
|
|
190
|
+
if (typeof a !== "string") return a;
|
|
191
|
+
let next = a;
|
|
192
|
+
if (next.includes(PLACEHOLDER)) {
|
|
193
|
+
next = next.replaceAll(PLACEHOLDER, safeRoot);
|
|
194
|
+
}
|
|
195
|
+
// #604: same auto-update ratchet hits plugin.json args (see #523).
|
|
196
|
+
if (hasStaleCacheVersionSegment(next, currentVersion)) {
|
|
197
|
+
next = fwd(next).replace(
|
|
198
|
+
CACHE_VERSION_RE,
|
|
199
|
+
`context-mode/context-mode/${currentVersion}`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
return next;
|
|
203
|
+
});
|
|
122
204
|
if (after.some((v, i) => v !== before[i])) {
|
|
123
205
|
srv.args = after;
|
|
124
206
|
mutated = true;
|
|
@@ -158,7 +240,7 @@ export function normalizeHooksOnStartup({ pluginRoot, nodePath, platform }) {
|
|
|
158
240
|
const hooksPath = resolve(pluginRoot, "hooks", "hooks.json");
|
|
159
241
|
if (existsSync(hooksPath)) {
|
|
160
242
|
const original = readFileSync(hooksPath, "utf-8");
|
|
161
|
-
if (needsHookNormalization(original)) {
|
|
243
|
+
if (needsHookNormalization(original, pluginRoot)) {
|
|
162
244
|
const next = normalizeHooksJson(original, nodePath, pluginRoot);
|
|
163
245
|
if (next !== original) {
|
|
164
246
|
writeFileSync(hooksPath, next, "utf-8");
|
|
@@ -174,7 +256,7 @@ export function normalizeHooksOnStartup({ pluginRoot, nodePath, platform }) {
|
|
|
174
256
|
const pluginPath = resolve(pluginRoot, ".claude-plugin", "plugin.json");
|
|
175
257
|
if (existsSync(pluginPath)) {
|
|
176
258
|
const original = readFileSync(pluginPath, "utf-8");
|
|
177
|
-
if (needsHookNormalization(original)) {
|
|
259
|
+
if (needsHookNormalization(original, pluginRoot)) {
|
|
178
260
|
const next = normalizePluginJson(original, nodePath, pluginRoot);
|
|
179
261
|
if (next !== original) {
|
|
180
262
|
writeFileSync(pluginPath, next, "utf-8");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createRequire as Y}from"node:module";import{existsSync as G,unlinkSync as x,renameSync as q}from"node:fs";import{tmpdir as z}from"node:os";import{join as K}from"node:path";var N=class{#t;constructor(t){this.#t=t}pragma(t){let s=this.#t.prepare(`PRAGMA ${t}`).all();if(!s||s.length===0)return;if(s.length>1)return s;let r=Object.values(s[0]);return r.length===1?r[0]:s[0]}exec(t){let e="",s=null;for(let o=0;o<t.length;o++){let a=t[o];if(s)e+=a,a===s&&(s=null);else if(a==="'"||a==='"')e+=a,s=a;else if(a===";"){let c=e.trim();c&&this.#t.prepare(c).run(),e=""}else e+=a}let r=e.trim();return r&&this.#t.prepare(r).run(),this}prepare(t){let e=this.#t.prepare(t);return{run:(...s)=>e.run(...s),get:(...s)=>{let r=e.get(...s);return r===null?void 0:r},all:(...s)=>e.all(...s),iterate:(...s)=>e.iterate(...s)}}transaction(t){return this.#t.transaction(t)}close(){this.#t.close()}},
|
|
1
|
+
import{createRequire as Y}from"node:module";import{existsSync as G,unlinkSync as x,renameSync as q}from"node:fs";import{tmpdir as z}from"node:os";import{join as K}from"node:path";var N=class{#t;constructor(t){this.#t=t}pragma(t){let s=this.#t.prepare(`PRAGMA ${t}`).all();if(!s||s.length===0)return;if(s.length>1)return s;let r=Object.values(s[0]);return r.length===1?r[0]:s[0]}exec(t){let e="",s=null;for(let o=0;o<t.length;o++){let a=t[o];if(s)e+=a,a===s&&(s=null);else if(a==="'"||a==='"')e+=a,s=a;else if(a===";"){let c=e.trim();c&&this.#t.prepare(c).run(),e=""}else e+=a}let r=e.trim();return r&&this.#t.prepare(r).run(),this}prepare(t){let e=this.#t.prepare(t);return{run:(...s)=>e.run(...s),get:(...s)=>{let r=e.get(...s);return r===null?void 0:r},all:(...s)=>e.all(...s),iterate:(...s)=>e.iterate(...s)}}transaction(t){return this.#t.transaction(t)}close(){this.#t.close()}},D=class{#t;constructor(t){this.#t=t}pragma(t){let s=this.#t.prepare(`PRAGMA ${t}`).all();if(!s||s.length===0)return;if(s.length>1)return s;let r=Object.values(s[0]);return r.length===1?r[0]:s[0]}exec(t){return this.#t.exec(t),this}prepare(t){let e=this.#t.prepare(t);return{run:(...s)=>e.run(...s),get:(...s)=>e.get(...s),all:(...s)=>e.all(...s),iterate:(...s)=>typeof e.iterate=="function"?e.iterate(...s):e.all(...s)[Symbol.iterator]()}}transaction(t){return(...e)=>{this.#t.exec("BEGIN");try{let s=t(...e);return this.#t.exec("COMMIT"),s}catch(s){throw this.#t.exec("ROLLBACK"),s}}}close(){this.#t.close()}},l=null;function V(n){let t=null;try{return t=new n(":memory:"),t.exec("CREATE VIRTUAL TABLE __fts5_probe USING fts5(x)"),!0}catch{return!1}finally{try{t?.close()}catch{}}}function Q(n,t){let e=t!==void 0?t:globalThis.Bun;if(typeof e<"u"&&e!==null)return!0;let s=n??process.versions,[r,o]=(s.node??"0.0.0").split("."),a=Number(r),c=Number(o);return!Number.isFinite(a)||!Number.isFinite(c)?!1:a>22||a===22&&c>=5}function J(){if(!l){let n=Y(import.meta.url);if(globalThis.Bun){let t=n(["bun","sqlite"].join(":")).Database;l=function(s,r){let o=new t(s,{readonly:r?.readonly,create:!0}),a=new N(o);return r?.timeout&&a.pragma(`busy_timeout = ${r.timeout}`),a}}else if(Q()){let t=null;try{({DatabaseSync:t}=n(["node","sqlite"].join(":")))}catch{t=null}t&&V(t)?l=function(s,r){let o=new t(s,{readOnly:r?.readonly??!1});return new D(o)}:l=n("better-sqlite3")}else l=n("better-sqlite3")}return l}function U(n){n.pragma("journal_mode = WAL"),n.pragma("synchronous = NORMAL");try{n.pragma("mmap_size = 268435456")}catch{}}function I(n){if(!G(n))for(let t of["-wal","-shm"])try{x(n+t)}catch{}}function Z(n){for(let t of["","-wal","-shm"])try{x(n+t)}catch{}}function A(n){try{n.pragma("wal_checkpoint(TRUNCATE)")}catch{}try{n.close()}catch{}}function M(n="context-mode"){return K(z(),`${n}-${process.pid}.db`)}function tt(n,t=[100,500,2e3]){let e;for(let s=0;s<=t.length;s++)try{return n()}catch(r){let o=r instanceof Error?r.message:String(r);if(!o.includes("SQLITE_BUSY")&&!o.includes("database is locked"))throw r;if(e=r instanceof Error?r:new Error(o),s<t.length){let a=t[s],c=Date.now();for(;Date.now()-c<a;);}}throw new Error(`SQLITE_BUSY: database is locked after ${t.length} retries. Original error: ${e?.message}`)}function et(n){return n.includes("SQLITE_CORRUPT")||n.includes("SQLITE_NOTADB")||n.includes("database disk image is malformed")||n.includes("file is not a database")}function st(n){let t=Date.now();for(let e of["","-wal","-shm"])try{q(n+e,`${n}${e}.corrupt-${t}`)}catch{}}var _=Symbol.for("__context_mode_live_dbs_v3__"),v=(()=>{let n=globalThis;return n[_]||(n[_]=new Set,process.on("exit",()=>{for(let t of n[_])A(t);n[_].clear()})),n[_]})(),y=class{#t;#e;constructor(t){let e=J();this.#t=t,I(t);let s;try{s=new e(t,{timeout:3e4}),U(s)}catch(r){let o=r instanceof Error?r.message:String(r);if(et(o)){st(t),I(t);try{s=new e(t,{timeout:3e4}),U(s)}catch(a){throw new Error(`Failed to create fresh DB after renaming corrupt file: ${a instanceof Error?a.message:String(a)}`)}}else throw r}this.#e=s,v.add(this.#e),this.initSchema(),this.prepareStatements()}get db(){return this.#e}get dbPath(){return this.#t}close(){v.delete(this.#e),A(this.#e)}withRetry(t){return tt(t)}cleanup(){v.delete(this.#e),A(this.#e),Z(this.#t)}};import{createHash as p}from"node:crypto";import{execFileSync as nt}from"node:child_process";import{existsSync as f,realpathSync as rt,renameSync as C}from"node:fs";import{join as b}from"node:path";var E;function g(n){let t=n.replace(/\\/g,"/");return/^\/+$/.test(t)?"/":/^[A-Za-z]:\/+$/.test(t)?`${t.slice(0,2)}/`:t.replace(/\/+$/,"")}function F(n){let t=n;try{t=rt.native(n)}catch{}let e=g(t);return process.platform==="win32"||process.platform==="darwin"?e.toLowerCase():e}function k(n,t){return nt("git",["-C",n,...t],{encoding:"utf-8",timeout:2e3,stdio:["ignore","pipe","ignore"]}).trim()}function it(n){let t=k(n,["rev-parse","--show-toplevel"]);return t.length>0?g(t):null}function ot(n){let t=k(n,["worktree","list","--porcelain"]).split(/\r?\n/).find(e=>e.startsWith("worktree "))?.replace("worktree ","")?.trim();return t?g(t):null}function at(n=process.cwd()){let t=process.env.CONTEXT_MODE_SESSION_SUFFIX;if(E&&E.projectDir===n&&E.envSuffix===t)return E.suffix;let e="";if(t!==void 0)e=t?`__${t}`:"";else try{let s=it(n),r=ot(n);if(s&&r){let o=F(s),a=F(r);o!==a&&(e=`__${p("sha256").update(o).digest("hex").slice(0,8)}`)}}catch{}return E={projectDir:n,envSuffix:t,suffix:e},e}function ht(){E=void 0}function X(n){return p("sha256").update(g(n)).digest("hex").slice(0,16)}function W(n){let t=g(n),e=process.platform==="darwin"||process.platform==="win32"?t.toLowerCase():t;return p("sha256").update(e).digest("hex").slice(0,16)}function ft(n){let{projectDir:t,contentDir:e}=n,s=W(t),r=b(e,`${s}.db`);if(f(r))return r;let o=X(t);if(o===s)return r;let a=b(e,`${o}.db`);if(f(a))try{C(a,r);for(let c of["-wal","-shm"])try{C(a+c,r+c)}catch{}}catch{}return r}function bt(n){return ct({...n,ext:".db"})}function ct(n){let{projectDir:t,sessionsDir:e,ext:s}=n,r=n.suffix??at(t),o=W(t),a=b(e,`${o}${r}${s}`);if(f(a))return a;let c=X(t);if(c===o)return a;let d=b(e,`${c}${r}${s}`);if(f(d))try{C(d,a)}catch{}return a}var B=1e3,P=5;function h(n){let t=Number(n);return!Number.isFinite(t)||t<=0?0:Math.floor(t)}var i={insertEvent:"insertEvent",getEvents:"getEvents",getEventsByType:"getEventsByType",getEventsByPriority:"getEventsByPriority",getEventsByTypeAndPriority:"getEventsByTypeAndPriority",getEventCount:"getEventCount",getLatestAttributedProject:"getLatestAttributedProject",checkDuplicate:"checkDuplicate",evictLowestPriority:"evictLowestPriority",updateMetaLastEvent:"updateMetaLastEvent",ensureSession:"ensureSession",getSessionStats:"getSessionStats",incrementCompactCount:"incrementCompactCount",upsertResume:"upsertResume",getResume:"getResume",markResumeConsumed:"markResumeConsumed",claimLatestUnconsumedResume:"claimLatestUnconsumedResume",deleteEvents:"deleteEvents",deleteMeta:"deleteMeta",deleteResume:"deleteResume",getOldSessions:"getOldSessions",searchEvents:"searchEvents",incrementToolCall:"incrementToolCall",getToolCallTotals:"getToolCallTotals",getToolCallByTool:"getToolCallByTool",getEventBytesSummary:"getEventBytesSummary"},j=class extends y{constructor(t){super(t?.dbPath??M("session"))}stmt(t){return this.stmts.get(t)}initSchema(){try{let e=this.db.pragma("table_xinfo(session_events)").find(s=>s.name==="data_hash");e&&e.hidden!==0&&this.db.exec("DROP TABLE session_events")}catch{}this.db.exec(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS session_events (
|
|
3
3
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
4
4
|
session_id TEXT NOT NULL,
|
|
@@ -102,7 +102,7 @@ import{createRequire as Y}from"node:module";import{existsSync as G,unlinkSync as
|
|
|
102
102
|
)
|
|
103
103
|
RETURNING session_id, snapshot`),t(i.deleteEvents,"DELETE FROM session_events WHERE session_id = ?"),t(i.deleteMeta,"DELETE FROM session_meta WHERE session_id = ?"),t(i.deleteResume,"DELETE FROM session_resume WHERE session_id = ?"),t(i.searchEvents,`SELECT id, session_id, category, type, data, created_at
|
|
104
104
|
FROM session_events
|
|
105
|
-
WHERE project_dir = ?
|
|
105
|
+
WHERE (project_dir = ? OR project_dir = '')
|
|
106
106
|
AND (data LIKE '%' || ? || '%' ESCAPE '\\' OR category LIKE '%' || ? || '%' ESCAPE '\\')
|
|
107
107
|
AND (? IS NULL OR category = ?)
|
|
108
108
|
ORDER BY id ASC
|
|
@@ -116,4 +116,4 @@ import{createRequire as Y}from"node:module";import{existsSync as G,unlinkSync as
|
|
|
116
116
|
FROM tool_calls WHERE session_id = ?`),t(i.getToolCallByTool,`SELECT tool, calls, bytes_returned
|
|
117
117
|
FROM tool_calls WHERE session_id = ? ORDER BY calls DESC`),t(i.getEventBytesSummary,`SELECT COALESCE(SUM(bytes_avoided), 0) AS bytes_avoided,
|
|
118
118
|
COALESCE(SUM(bytes_returned), 0) AS bytes_returned
|
|
119
|
-
FROM session_events WHERE session_id = ?`)}insertEvent(t,e,s="PostToolUse",r,o){let a=p("sha256").update(e.data).digest("hex").slice(0,16).toUpperCase(),c=String(r?.projectDir??e.project_dir??
|
|
119
|
+
FROM session_events WHERE session_id = ?`)}insertEvent(t,e,s="PostToolUse",r,o){let a=p("sha256").update(e.data).digest("hex").slice(0,16).toUpperCase(),c=String(r?.projectDir??e.project_dir??this._getSessionProjectDir(t)).trim(),d=String(r?.source??e.attribution_source??"unknown"),u=Number(r?.confidence??e.attribution_confidence??0),T=Number.isFinite(u)?Math.max(0,Math.min(1,u)):0,m=h(o?.bytesAvoided),S=h(o?.bytesReturned),L=this.db.transaction(()=>{if(this.stmt(i.checkDuplicate).get(t,P,e.type,a))return;this.stmt(i.getEventCount).get(t).cnt>=B&&this.stmt(i.evictLowestPriority).run(t),this.stmt(i.insertEvent).run(t,e.type,e.category,e.priority,e.data,c,d,T,m,S,s,a),this.stmt(i.updateMetaLastEvent).run(t)});this.withRetry(()=>L())}bulkInsertEvents(t,e,s="PostToolUse",r,o){if(!e||e.length===0)return;if(e.length===1){this.insertEvent(t,e[0],s,r?.[0],o?.[0]);return}let a=e.map((d,u)=>{let T=p("sha256").update(d.data).digest("hex").slice(0,16).toUpperCase(),m=r?.[u],S=String(m?.projectDir??d.project_dir??this._getSessionProjectDir(t)??"").trim(),L=String(m?.source??d.attribution_source??"unknown"),R=Number(m?.confidence??d.attribution_confidence??0),O=Number.isFinite(R)?Math.max(0,Math.min(1,R)):0,w=o?.[u],H=h(w?.bytesAvoided),$=h(w?.bytesReturned);return{event:d,dataHash:T,projectDir:S,attributionSource:L,attributionConfidence:O,bytesAvoided:H,bytesReturned:$}}),c=this.db.transaction(()=>{let d=this.stmt(i.getEventCount).get(t).cnt;for(let u of a)this.stmt(i.checkDuplicate).get(t,P,u.event.type,u.dataHash)||(d>=B?this.stmt(i.evictLowestPriority).run(t):d++,this.stmt(i.insertEvent).run(t,u.event.type,u.event.category,u.event.priority,u.event.data,u.projectDir,u.attributionSource,u.attributionConfidence,u.bytesAvoided,u.bytesReturned,s,u.dataHash));this.stmt(i.updateMetaLastEvent).run(t)});this.withRetry(()=>c())}getEvents(t,e){let s=e?.limit??1e3,r=e?.type,o=e?.minPriority;return r&&o!==void 0?this.stmt(i.getEventsByTypeAndPriority).all(t,r,o,s):r?this.stmt(i.getEventsByType).all(t,r,s):o!==void 0?this.stmt(i.getEventsByPriority).all(t,o,s):this.stmt(i.getEvents).all(t,s)}getEventCount(t){return this.stmt(i.getEventCount).get(t).cnt}getEventBytesSummary(t){let e=this.stmt(i.getEventBytesSummary).get(t);return{bytesAvoided:Number(e?.bytes_avoided??0),bytesReturned:Number(e?.bytes_returned??0)}}getLatestAttributedProjectDir(t){return this.stmt(i.getLatestAttributedProject).get(t)?.project_dir||null}_getSessionProjectDir(t){try{return this.db.prepare("SELECT project_dir FROM session_meta WHERE session_id = ?").get(t)?.project_dir||""}catch{return""}}searchEvents(t,e,s,r){try{let o=t.replace(/[%_]/g,c=>"\\"+c),a=r??null;return this.stmt(i.searchEvents).all(s,o,o,a,a,e)}catch{return[]}}ensureSession(t,e){this.stmt(i.ensureSession).run(t,e)}getSessionStats(t){return this.stmt(i.getSessionStats).get(t)??null}incrementCompactCount(t){this.stmt(i.incrementCompactCount).run(t)}upsertResume(t,e,s){this.stmt(i.upsertResume).run(t,e,s??0)}getResume(t){return this.stmt(i.getResume).get(t)??null}markResumeConsumed(t){this.stmt(i.markResumeConsumed).run(t)}claimLatestUnconsumedResume(t){let e=this.stmt(i.claimLatestUnconsumedResume).get(t);return e?{sessionId:e.session_id,snapshot:e.snapshot}:null}getLatestSessionId(){try{return this.db.prepare("SELECT session_id FROM session_meta ORDER BY started_at DESC LIMIT 1").get()?.session_id??null}catch{return null}}incrementToolCall(t,e,s=0){let r=Number.isFinite(s)&&s>0?Math.round(s):0;try{this.stmt(i.incrementToolCall).run(t,e,r)}catch{}}getToolCallStats(t){try{let e=this.stmt(i.getToolCallTotals).get(t),s=this.stmt(i.getToolCallByTool).all(t),r={};for(let o of s)r[o.tool]={calls:o.calls,bytesReturned:o.bytes_returned};return{totalCalls:e?.calls??0,totalBytesReturned:e?.bytes_returned??0,byTool:r}}catch{return{totalCalls:0,totalBytesReturned:0,byTool:{}}}}deleteSession(t){this.db.transaction(()=>{this.stmt(i.deleteEvents).run(t),this.stmt(i.deleteResume).run(t),this.stmt(i.deleteMeta).run(t)})()}cleanupOldSessions(t=7){let e=`-${t}`,s=this.stmt(i.getOldSessions).all(e);for(let{session_id:r}of s)this.deleteSession(r);return s.length}};export{j as SessionDB,ht as _resetWorktreeSuffixCacheForTests,at as getWorktreeSuffix,W as hashProjectDirCanonical,X as hashProjectDirLegacy,g as normalizeWorktreePath,ft as resolveContentStorePath,bt as resolveSessionDbPath,ct as resolveSessionPath};
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.137",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.137",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
|
|
6
6
|
"author": "Mert Koseoğlu",
|