context-mode 1.0.154 → 1.0.156
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/build/concurrency/runPool.d.ts +36 -0
- package/build/concurrency/runPool.js +51 -0
- package/build/openclaw/mcp-tools.d.ts +54 -0
- package/build/openclaw/mcp-tools.js +198 -0
- package/build/openclaw/workspace-router.d.ts +29 -0
- package/build/openclaw/workspace-router.js +64 -0
- package/build/openclaw-plugin.d.ts +130 -0
- package/build/openclaw-plugin.js +626 -0
- package/build/opencode-plugin.d.ts +122 -0
- package/build/opencode-plugin.js +372 -0
- package/build/pi-extension.d.ts +14 -0
- package/build/pi-extension.js +451 -0
- package/build/util/db-lock.d.ts +65 -0
- package/build/util/db-lock.js +166 -0
- package/cli.bundle.mjs +2 -2
- package/hooks/platform-bridge.mjs +209 -0
- package/hooks/session-loaders.mjs +28 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +2 -2
|
@@ -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.156"
|
|
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.156",
|
|
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.156",
|
|
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",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.156",
|
|
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.156",
|
|
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.156",
|
|
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",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic in-flight-capped worker pool.
|
|
3
|
+
*
|
|
4
|
+
* Used by:
|
|
5
|
+
* - runBatchCommands (ctx_batch_execute parallel branch)
|
|
6
|
+
* - runBatchFetch (ctx_fetch_and_index batch path)
|
|
7
|
+
*
|
|
8
|
+
* Returns Promise.allSettled-style results so one job's throw cannot
|
|
9
|
+
* strand siblings. Caller maps fulfilled/rejected per index. Output
|
|
10
|
+
* order is preserved by input index (not completion order).
|
|
11
|
+
*
|
|
12
|
+
* Designed to be the SINGLE concurrency primitive for the project —
|
|
13
|
+
* all "run N independent operations with at most M in flight" needs
|
|
14
|
+
* route here. Avoids the worker-pool copy-paste flagged in the
|
|
15
|
+
* concurrency PRD architectural review (finding G).
|
|
16
|
+
*/
|
|
17
|
+
export interface PoolJob<T> {
|
|
18
|
+
run(): Promise<T>;
|
|
19
|
+
}
|
|
20
|
+
export interface RunPoolOptions {
|
|
21
|
+
/** Hard concurrency cap (1-N). Auto-clamped to job count. */
|
|
22
|
+
concurrency: number;
|
|
23
|
+
/** Optional: also clamp by `os.cpus().length` (memory-pressure safety). Default false. */
|
|
24
|
+
capByCpuCount?: boolean;
|
|
25
|
+
/** Optional: per-settled callback (e.g. for progress reporting / metrics). */
|
|
26
|
+
onSettled?: (idx: number, result: PromiseSettledResult<unknown>) => void;
|
|
27
|
+
}
|
|
28
|
+
export interface RunPoolResult<T> {
|
|
29
|
+
/** Per-index settled result, ordered by input index. */
|
|
30
|
+
settled: PromiseSettledResult<T>[];
|
|
31
|
+
/** Concurrency actually used after all caps applied. */
|
|
32
|
+
effectiveConcurrency: number;
|
|
33
|
+
/** True when effectiveConcurrency < requested concurrency. */
|
|
34
|
+
capped: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function runPool<T>(jobs: PoolJob<T>[], opts: RunPoolOptions): Promise<RunPoolResult<T>>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic in-flight-capped worker pool.
|
|
3
|
+
*
|
|
4
|
+
* Used by:
|
|
5
|
+
* - runBatchCommands (ctx_batch_execute parallel branch)
|
|
6
|
+
* - runBatchFetch (ctx_fetch_and_index batch path)
|
|
7
|
+
*
|
|
8
|
+
* Returns Promise.allSettled-style results so one job's throw cannot
|
|
9
|
+
* strand siblings. Caller maps fulfilled/rejected per index. Output
|
|
10
|
+
* order is preserved by input index (not completion order).
|
|
11
|
+
*
|
|
12
|
+
* Designed to be the SINGLE concurrency primitive for the project —
|
|
13
|
+
* all "run N independent operations with at most M in flight" needs
|
|
14
|
+
* route here. Avoids the worker-pool copy-paste flagged in the
|
|
15
|
+
* concurrency PRD architectural review (finding G).
|
|
16
|
+
*/
|
|
17
|
+
import { cpus } from "node:os";
|
|
18
|
+
export async function runPool(jobs, opts) {
|
|
19
|
+
const { concurrency, capByCpuCount = false, onSettled } = opts;
|
|
20
|
+
if (jobs.length === 0) {
|
|
21
|
+
return { settled: [], effectiveConcurrency: 0, capped: false };
|
|
22
|
+
}
|
|
23
|
+
const requested = Math.max(1, concurrency);
|
|
24
|
+
const cpuCap = capByCpuCount ? Math.max(1, cpus().length) : requested;
|
|
25
|
+
const effectiveConcurrency = Math.min(requested, cpuCap, jobs.length);
|
|
26
|
+
const capped = effectiveConcurrency < requested;
|
|
27
|
+
const settled = new Array(jobs.length);
|
|
28
|
+
let nextIdx = 0;
|
|
29
|
+
async function worker() {
|
|
30
|
+
while (true) {
|
|
31
|
+
const idx = nextIdx++;
|
|
32
|
+
if (idx >= jobs.length)
|
|
33
|
+
return;
|
|
34
|
+
try {
|
|
35
|
+
const value = await jobs[idx].run();
|
|
36
|
+
settled[idx] = { status: "fulfilled", value };
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
settled[idx] = { status: "rejected", reason: err };
|
|
40
|
+
}
|
|
41
|
+
onSettled?.(idx, settled[idx]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const workers = [];
|
|
45
|
+
for (let w = 0; w < effectiveConcurrency; w++)
|
|
46
|
+
workers.push(worker());
|
|
47
|
+
// allSettled defends against any promise rejection escaping a worker
|
|
48
|
+
// (the worker already swallows its own errors, but this is belt-and-braces).
|
|
49
|
+
await Promise.allSettled(workers);
|
|
50
|
+
return { settled, effectiveConcurrency, capped };
|
|
51
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw MCP tool registry.
|
|
3
|
+
*
|
|
4
|
+
* Catalogs the 11 ctx_* tools that OpenClaw plugin must register via
|
|
5
|
+
* api.registerTool(...) so the routing block (which nudges agents toward
|
|
6
|
+
* ctx_execute, ctx_search, etc.) actually has tools to call. Without this,
|
|
7
|
+
* Phase 7 audit (v1.0.107-adapter-openclaw.json) flagged severity=CRITICAL —
|
|
8
|
+
* routing-block premise is broken when the named tools don't exist.
|
|
9
|
+
*
|
|
10
|
+
* Pattern mirrors the swarmvault MCP plugin
|
|
11
|
+
* (refs/plugin-examples/openclaw/swarmvault/packages/engine/src/mcp.ts:46-51):
|
|
12
|
+
* server.registerTool(name, { description, inputSchema }, handler)
|
|
13
|
+
*
|
|
14
|
+
* OpenClaw signature is slightly different — see building-plugins.md:116
|
|
15
|
+
* api.registerTool({ name, description, parameters: TypeBox, execute(id, params) })
|
|
16
|
+
*
|
|
17
|
+
* Tool handlers are intentionally thin shims that delegate to the bundled CLI
|
|
18
|
+
* (cli.bundle.mjs) — same fall-through pattern already used by ctx-doctor and
|
|
19
|
+
* ctx-upgrade slash commands. This keeps the plugin's blast radius minimal:
|
|
20
|
+
* we don't re-export the entire MCP server stack inside OpenClaw's process.
|
|
21
|
+
*
|
|
22
|
+
* The 11 tools mirror src/server.ts registerTool calls (lines 897, 1226, 1371,
|
|
23
|
+
* 1497, 2034, 2256, 2440, 2501, 2592, 2712, 2808).
|
|
24
|
+
*/
|
|
25
|
+
/** Minimal JSON-schema-like parameter spec accepted by OpenClaw registerTool. */
|
|
26
|
+
export interface OpenClawToolParameters {
|
|
27
|
+
type: "object";
|
|
28
|
+
properties: Record<string, {
|
|
29
|
+
type: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
}>;
|
|
32
|
+
required?: string[];
|
|
33
|
+
additionalProperties?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** Tool definition shape returned to OpenClaw via api.registerTool. */
|
|
36
|
+
export interface OpenClawToolDef {
|
|
37
|
+
name: string;
|
|
38
|
+
description: string;
|
|
39
|
+
parameters: OpenClawToolParameters;
|
|
40
|
+
execute: (id: string, params: Record<string, unknown>) => Promise<{
|
|
41
|
+
content: Array<{
|
|
42
|
+
type: "text";
|
|
43
|
+
text: string;
|
|
44
|
+
}>;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The 11 ctx_* tool definitions registered into OpenClaw via api.registerTool.
|
|
49
|
+
* Names + descriptions mirror src/server.ts registerTool blocks 1:1 so prompts
|
|
50
|
+
* referencing them (routing block, AGENTS.md) resolve to real callable tools.
|
|
51
|
+
*/
|
|
52
|
+
export declare const OPENCLAW_TOOL_DEFS: readonly OpenClawToolDef[];
|
|
53
|
+
/** Stable list of tool names — used by tests and manifest validation. */
|
|
54
|
+
export declare const OPENCLAW_TOOL_NAMES: readonly string[];
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw MCP tool registry.
|
|
3
|
+
*
|
|
4
|
+
* Catalogs the 11 ctx_* tools that OpenClaw plugin must register via
|
|
5
|
+
* api.registerTool(...) so the routing block (which nudges agents toward
|
|
6
|
+
* ctx_execute, ctx_search, etc.) actually has tools to call. Without this,
|
|
7
|
+
* Phase 7 audit (v1.0.107-adapter-openclaw.json) flagged severity=CRITICAL —
|
|
8
|
+
* routing-block premise is broken when the named tools don't exist.
|
|
9
|
+
*
|
|
10
|
+
* Pattern mirrors the swarmvault MCP plugin
|
|
11
|
+
* (refs/plugin-examples/openclaw/swarmvault/packages/engine/src/mcp.ts:46-51):
|
|
12
|
+
* server.registerTool(name, { description, inputSchema }, handler)
|
|
13
|
+
*
|
|
14
|
+
* OpenClaw signature is slightly different — see building-plugins.md:116
|
|
15
|
+
* api.registerTool({ name, description, parameters: TypeBox, execute(id, params) })
|
|
16
|
+
*
|
|
17
|
+
* Tool handlers are intentionally thin shims that delegate to the bundled CLI
|
|
18
|
+
* (cli.bundle.mjs) — same fall-through pattern already used by ctx-doctor and
|
|
19
|
+
* ctx-upgrade slash commands. This keeps the plugin's blast radius minimal:
|
|
20
|
+
* we don't re-export the entire MCP server stack inside OpenClaw's process.
|
|
21
|
+
*
|
|
22
|
+
* The 11 tools mirror src/server.ts registerTool calls (lines 897, 1226, 1371,
|
|
23
|
+
* 1497, 2034, 2256, 2440, 2501, 2592, 2712, 2808).
|
|
24
|
+
*/
|
|
25
|
+
/** Wrap any handler so failures become a well-formed text error rather than crashing. */
|
|
26
|
+
function safe(handler) {
|
|
27
|
+
return async (_id, params) => {
|
|
28
|
+
try {
|
|
29
|
+
return await handler(params ?? {});
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
33
|
+
return {
|
|
34
|
+
content: [
|
|
35
|
+
{
|
|
36
|
+
type: "text",
|
|
37
|
+
text: `[context-mode] tool error: ${message}`,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Stub handler — points users at the bundled CLI for full functionality. */
|
|
45
|
+
function cliRedirect(toolName) {
|
|
46
|
+
return safe(async () => ({
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: `[context-mode] ${toolName} is exposed via the bundled context-mode CLI. Run 'context-mode ${toolName}' or invoke the MCP server directly. This OpenClaw stub registers the tool name so the routing block remains valid; full execution requires the standalone MCP transport.`,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The 11 ctx_* tool definitions registered into OpenClaw via api.registerTool.
|
|
57
|
+
* Names + descriptions mirror src/server.ts registerTool blocks 1:1 so prompts
|
|
58
|
+
* referencing them (routing block, AGENTS.md) resolve to real callable tools.
|
|
59
|
+
*/
|
|
60
|
+
export const OPENCLAW_TOOL_DEFS = [
|
|
61
|
+
{
|
|
62
|
+
name: "ctx_execute",
|
|
63
|
+
description: "Execute code in a sandboxed subprocess. Only stdout enters context. Prefer over Bash for any command producing >20 lines.",
|
|
64
|
+
parameters: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
language: { type: "string", description: "Runtime language" },
|
|
68
|
+
code: { type: "string", description: "Source code to execute" },
|
|
69
|
+
timeout: { type: "number", description: "Max execution time in ms" },
|
|
70
|
+
},
|
|
71
|
+
required: ["language", "code"],
|
|
72
|
+
additionalProperties: true,
|
|
73
|
+
},
|
|
74
|
+
execute: cliRedirect("ctx_execute"),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "ctx_execute_file",
|
|
78
|
+
description: "Execute code with a file path. Only printed summary enters context — raw file stays in sandbox.",
|
|
79
|
+
parameters: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {
|
|
82
|
+
path: { type: "string", description: "File path" },
|
|
83
|
+
language: { type: "string", description: "Runtime language" },
|
|
84
|
+
code: { type: "string", description: "Source code" },
|
|
85
|
+
},
|
|
86
|
+
required: ["path", "language", "code"],
|
|
87
|
+
additionalProperties: true,
|
|
88
|
+
},
|
|
89
|
+
execute: cliRedirect("ctx_execute_file"),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "ctx_index",
|
|
93
|
+
description: "Store content in the FTS5 knowledge base for later search.",
|
|
94
|
+
parameters: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
content: { type: "string", description: "Text to index" },
|
|
98
|
+
source: { type: "string", description: "Descriptive source label" },
|
|
99
|
+
},
|
|
100
|
+
required: ["content", "source"],
|
|
101
|
+
additionalProperties: true,
|
|
102
|
+
},
|
|
103
|
+
execute: cliRedirect("ctx_index"),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "ctx_search",
|
|
107
|
+
description: "Query indexed content via FTS5. Pass all questions as an array in ONE call.",
|
|
108
|
+
parameters: {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties: {
|
|
111
|
+
queries: { type: "array", description: "Search queries" },
|
|
112
|
+
source: { type: "string", description: "Optional source filter" },
|
|
113
|
+
sort: { type: "string", description: "relevance | timeline" },
|
|
114
|
+
},
|
|
115
|
+
additionalProperties: true,
|
|
116
|
+
},
|
|
117
|
+
execute: cliRedirect("ctx_search"),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "ctx_fetch_and_index",
|
|
121
|
+
description: "Fetch a URL, chunk it, and index — raw HTML never enters context.",
|
|
122
|
+
parameters: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
url: { type: "string", description: "URL to fetch" },
|
|
126
|
+
source: { type: "string", description: "Source label for indexed chunks" },
|
|
127
|
+
},
|
|
128
|
+
required: ["url"],
|
|
129
|
+
additionalProperties: true,
|
|
130
|
+
},
|
|
131
|
+
execute: cliRedirect("ctx_fetch_and_index"),
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "ctx_batch_execute",
|
|
135
|
+
description: "Run multiple commands and search queries in ONE call. Primary research tool — replaces 30+ individual calls.",
|
|
136
|
+
parameters: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
commands: { type: "array", description: "Array of {label, command} objects" },
|
|
140
|
+
queries: { type: "array", description: "Search queries to run after indexing" },
|
|
141
|
+
},
|
|
142
|
+
additionalProperties: true,
|
|
143
|
+
},
|
|
144
|
+
execute: cliRedirect("ctx_batch_execute"),
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "ctx_stats",
|
|
148
|
+
description: "Show context-mode session statistics — token consumption and per-tool breakdown.",
|
|
149
|
+
parameters: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {},
|
|
152
|
+
additionalProperties: true,
|
|
153
|
+
},
|
|
154
|
+
execute: cliRedirect("ctx_stats"),
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "ctx_doctor",
|
|
158
|
+
description: "Run context-mode diagnostics — runtimes, hooks, FTS5, plugin registration.",
|
|
159
|
+
parameters: {
|
|
160
|
+
type: "object",
|
|
161
|
+
properties: {},
|
|
162
|
+
additionalProperties: true,
|
|
163
|
+
},
|
|
164
|
+
execute: cliRedirect("ctx_doctor"),
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: "ctx_upgrade",
|
|
168
|
+
description: "Upgrade context-mode to the latest version.",
|
|
169
|
+
parameters: {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {},
|
|
172
|
+
additionalProperties: true,
|
|
173
|
+
},
|
|
174
|
+
execute: cliRedirect("ctx_upgrade"),
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: "ctx_purge",
|
|
178
|
+
description: "Permanently delete all indexed content and reset session stats. Destructive.",
|
|
179
|
+
parameters: {
|
|
180
|
+
type: "object",
|
|
181
|
+
properties: {},
|
|
182
|
+
additionalProperties: true,
|
|
183
|
+
},
|
|
184
|
+
execute: cliRedirect("ctx_purge"),
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "ctx_insight",
|
|
188
|
+
description: "Open the context-mode Insight analytics dashboard in the browser.",
|
|
189
|
+
parameters: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {},
|
|
192
|
+
additionalProperties: true,
|
|
193
|
+
},
|
|
194
|
+
execute: cliRedirect("ctx_insight"),
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
/** Stable list of tool names — used by tests and manifest validation. */
|
|
198
|
+
export const OPENCLAW_TOOL_NAMES = OPENCLAW_TOOL_DEFS.map((def) => def.name);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract the agent workspace path from tool call params.
|
|
3
|
+
* Looks for /openclaw/workspace-<name> patterns in cwd, file_path, and command.
|
|
4
|
+
* Returns the workspace root (e.g. "/openclaw/workspace-trainer") or null.
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractWorkspace(params: Record<string, unknown>): string | null;
|
|
7
|
+
/**
|
|
8
|
+
* Maps agent workspaces to sessionIds using sessionKey convention.
|
|
9
|
+
* sessionKey pattern: "agent:<name>:main" → workspace "/openclaw/workspace-<name>"
|
|
10
|
+
*
|
|
11
|
+
* Why this exists alongside per-session closures:
|
|
12
|
+
* Each register() call creates its own closure with its own sessionId, which
|
|
13
|
+
* naturally isolates sessions. The WorkspaceRouter acts as a safety net for
|
|
14
|
+
* after_tool_call events where OpenClaw may deliver the event to the wrong
|
|
15
|
+
* closure (e.g. tool calls interleaving across agents). It resolves the correct
|
|
16
|
+
* sessionId from workspace paths in tool params, falling back to the closure
|
|
17
|
+
* sessionId when no workspace is detected.
|
|
18
|
+
*/
|
|
19
|
+
export declare class WorkspaceRouter {
|
|
20
|
+
private map;
|
|
21
|
+
/** Register a session from session_start event. */
|
|
22
|
+
registerSession(sessionKey: string, sessionId: string): void;
|
|
23
|
+
/** Remove a session (e.g. on command:stop). */
|
|
24
|
+
removeSession(sessionKey: string): void;
|
|
25
|
+
/** Resolve sessionId from tool call params. Returns null if no match. */
|
|
26
|
+
resolveSessionId(params: Record<string, unknown>): string | null;
|
|
27
|
+
/** Derive workspace path from sessionKey. */
|
|
28
|
+
private workspaceFromKey;
|
|
29
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract the agent workspace path from tool call params.
|
|
3
|
+
* Looks for /openclaw/workspace-<name> patterns in cwd, file_path, and command.
|
|
4
|
+
* Returns the workspace root (e.g. "/openclaw/workspace-trainer") or null.
|
|
5
|
+
*/
|
|
6
|
+
export function extractWorkspace(params) {
|
|
7
|
+
// Priority: cwd > file_path > command (most specific first)
|
|
8
|
+
const sources = [
|
|
9
|
+
params.cwd,
|
|
10
|
+
params.file_path,
|
|
11
|
+
params.command,
|
|
12
|
+
].filter((v) => typeof v === "string");
|
|
13
|
+
for (const src of sources) {
|
|
14
|
+
const match = src.match(/\/openclaw\/workspace-[a-zA-Z0-9_-]+/);
|
|
15
|
+
if (match)
|
|
16
|
+
return match[0];
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Maps agent workspaces to sessionIds using sessionKey convention.
|
|
22
|
+
* sessionKey pattern: "agent:<name>:main" → workspace "/openclaw/workspace-<name>"
|
|
23
|
+
*
|
|
24
|
+
* Why this exists alongside per-session closures:
|
|
25
|
+
* Each register() call creates its own closure with its own sessionId, which
|
|
26
|
+
* naturally isolates sessions. The WorkspaceRouter acts as a safety net for
|
|
27
|
+
* after_tool_call events where OpenClaw may deliver the event to the wrong
|
|
28
|
+
* closure (e.g. tool calls interleaving across agents). It resolves the correct
|
|
29
|
+
* sessionId from workspace paths in tool params, falling back to the closure
|
|
30
|
+
* sessionId when no workspace is detected.
|
|
31
|
+
*/
|
|
32
|
+
export class WorkspaceRouter {
|
|
33
|
+
// workspace path → sessionId
|
|
34
|
+
map = new Map();
|
|
35
|
+
/** Register a session from session_start event. */
|
|
36
|
+
registerSession(sessionKey, sessionId) {
|
|
37
|
+
const workspace = this.workspaceFromKey(sessionKey);
|
|
38
|
+
if (workspace) {
|
|
39
|
+
this.map.set(workspace, sessionId);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Remove a session (e.g. on command:stop). */
|
|
43
|
+
removeSession(sessionKey) {
|
|
44
|
+
const workspace = this.workspaceFromKey(sessionKey);
|
|
45
|
+
if (workspace) {
|
|
46
|
+
this.map.delete(workspace);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Resolve sessionId from tool call params. Returns null if no match. */
|
|
50
|
+
resolveSessionId(params) {
|
|
51
|
+
const workspace = extractWorkspace(params);
|
|
52
|
+
if (!workspace)
|
|
53
|
+
return null;
|
|
54
|
+
return this.map.get(workspace) ?? null;
|
|
55
|
+
}
|
|
56
|
+
/** Derive workspace path from sessionKey. */
|
|
57
|
+
workspaceFromKey(key) {
|
|
58
|
+
// Pattern: "agent:<name>:main" or "agent:<name>:<channel>"
|
|
59
|
+
const match = key.match(/^agent:([^:]+):/);
|
|
60
|
+
if (!match)
|
|
61
|
+
return null;
|
|
62
|
+
return `/openclaw/workspace-${match[1]}`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw TypeScript plugin entry point for context-mode.
|
|
3
|
+
*
|
|
4
|
+
* Exports an object with { id, name, configSchema, register(api) } for
|
|
5
|
+
* declarative metadata and config validation before code execution.
|
|
6
|
+
*
|
|
7
|
+
* register(api) registers:
|
|
8
|
+
* - before_tool_call hook — Routing enforcement (deny/modify/passthrough)
|
|
9
|
+
* - after_tool_call hook — Session event capture
|
|
10
|
+
* - command:new hook — Session initialization and cleanup
|
|
11
|
+
* - session_start hook — Re-key DB session to OpenClaw's session ID
|
|
12
|
+
* - before_compaction hook — Flush events to resume snapshot
|
|
13
|
+
* - after_compaction hook — Increment compact count
|
|
14
|
+
* - before_prompt_build (p=10) — Resume snapshot injection into system context
|
|
15
|
+
* - before_prompt_build (p=5) — Routing instruction injection into system context
|
|
16
|
+
* - context-mode engine — Context engine with compaction management
|
|
17
|
+
* - /ctx-stats command — Auto-reply command for session statistics
|
|
18
|
+
* - /ctx-doctor command — Auto-reply command for diagnostics
|
|
19
|
+
* - /ctx-upgrade command — Auto-reply command for upgrade
|
|
20
|
+
*
|
|
21
|
+
* Loaded by OpenClaw via: openclaw.extensions entry in package.json
|
|
22
|
+
*
|
|
23
|
+
* OpenClaw plugin paradigm:
|
|
24
|
+
* - Plugins export { id, name, configSchema, register(api) } for metadata
|
|
25
|
+
* - api.registerHook() for event-driven hooks
|
|
26
|
+
* - api.on() for typed lifecycle hooks
|
|
27
|
+
* - api.registerContextEngine() for compaction ownership
|
|
28
|
+
* - api.registerCommand() for auto-reply slash commands
|
|
29
|
+
* - Plugins run in-process with the Gateway (trusted code)
|
|
30
|
+
*/
|
|
31
|
+
import type { OpenClawToolDef } from "./openclaw/mcp-tools.js";
|
|
32
|
+
/** Context for auto-reply command handlers. */
|
|
33
|
+
interface CommandContext {
|
|
34
|
+
senderId?: string;
|
|
35
|
+
channel?: string;
|
|
36
|
+
isAuthorizedSender?: boolean;
|
|
37
|
+
args?: string;
|
|
38
|
+
commandBody?: string;
|
|
39
|
+
config?: Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
/** OpenClaw plugin API provided to the register function. */
|
|
42
|
+
interface OpenClawPluginApi {
|
|
43
|
+
registerHook(event: string, handler: (...args: unknown[]) => unknown, meta: {
|
|
44
|
+
name: string;
|
|
45
|
+
description: string;
|
|
46
|
+
}): void;
|
|
47
|
+
/**
|
|
48
|
+
* Register a typed lifecycle hook.
|
|
49
|
+
* Supported names: "session_start", "before_compaction", "after_compaction",
|
|
50
|
+
* "before_prompt_build"
|
|
51
|
+
*/
|
|
52
|
+
on(event: string, handler: (...args: unknown[]) => unknown, opts?: {
|
|
53
|
+
priority?: number;
|
|
54
|
+
}): void;
|
|
55
|
+
registerContextEngine(id: string, factory: () => ContextEngineInstance): void;
|
|
56
|
+
registerCommand?(cmd: {
|
|
57
|
+
name: string;
|
|
58
|
+
description: string;
|
|
59
|
+
acceptsArgs?: boolean;
|
|
60
|
+
requireAuth?: boolean;
|
|
61
|
+
handler: (ctx: CommandContext) => {
|
|
62
|
+
text: string;
|
|
63
|
+
} | Promise<{
|
|
64
|
+
text: string;
|
|
65
|
+
}>;
|
|
66
|
+
}): void;
|
|
67
|
+
registerCli?(factory: (ctx: {
|
|
68
|
+
program: unknown;
|
|
69
|
+
}) => void, meta: {
|
|
70
|
+
commands: string[];
|
|
71
|
+
}): void;
|
|
72
|
+
/**
|
|
73
|
+
* Register an agent tool (OpenClaw native registerTool) — see
|
|
74
|
+
* refs/platforms/openclaw/docs/plugins/building-plugins.md:116. Optional in
|
|
75
|
+
* the type so we degrade silently on legacy hosts that pre-date this API.
|
|
76
|
+
*/
|
|
77
|
+
registerTool?(tool: OpenClawToolDef, opts?: {
|
|
78
|
+
optional?: boolean;
|
|
79
|
+
}): void;
|
|
80
|
+
logger?: {
|
|
81
|
+
info: (...args: unknown[]) => void;
|
|
82
|
+
error: (...args: unknown[]) => void;
|
|
83
|
+
debug?: (...args: unknown[]) => void;
|
|
84
|
+
warn?: (...args: unknown[]) => void;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Context engine instance returned by the factory. */
|
|
88
|
+
interface ContextEngineInstance {
|
|
89
|
+
info: {
|
|
90
|
+
id: string;
|
|
91
|
+
name: string;
|
|
92
|
+
ownsCompaction: boolean;
|
|
93
|
+
};
|
|
94
|
+
ingest(data: unknown): Promise<{
|
|
95
|
+
ingested: boolean;
|
|
96
|
+
}>;
|
|
97
|
+
assemble(ctx: {
|
|
98
|
+
messages: unknown[];
|
|
99
|
+
}): Promise<{
|
|
100
|
+
messages: unknown[];
|
|
101
|
+
estimatedTokens: number;
|
|
102
|
+
}>;
|
|
103
|
+
compact(): Promise<{
|
|
104
|
+
ok: boolean;
|
|
105
|
+
compacted: boolean;
|
|
106
|
+
}>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* OpenClaw plugin definition. The object form provides declarative metadata
|
|
110
|
+
* (id, name, configSchema) that OpenClaw can read without executing code.
|
|
111
|
+
* register() is called once per agent session with a fresh api object.
|
|
112
|
+
* Each call creates isolated closures (db, sessionId, hooks) — no shared state.
|
|
113
|
+
*/
|
|
114
|
+
declare const _default: {
|
|
115
|
+
id: string;
|
|
116
|
+
name: string;
|
|
117
|
+
configSchema: {
|
|
118
|
+
type: "object";
|
|
119
|
+
properties: {
|
|
120
|
+
enabled: {
|
|
121
|
+
type: "boolean";
|
|
122
|
+
default: boolean;
|
|
123
|
+
description: string;
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
additionalProperties: boolean;
|
|
127
|
+
};
|
|
128
|
+
register(api: OpenClawPluginApi): void;
|
|
129
|
+
};
|
|
130
|
+
export default _default;
|