context-mode 1.0.104 → 1.0.106
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/README.md +2 -31
- package/build/executor.d.ts +1 -1
- package/build/executor.js +24 -11
- package/build/opencode-plugin.d.ts +23 -6
- package/build/opencode-plugin.js +72 -30
- package/build/server.d.ts +7 -2
- package/build/server.js +61 -34
- package/build/session/db.d.ts +23 -0
- package/build/session/db.js +45 -0
- package/cli.bundle.mjs +148 -139
- package/hooks/core/routing.mjs +3 -2
- package/hooks/session-db.bundle.mjs +13 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +136 -127
- package/skills/ctx-doctor/SKILL.md +3 -3
package/hooks/core/routing.mjs
CHANGED
|
@@ -289,9 +289,10 @@ export function routePreToolUse(toolName, toolInput, projectDir, platform, sessi
|
|
|
289
289
|
});
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
-
// Build tools (gradle, maven) → redirect to execute sandbox (Issue #38).
|
|
292
|
+
// Build tools (gradle, maven, sbt) → redirect to execute sandbox (Issue #38, #406).
|
|
293
293
|
// These produce extremely verbose output that should stay in sandbox.
|
|
294
|
-
|
|
294
|
+
// Word-boundary guard prevents matching `gradle-wrapper-config`, `mvnDocker`, etc.
|
|
295
|
+
if (/(^|\s|&&|\||\;)(\.\/gradlew|gradlew|gradle|\.\/mvnw|mvnw|mvn|\.\/sbt|sbt)(\s|$)/i.test(stripped)) {
|
|
295
296
|
const safeCmd = command.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
296
297
|
return mcpRedirect({
|
|
297
298
|
action: "modify",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as v,renameSync as M}from"node:fs";import{tmpdir as x}from"node:os";import{join as F}from"node:path";var g=class{#t;constructor(t){this.#t=t}pragma(t){let
|
|
1
|
+
import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as v,renameSync as M}from"node:fs";import{tmpdir as x}from"node:os";import{join as F}from"node:path";var g=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()}},h=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()}},d=null;function B(){if(!d){let i=I(import.meta.url);if(globalThis.Bun){let t=i(["bun","sqlite"].join(":")).Database;d=function(s,r){let o=new t(s,{readonly:r?.readonly,create:!0}),a=new g(o);return r?.timeout&&a.pragma(`busy_timeout = ${r.timeout}`),a}}else if(process.platform==="linux")try{let{DatabaseSync:t}=i(["node","sqlite"].join(":"));d=function(s,r){let o=new t(s,{readOnly:r?.readonly??!1});return new h(o)}}catch{d=i("better-sqlite3")}else d=i("better-sqlite3")}return d}function b(i){i.pragma("journal_mode = WAL"),i.pragma("synchronous = NORMAL");try{i.pragma("mmap_size = 268435456")}catch{}}function N(i){if(!U(i))for(let t of["-wal","-shm"])try{v(i+t)}catch{}}function P(i){for(let t of["","-wal","-shm"])try{v(i+t)}catch{}}function y(i){try{i.pragma("wal_checkpoint(TRUNCATE)")}catch{}try{i.close()}catch{}}function C(i="context-mode"){return F(x(),`${i}-${process.pid}.db`)}function k(i,t=[100,500,2e3]){let e;for(let s=0;s<=t.length;s++)try{return i()}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 j(i){return i.includes("SQLITE_CORRUPT")||i.includes("SQLITE_NOTADB")||i.includes("database disk image is malformed")||i.includes("file is not a database")}function X(i){let t=Date.now();for(let e of["","-wal","-shm"])try{M(i+e,`${i}${e}.corrupt-${t}`)}catch{}}var m=Symbol.for("__context_mode_live_dbs__"),p=(()=>{let i=globalThis;return i[m]||(i[m]=new Set,process.on("exit",()=>{for(let t of i[m])y(t);i[m].clear()})),i[m]})(),T=class{#t;#e;constructor(t){let e=B();this.#t=t,N(t);let s;try{s=new e(t,{timeout:3e4}),b(s)}catch(r){let o=r instanceof Error?r.message:String(r);if(j(o)){X(t),N(t);try{s=new e(t,{timeout:3e4}),b(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,p.add(this.#e),this.initSchema(),this.prepareStatements()}get db(){return this.#e}get dbPath(){return this.#t}close(){p.delete(this.#e),y(this.#e)}withRetry(t){return k(t)}cleanup(){p.delete(this.#e),y(this.#e),P(this.#t)}};import{createHash as f}from"node:crypto";import{execFileSync as W}from"node:child_process";var l;function z(){let i=process.env.CONTEXT_MODE_SESSION_SUFFIX,t=process.cwd();if(l&&l.cwd===t&&l.envSuffix===i)return l.suffix;let e="";if(i!==void 0)e=i?`__${i}`:"";else try{let s=W("git",["worktree","list","--porcelain"],{encoding:"utf-8",timeout:2e3,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).find(r=>r.startsWith("worktree "))?.replace("worktree ","")?.trim();s&&t!==s&&(e=`__${f("sha256").update(t).digest("hex").slice(0,8)}`)}catch{}return l={cwd:t,envSuffix:i,suffix:e},e}function J(){l=void 0}var D=1e3,O=5,n={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"},A=class extends T{constructor(t){super(t?.dbPath??C("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,
|
|
@@ -46,7 +46,7 @@ import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as
|
|
|
46
46
|
);
|
|
47
47
|
|
|
48
48
|
CREATE INDEX IF NOT EXISTS idx_tool_calls_session ON tool_calls(session_id);
|
|
49
|
-
`);try{let t=this.db.pragma("table_xinfo(session_events)"),
|
|
49
|
+
`);try{let t=this.db.pragma("table_xinfo(session_events)"),e=new Set(t.map(s=>s.name));e.has("project_dir")||this.db.exec("ALTER TABLE session_events ADD COLUMN project_dir TEXT NOT NULL DEFAULT ''"),e.has("attribution_source")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_source TEXT NOT NULL DEFAULT 'unknown'"),e.has("attribution_confidence")||this.db.exec("ALTER TABLE session_events ADD COLUMN attribution_confidence REAL NOT NULL DEFAULT 0"),this.db.exec("CREATE INDEX IF NOT EXISTS idx_session_events_project ON session_events(session_id, project_dir)")}catch{}}prepareStatements(){this.stmts=new Map;let t=(e,s)=>{this.stmts.set(e,this.db.prepare(s))};t(n.insertEvent,`INSERT INTO session_events (
|
|
50
50
|
session_id, type, category, priority, data,
|
|
51
51
|
project_dir, attribution_source, attribution_confidence,
|
|
52
52
|
source_hook, data_hash
|
|
@@ -84,7 +84,16 @@ import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as
|
|
|
84
84
|
snapshot = excluded.snapshot,
|
|
85
85
|
event_count = excluded.event_count,
|
|
86
86
|
created_at = datetime('now'),
|
|
87
|
-
consumed = 0`),t(n.getResume,"SELECT snapshot, event_count, consumed FROM session_resume WHERE session_id = ?"),t(n.markResumeConsumed,"UPDATE session_resume SET consumed = 1 WHERE session_id = ?"),t(n.
|
|
87
|
+
consumed = 0`),t(n.getResume,"SELECT snapshot, event_count, consumed FROM session_resume WHERE session_id = ?"),t(n.markResumeConsumed,"UPDATE session_resume SET consumed = 1 WHERE session_id = ?"),t(n.claimLatestUnconsumedResume,`UPDATE session_resume
|
|
88
|
+
SET consumed = 1
|
|
89
|
+
WHERE id = (
|
|
90
|
+
SELECT id FROM session_resume
|
|
91
|
+
WHERE consumed = 0
|
|
92
|
+
AND session_id != ?
|
|
93
|
+
ORDER BY created_at DESC, id DESC
|
|
94
|
+
LIMIT 1
|
|
95
|
+
)
|
|
96
|
+
RETURNING session_id, snapshot`),t(n.deleteEvents,"DELETE FROM session_events WHERE session_id = ?"),t(n.deleteMeta,"DELETE FROM session_meta WHERE session_id = ?"),t(n.deleteResume,"DELETE FROM session_resume WHERE session_id = ?"),t(n.searchEvents,`SELECT id, session_id, category, type, data, created_at
|
|
88
97
|
FROM session_events
|
|
89
98
|
WHERE project_dir = ?
|
|
90
99
|
AND (data LIKE '%' || ? || '%' ESCAPE '\\' OR category LIKE '%' || ? || '%' ESCAPE '\\')
|
|
@@ -98,4 +107,4 @@ import{createRequire as I}from"node:module";import{existsSync as U,unlinkSync as
|
|
|
98
107
|
updated_at = datetime('now')`),t(n.getToolCallTotals,`SELECT COALESCE(SUM(calls), 0) AS calls,
|
|
99
108
|
COALESCE(SUM(bytes_returned), 0) AS bytes_returned
|
|
100
109
|
FROM tool_calls WHERE session_id = ?`),t(n.getToolCallByTool,`SELECT tool, calls, bytes_returned
|
|
101
|
-
FROM tool_calls WHERE session_id = ? ORDER BY calls DESC`)}insertEvent(t,s
|
|
110
|
+
FROM tool_calls WHERE session_id = ? ORDER BY calls DESC`)}insertEvent(t,e,s="PostToolUse",r){let o=f("sha256").update(e.data).digest("hex").slice(0,16).toUpperCase(),a=String(r?.projectDir??e.project_dir??"").trim(),c=String(r?.source??e.attribution_source??"unknown"),u=Number(r?.confidence??e.attribution_confidence??0),_=Number.isFinite(u)?Math.max(0,Math.min(1,u)):0,E=this.db.transaction(()=>{if(this.stmt(n.checkDuplicate).get(t,O,e.type,o))return;this.stmt(n.getEventCount).get(t).cnt>=D&&this.stmt(n.evictLowestPriority).run(t),this.stmt(n.insertEvent).run(t,e.type,e.category,e.priority,e.data,a,c,_,s,o),this.stmt(n.updateMetaLastEvent).run(t)});this.withRetry(()=>E())}bulkInsertEvents(t,e,s="PostToolUse",r){if(!e||e.length===0)return;if(e.length===1){this.insertEvent(t,e[0],s,r?.[0]);return}let o=e.map((c,u)=>{let _=f("sha256").update(c.data).digest("hex").slice(0,16).toUpperCase(),E=r?.[u],S=String(E?.projectDir??c.project_dir??"").trim(),L=String(E?.source??c.attribution_source??"unknown"),R=Number(E?.confidence??c.attribution_confidence??0),w=Number.isFinite(R)?Math.max(0,Math.min(1,R)):0;return{event:c,dataHash:_,projectDir:S,attributionSource:L,attributionConfidence:w}}),a=this.db.transaction(()=>{let c=this.stmt(n.getEventCount).get(t).cnt;for(let u of o)this.stmt(n.checkDuplicate).get(t,O,u.event.type,u.dataHash)||(c>=D?this.stmt(n.evictLowestPriority).run(t):c++,this.stmt(n.insertEvent).run(t,u.event.type,u.event.category,u.event.priority,u.event.data,u.projectDir,u.attributionSource,u.attributionConfidence,s,u.dataHash));this.stmt(n.updateMetaLastEvent).run(t)});this.withRetry(()=>a())}getEvents(t,e){let s=e?.limit??1e3,r=e?.type,o=e?.minPriority;return r&&o!==void 0?this.stmt(n.getEventsByTypeAndPriority).all(t,r,o,s):r?this.stmt(n.getEventsByType).all(t,r,s):o!==void 0?this.stmt(n.getEventsByPriority).all(t,o,s):this.stmt(n.getEvents).all(t,s)}getEventCount(t){return this.stmt(n.getEventCount).get(t).cnt}getLatestAttributedProjectDir(t){return this.stmt(n.getLatestAttributedProject).get(t)?.project_dir||null}searchEvents(t,e,s,r){try{let o=t.replace(/[%_]/g,c=>"\\"+c),a=r??null;return this.stmt(n.searchEvents).all(s,o,o,a,a,e)}catch{return[]}}ensureSession(t,e){this.stmt(n.ensureSession).run(t,e)}getSessionStats(t){return this.stmt(n.getSessionStats).get(t)??null}incrementCompactCount(t){this.stmt(n.incrementCompactCount).run(t)}upsertResume(t,e,s){this.stmt(n.upsertResume).run(t,e,s??0)}getResume(t){return this.stmt(n.getResume).get(t)??null}markResumeConsumed(t){this.stmt(n.markResumeConsumed).run(t)}claimLatestUnconsumedResume(t){let e=this.stmt(n.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(n.incrementToolCall).run(t,e,r)}catch{}}getToolCallStats(t){try{let e=this.stmt(n.getToolCallTotals).get(t),s=this.stmt(n.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(n.deleteEvents).run(t),this.stmt(n.deleteResume).run(t),this.stmt(n.deleteMeta).run(t)})()}cleanupOldSessions(t=7){let e=`-${t}`,s=this.stmt(n.getOldSessions).all(e);for(let{session_id:r}of s)this.deleteSession(r);return s.length}};export{A as SessionDB,J as _resetWorktreeSuffixCacheForTests,z as getWorktreeSuffix};
|
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.106",
|
|
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.106",
|
|
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",
|