context-mode 1.0.21 → 1.0.23
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 +4 -2
- package/.openclaw-plugin/index.ts +11 -0
- package/.openclaw-plugin/openclaw.plugin.json +23 -0
- package/.openclaw-plugin/package.json +28 -0
- package/README.md +165 -26
- package/build/adapters/antigravity/index.d.ts +49 -0
- package/build/adapters/antigravity/index.js +217 -0
- package/build/adapters/client-map.d.ts +10 -0
- package/build/adapters/client-map.js +18 -0
- package/build/adapters/detect.d.ts +8 -1
- package/build/adapters/detect.js +58 -1
- package/build/adapters/kiro/hooks.d.ts +32 -0
- package/build/adapters/kiro/hooks.js +47 -0
- package/build/adapters/kiro/index.d.ts +50 -0
- package/build/adapters/kiro/index.js +325 -0
- package/build/adapters/openclaw/config.d.ts +8 -0
- package/build/adapters/openclaw/config.js +8 -0
- package/build/adapters/openclaw/hooks.d.ts +50 -0
- package/build/adapters/openclaw/hooks.js +61 -0
- package/build/adapters/openclaw/index.d.ts +51 -0
- package/build/adapters/openclaw/index.js +459 -0
- package/build/adapters/openclaw/session-db.d.ts +55 -0
- package/build/adapters/openclaw/session-db.js +88 -0
- package/build/adapters/types.d.ts +1 -1
- package/build/cli.js +5 -3
- package/build/executor.js +99 -112
- package/build/openclaw/workspace-router.d.ts +29 -0
- package/build/openclaw/workspace-router.js +64 -0
- package/build/openclaw-plugin.d.ts +121 -0
- package/build/openclaw-plugin.js +525 -0
- package/build/server.js +45 -10
- package/build/session/db.d.ts +9 -0
- package/build/session/db.js +38 -0
- package/cli.bundle.mjs +136 -124
- package/configs/antigravity/GEMINI.md +58 -0
- package/configs/antigravity/mcp_config.json +7 -0
- package/configs/kiro/mcp_config.json +7 -0
- package/configs/openclaw/AGENTS.md +58 -0
- package/configs/openclaw/openclaw.json +13 -0
- package/hooks/core/routing.mjs +16 -8
- package/hooks/kiro/posttooluse.mjs +58 -0
- package/hooks/kiro/pretooluse.mjs +63 -0
- package/hooks/posttooluse.mjs +6 -5
- package/hooks/precompact.mjs +5 -4
- package/hooks/session-db.bundle.mjs +57 -0
- package/hooks/session-extract.bundle.mjs +1 -0
- package/hooks/session-helpers.mjs +41 -3
- package/hooks/session-loaders.mjs +28 -0
- package/hooks/session-snapshot.bundle.mjs +14 -0
- package/hooks/sessionstart.mjs +6 -5
- package/hooks/userpromptsubmit.mjs +6 -5
- package/hooks/vscode-copilot/posttooluse.mjs +5 -4
- package/hooks/vscode-copilot/precompact.mjs +5 -4
- package/hooks/vscode-copilot/sessionstart.mjs +5 -4
- package/openclaw.plugin.json +23 -0
- package/package.json +13 -2
- package/server.bundle.mjs +94 -82
- package/start.mjs +1 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/openclaw/hooks — OpenClaw hook definitions and validators.
|
|
3
|
+
*
|
|
4
|
+
* Defines the hook types and validation helpers specific to OpenClaw's
|
|
5
|
+
* TypeScript plugin paradigm. This module is used by:
|
|
6
|
+
* - CLI setup/upgrade commands (to configure plugin in openclaw.json)
|
|
7
|
+
* - Doctor command (to validate plugin configuration)
|
|
8
|
+
*
|
|
9
|
+
* OpenClaw hook system reference:
|
|
10
|
+
* - I/O: TS plugin functions via api.registerHook() and api.on()
|
|
11
|
+
* - Hook events: tool_call:before, tool_call:after, command:new, command:reset
|
|
12
|
+
* - Lifecycle: session_start, before_compaction, after_compaction, before_prompt_build, before_model_resolve
|
|
13
|
+
* - Context engine: api.registerContextEngine() with ownsCompaction
|
|
14
|
+
* - Blocking: return { block: true, blockReason } from tool_call:before
|
|
15
|
+
* - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
|
|
16
|
+
*/
|
|
17
|
+
// ─────────────────────────────────────────────────────────
|
|
18
|
+
// Hook type constants
|
|
19
|
+
// ─────────────────────────────────────────────────────────
|
|
20
|
+
/** OpenClaw hook event names (registered via api.registerHook). */
|
|
21
|
+
export const HOOK_EVENTS = {
|
|
22
|
+
TOOL_CALL_BEFORE: "tool_call:before",
|
|
23
|
+
TOOL_CALL_AFTER: "tool_call:after",
|
|
24
|
+
COMMAND_NEW: "command:new",
|
|
25
|
+
COMMAND_RESET: "command:reset",
|
|
26
|
+
COMMAND_STOP: "command:stop",
|
|
27
|
+
};
|
|
28
|
+
/** OpenClaw lifecycle hook names (registered via api.on). */
|
|
29
|
+
export const LIFECYCLE_HOOKS = {
|
|
30
|
+
SESSION_START: "session_start",
|
|
31
|
+
BEFORE_COMPACTION: "before_compaction",
|
|
32
|
+
AFTER_COMPACTION: "after_compaction",
|
|
33
|
+
BEFORE_PROMPT_BUILD: "before_prompt_build",
|
|
34
|
+
BEFORE_MODEL_RESOLVE: "before_model_resolve",
|
|
35
|
+
BEFORE_AGENT_START: "before_agent_start",
|
|
36
|
+
};
|
|
37
|
+
// ─────────────────────────────────────────────────────────
|
|
38
|
+
// Hook validation
|
|
39
|
+
// ─────────────────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Required hooks that must be active for context-mode to function.
|
|
42
|
+
* OpenClaw registers these via api.registerHook() in the plugin entry point.
|
|
43
|
+
*/
|
|
44
|
+
export const REQUIRED_HOOKS = [
|
|
45
|
+
HOOK_EVENTS.TOOL_CALL_BEFORE,
|
|
46
|
+
HOOK_EVENTS.TOOL_CALL_AFTER,
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Optional hooks that enhance functionality but aren't critical.
|
|
50
|
+
* command:new provides session cleanup; context engine handles compaction.
|
|
51
|
+
*/
|
|
52
|
+
export const OPTIONAL_HOOKS = [
|
|
53
|
+
HOOK_EVENTS.COMMAND_NEW,
|
|
54
|
+
];
|
|
55
|
+
/**
|
|
56
|
+
* Check if a plugin entry is the context-mode plugin.
|
|
57
|
+
* OpenClaw plugins are registered by id in plugins.entries.
|
|
58
|
+
*/
|
|
59
|
+
export function isContextModePlugin(pluginId) {
|
|
60
|
+
return pluginId.includes("context-mode");
|
|
61
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/openclaw — OpenClaw platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Implements HookAdapter for OpenClaw's TypeScript plugin paradigm.
|
|
5
|
+
*
|
|
6
|
+
* OpenClaw hook specifics:
|
|
7
|
+
* - I/O: TS plugin functions via api.registerHook() and api.on()
|
|
8
|
+
* - Hook events: tool_call:before, tool_call:after, command:new
|
|
9
|
+
* - Lifecycle: before_prompt_build (routing instruction injection)
|
|
10
|
+
* - Context engine: api.registerContextEngine() with ownsCompaction
|
|
11
|
+
* - Arg modification: mutate event.params in tool_call:before
|
|
12
|
+
* - Blocking: return { block: true, blockReason } from tool_call:before
|
|
13
|
+
* - Session ID: event context (no specific env var)
|
|
14
|
+
* - Project dir: process.env.OPENCLAW_PROJECT_DIR or process.cwd()
|
|
15
|
+
* - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
|
|
16
|
+
* - Session dir: ~/.openclaw/context-mode/sessions/
|
|
17
|
+
*/
|
|
18
|
+
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration, RoutingInstructionsConfig } from "../types.js";
|
|
19
|
+
export declare class OpenClawAdapter implements HookAdapter {
|
|
20
|
+
readonly name = "OpenClaw";
|
|
21
|
+
readonly paradigm: HookParadigm;
|
|
22
|
+
readonly capabilities: PlatformCapabilities;
|
|
23
|
+
parsePreToolUseInput(raw: unknown): PreToolUseEvent;
|
|
24
|
+
parsePostToolUseInput(raw: unknown): PostToolUseEvent;
|
|
25
|
+
parsePreCompactInput(raw: unknown): PreCompactEvent;
|
|
26
|
+
parseSessionStartInput(raw: unknown): SessionStartEvent;
|
|
27
|
+
formatPreToolUseResponse(response: PreToolUseResponse): unknown;
|
|
28
|
+
formatPostToolUseResponse(response: PostToolUseResponse): unknown;
|
|
29
|
+
formatPreCompactResponse(response: PreCompactResponse): unknown;
|
|
30
|
+
formatSessionStartResponse(response: SessionStartResponse): unknown;
|
|
31
|
+
getSettingsPath(): string;
|
|
32
|
+
getSessionDir(): string;
|
|
33
|
+
getSessionDBPath(projectDir: string): string;
|
|
34
|
+
getSessionEventsPath(projectDir: string): string;
|
|
35
|
+
generateHookConfig(_pluginRoot: string): HookRegistration;
|
|
36
|
+
readSettings(): Record<string, unknown> | null;
|
|
37
|
+
writeSettings(settings: Record<string, unknown>): void;
|
|
38
|
+
validateHooks(_pluginRoot: string): DiagnosticResult[];
|
|
39
|
+
checkPluginRegistration(): DiagnosticResult;
|
|
40
|
+
getInstalledVersion(): string;
|
|
41
|
+
configureAllHooks(_pluginRoot: string): string[];
|
|
42
|
+
backupSettings(): string | null;
|
|
43
|
+
setHookPermissions(_pluginRoot: string): string[];
|
|
44
|
+
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
45
|
+
getRoutingInstructionsConfig(): RoutingInstructionsConfig;
|
|
46
|
+
writeRoutingInstructions(projectDir: string, pluginRoot: string): string | null;
|
|
47
|
+
/**
|
|
48
|
+
* Extract session ID from OpenClaw hook input.
|
|
49
|
+
*/
|
|
50
|
+
private extractSessionId;
|
|
51
|
+
}
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/openclaw — OpenClaw platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Implements HookAdapter for OpenClaw's TypeScript plugin paradigm.
|
|
5
|
+
*
|
|
6
|
+
* OpenClaw hook specifics:
|
|
7
|
+
* - I/O: TS plugin functions via api.registerHook() and api.on()
|
|
8
|
+
* - Hook events: tool_call:before, tool_call:after, command:new
|
|
9
|
+
* - Lifecycle: before_prompt_build (routing instruction injection)
|
|
10
|
+
* - Context engine: api.registerContextEngine() with ownsCompaction
|
|
11
|
+
* - Arg modification: mutate event.params in tool_call:before
|
|
12
|
+
* - Blocking: return { block: true, blockReason } from tool_call:before
|
|
13
|
+
* - Session ID: event context (no specific env var)
|
|
14
|
+
* - Project dir: process.env.OPENCLAW_PROJECT_DIR or process.cwd()
|
|
15
|
+
* - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
|
|
16
|
+
* - Session dir: ~/.openclaw/context-mode/sessions/
|
|
17
|
+
*/
|
|
18
|
+
import { createHash } from "node:crypto";
|
|
19
|
+
import { readFileSync, writeFileSync, mkdirSync, copyFileSync, accessSync, constants, } from "node:fs";
|
|
20
|
+
import { resolve, join } from "node:path";
|
|
21
|
+
import { homedir } from "node:os";
|
|
22
|
+
// ─────────────────────────────────────────────────────────
|
|
23
|
+
// Hook constants (re-exported from hooks.ts)
|
|
24
|
+
// ─────────────────────────────────────────────────────────
|
|
25
|
+
import { HOOK_EVENTS as OPENCLAW_HOOK_EVENTS } from "./hooks.js";
|
|
26
|
+
// ─────────────────────────────────────────────────────────
|
|
27
|
+
// Adapter implementation
|
|
28
|
+
// ─────────────────────────────────────────────────────────
|
|
29
|
+
export class OpenClawAdapter {
|
|
30
|
+
name = "OpenClaw";
|
|
31
|
+
paradigm = "ts-plugin";
|
|
32
|
+
capabilities = {
|
|
33
|
+
preToolUse: true,
|
|
34
|
+
postToolUse: true,
|
|
35
|
+
preCompact: true, // via registerContextEngine with ownsCompaction
|
|
36
|
+
sessionStart: true, // via command:new hook
|
|
37
|
+
canModifyArgs: true,
|
|
38
|
+
canModifyOutput: false,
|
|
39
|
+
canInjectSessionContext: true, // via before_prompt_build lifecycle hook
|
|
40
|
+
};
|
|
41
|
+
// ── Input parsing ──────────────────────────────────────
|
|
42
|
+
parsePreToolUseInput(raw) {
|
|
43
|
+
const input = raw;
|
|
44
|
+
return {
|
|
45
|
+
toolName: input.toolName ?? input.tool_name ?? "",
|
|
46
|
+
toolInput: input.params ?? input.tool_input ?? {},
|
|
47
|
+
sessionId: this.extractSessionId(input),
|
|
48
|
+
projectDir: process.env.OPENCLAW_PROJECT_DIR || process.cwd(),
|
|
49
|
+
raw,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
parsePostToolUseInput(raw) {
|
|
53
|
+
const input = raw;
|
|
54
|
+
return {
|
|
55
|
+
toolName: input.toolName ?? input.tool_name ?? "",
|
|
56
|
+
toolInput: input.params ?? input.tool_input ?? {},
|
|
57
|
+
toolOutput: input.output ?? input.tool_output,
|
|
58
|
+
isError: input.isError ?? input.is_error,
|
|
59
|
+
sessionId: this.extractSessionId(input),
|
|
60
|
+
projectDir: process.env.OPENCLAW_PROJECT_DIR || process.cwd(),
|
|
61
|
+
raw,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
parsePreCompactInput(raw) {
|
|
65
|
+
const input = raw;
|
|
66
|
+
return {
|
|
67
|
+
sessionId: this.extractSessionId(input),
|
|
68
|
+
projectDir: process.env.OPENCLAW_PROJECT_DIR || process.cwd(),
|
|
69
|
+
raw,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
parseSessionStartInput(raw) {
|
|
73
|
+
const input = raw;
|
|
74
|
+
const rawSource = input.source ?? "startup";
|
|
75
|
+
let source;
|
|
76
|
+
switch (rawSource) {
|
|
77
|
+
case "compact":
|
|
78
|
+
source = "compact";
|
|
79
|
+
break;
|
|
80
|
+
case "resume":
|
|
81
|
+
source = "resume";
|
|
82
|
+
break;
|
|
83
|
+
case "clear":
|
|
84
|
+
source = "clear";
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
source = "startup";
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
sessionId: this.extractSessionId(input),
|
|
91
|
+
source,
|
|
92
|
+
projectDir: process.env.OPENCLAW_PROJECT_DIR || process.cwd(),
|
|
93
|
+
raw,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// ── Response formatting ────────────────────────────────
|
|
97
|
+
formatPreToolUseResponse(response) {
|
|
98
|
+
if (response.decision === "deny") {
|
|
99
|
+
// OpenClaw plugin paradigm: return { block, blockReason } to block
|
|
100
|
+
return {
|
|
101
|
+
block: true,
|
|
102
|
+
blockReason: response.reason ?? "Blocked by context-mode hook",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (response.decision === "modify" && response.updatedInput) {
|
|
106
|
+
// OpenClaw: mutate params in the event object
|
|
107
|
+
return { params: response.updatedInput };
|
|
108
|
+
}
|
|
109
|
+
if (response.decision === "ask") {
|
|
110
|
+
// OpenClaw: block for safety when user confirmation needed
|
|
111
|
+
return {
|
|
112
|
+
block: true,
|
|
113
|
+
blockReason: response.reason ?? "Action requires user confirmation (security policy)",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (response.decision === "context" && response.additionalContext) {
|
|
117
|
+
// OpenClaw supports context injection via before_prompt_build,
|
|
118
|
+
// but not inline in tool_call:before. Passthrough.
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
// "allow" — passthrough
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
formatPostToolUseResponse(response) {
|
|
125
|
+
const result = {};
|
|
126
|
+
if (response.additionalContext) {
|
|
127
|
+
result.additionalContext = response.additionalContext;
|
|
128
|
+
}
|
|
129
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
130
|
+
}
|
|
131
|
+
formatPreCompactResponse(response) {
|
|
132
|
+
// Context engine compact() returns { ok, compacted } — context is managed internally
|
|
133
|
+
return response.context ?? "";
|
|
134
|
+
}
|
|
135
|
+
formatSessionStartResponse(response) {
|
|
136
|
+
return response.context ?? "";
|
|
137
|
+
}
|
|
138
|
+
// ── Configuration ──────────────────────────────────────
|
|
139
|
+
getSettingsPath() {
|
|
140
|
+
// OpenClaw uses openclaw.json in the project root or ~/.openclaw/openclaw.json
|
|
141
|
+
return resolve("openclaw.json");
|
|
142
|
+
}
|
|
143
|
+
getSessionDir() {
|
|
144
|
+
const dir = join(homedir(), ".openclaw", "context-mode", "sessions");
|
|
145
|
+
mkdirSync(dir, { recursive: true });
|
|
146
|
+
return dir;
|
|
147
|
+
}
|
|
148
|
+
getSessionDBPath(projectDir) {
|
|
149
|
+
const hash = createHash("sha256")
|
|
150
|
+
.update(projectDir)
|
|
151
|
+
.digest("hex")
|
|
152
|
+
.slice(0, 16);
|
|
153
|
+
return join(this.getSessionDir(), `${hash}.db`);
|
|
154
|
+
}
|
|
155
|
+
getSessionEventsPath(projectDir) {
|
|
156
|
+
const hash = createHash("sha256")
|
|
157
|
+
.update(projectDir)
|
|
158
|
+
.digest("hex")
|
|
159
|
+
.slice(0, 16);
|
|
160
|
+
return join(this.getSessionDir(), `${hash}-events.md`);
|
|
161
|
+
}
|
|
162
|
+
generateHookConfig(_pluginRoot) {
|
|
163
|
+
// OpenClaw uses TS plugin paradigm — hooks are registered via
|
|
164
|
+
// api.registerHook() in the plugin entry point, not via config files.
|
|
165
|
+
// Return the hook name mapping for documentation purposes.
|
|
166
|
+
return {
|
|
167
|
+
[OPENCLAW_HOOK_EVENTS.TOOL_CALL_BEFORE]: [
|
|
168
|
+
{
|
|
169
|
+
matcher: "",
|
|
170
|
+
hooks: [
|
|
171
|
+
{
|
|
172
|
+
type: "plugin",
|
|
173
|
+
command: "context-mode",
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
[OPENCLAW_HOOK_EVENTS.TOOL_CALL_AFTER]: [
|
|
179
|
+
{
|
|
180
|
+
matcher: "",
|
|
181
|
+
hooks: [
|
|
182
|
+
{
|
|
183
|
+
type: "plugin",
|
|
184
|
+
command: "context-mode",
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
[OPENCLAW_HOOK_EVENTS.COMMAND_NEW]: [
|
|
190
|
+
{
|
|
191
|
+
matcher: "",
|
|
192
|
+
hooks: [
|
|
193
|
+
{
|
|
194
|
+
type: "plugin",
|
|
195
|
+
command: "context-mode",
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
readSettings() {
|
|
203
|
+
// Try project-local paths first, then global config
|
|
204
|
+
const paths = [
|
|
205
|
+
resolve("openclaw.json"),
|
|
206
|
+
resolve(".openclaw", "openclaw.json"),
|
|
207
|
+
join(homedir(), ".openclaw", "openclaw.json"),
|
|
208
|
+
];
|
|
209
|
+
for (const configPath of paths) {
|
|
210
|
+
try {
|
|
211
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
212
|
+
return JSON.parse(raw);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
writeSettings(settings) {
|
|
221
|
+
// Write to openclaw.json in current directory
|
|
222
|
+
const configPath = resolve("openclaw.json");
|
|
223
|
+
writeFileSync(configPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
224
|
+
}
|
|
225
|
+
// ── Diagnostics (doctor) ─────────────────────────────────
|
|
226
|
+
validateHooks(_pluginRoot) {
|
|
227
|
+
const results = [];
|
|
228
|
+
const settings = this.readSettings();
|
|
229
|
+
if (!settings) {
|
|
230
|
+
results.push({
|
|
231
|
+
check: "Plugin configuration",
|
|
232
|
+
status: "fail",
|
|
233
|
+
message: "Could not read openclaw.json",
|
|
234
|
+
fix: "context-mode upgrade",
|
|
235
|
+
});
|
|
236
|
+
return results;
|
|
237
|
+
}
|
|
238
|
+
// Check for context-mode in plugins.entries
|
|
239
|
+
const plugins = settings.plugins;
|
|
240
|
+
const entries = plugins?.entries;
|
|
241
|
+
if (entries) {
|
|
242
|
+
const hasPlugin = Object.keys(entries).some((k) => k.includes("context-mode"));
|
|
243
|
+
results.push({
|
|
244
|
+
check: "Plugin registration",
|
|
245
|
+
status: hasPlugin ? "pass" : "fail",
|
|
246
|
+
message: hasPlugin
|
|
247
|
+
? "context-mode found in plugins.entries"
|
|
248
|
+
: "context-mode not found in plugins.entries",
|
|
249
|
+
fix: hasPlugin
|
|
250
|
+
? undefined
|
|
251
|
+
: "context-mode upgrade",
|
|
252
|
+
});
|
|
253
|
+
// Check if enabled
|
|
254
|
+
if (hasPlugin) {
|
|
255
|
+
const entry = entries["context-mode"];
|
|
256
|
+
const isEnabled = entry?.enabled !== false;
|
|
257
|
+
results.push({
|
|
258
|
+
check: "Plugin enabled",
|
|
259
|
+
status: isEnabled ? "pass" : "warn",
|
|
260
|
+
message: isEnabled
|
|
261
|
+
? "context-mode plugin is enabled"
|
|
262
|
+
: "context-mode plugin is disabled",
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
results.push({
|
|
268
|
+
check: "Plugin registration",
|
|
269
|
+
status: "fail",
|
|
270
|
+
message: "No plugins.entries found in openclaw.json",
|
|
271
|
+
fix: "context-mode upgrade",
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
// Check context engine slot
|
|
275
|
+
const slots = plugins?.slots;
|
|
276
|
+
if (slots?.contextEngine === "context-mode") {
|
|
277
|
+
results.push({
|
|
278
|
+
check: "Context engine",
|
|
279
|
+
status: "pass",
|
|
280
|
+
message: "context-mode registered as context engine (owns compaction)",
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
results.push({
|
|
285
|
+
check: "Context engine",
|
|
286
|
+
status: "warn",
|
|
287
|
+
message: "context-mode not set as context engine — compaction will use default engine",
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return results;
|
|
291
|
+
}
|
|
292
|
+
checkPluginRegistration() {
|
|
293
|
+
const settings = this.readSettings();
|
|
294
|
+
if (!settings) {
|
|
295
|
+
return {
|
|
296
|
+
check: "Plugin registration",
|
|
297
|
+
status: "warn",
|
|
298
|
+
message: "Could not read openclaw.json",
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const plugins = settings.plugins;
|
|
302
|
+
const entries = plugins?.entries;
|
|
303
|
+
if (entries) {
|
|
304
|
+
const hasPlugin = Object.keys(entries).some((k) => k.includes("context-mode"));
|
|
305
|
+
if (hasPlugin) {
|
|
306
|
+
return {
|
|
307
|
+
check: "Plugin registration",
|
|
308
|
+
status: "pass",
|
|
309
|
+
message: "context-mode found in plugins.entries",
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
check: "Plugin registration",
|
|
315
|
+
status: "fail",
|
|
316
|
+
message: "context-mode not found in openclaw.json plugins.entries",
|
|
317
|
+
fix: "context-mode upgrade",
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
getInstalledVersion() {
|
|
321
|
+
// Check ~/.openclaw/extensions/context-mode/ for the plugin
|
|
322
|
+
try {
|
|
323
|
+
const pkgPath = resolve(homedir(), ".openclaw", "extensions", "context-mode", "package.json");
|
|
324
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
325
|
+
if (typeof pkg.version === "string")
|
|
326
|
+
return pkg.version;
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
/* not found */
|
|
330
|
+
}
|
|
331
|
+
// Also check node_modules
|
|
332
|
+
try {
|
|
333
|
+
const pkgPath = resolve("node_modules", "context-mode", "package.json");
|
|
334
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
335
|
+
if (typeof pkg.version === "string")
|
|
336
|
+
return pkg.version;
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
/* not found */
|
|
340
|
+
}
|
|
341
|
+
return "not installed";
|
|
342
|
+
}
|
|
343
|
+
// ── Upgrade ────────────────────────────────────────────
|
|
344
|
+
configureAllHooks(_pluginRoot) {
|
|
345
|
+
const settings = this.readSettings() ?? {};
|
|
346
|
+
const changes = [];
|
|
347
|
+
// Ensure plugins.entries exists
|
|
348
|
+
if (!settings.plugins) {
|
|
349
|
+
settings.plugins = {};
|
|
350
|
+
}
|
|
351
|
+
const plugins = settings.plugins;
|
|
352
|
+
if (!plugins.entries) {
|
|
353
|
+
plugins.entries = {};
|
|
354
|
+
}
|
|
355
|
+
const entries = plugins.entries;
|
|
356
|
+
// Add context-mode to plugins.entries
|
|
357
|
+
if (!entries["context-mode"]) {
|
|
358
|
+
entries["context-mode"] = { enabled: true };
|
|
359
|
+
changes.push("Added context-mode to plugins.entries");
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
const entry = entries["context-mode"];
|
|
363
|
+
if (entry.enabled === false) {
|
|
364
|
+
entry.enabled = true;
|
|
365
|
+
changes.push("Enabled context-mode plugin");
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
changes.push("context-mode already configured in plugins.entries");
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Optionally set context engine slot
|
|
372
|
+
if (!plugins.slots) {
|
|
373
|
+
plugins.slots = {};
|
|
374
|
+
}
|
|
375
|
+
const slots = plugins.slots;
|
|
376
|
+
if (!slots.contextEngine) {
|
|
377
|
+
slots.contextEngine = "context-mode";
|
|
378
|
+
changes.push("Set context-mode as context engine (owns compaction)");
|
|
379
|
+
}
|
|
380
|
+
else if (slots.contextEngine !== "context-mode") {
|
|
381
|
+
changes.push(`Context engine already set to "${slots.contextEngine}" — not overwriting`);
|
|
382
|
+
}
|
|
383
|
+
this.writeSettings(settings);
|
|
384
|
+
return changes;
|
|
385
|
+
}
|
|
386
|
+
backupSettings() {
|
|
387
|
+
const paths = [
|
|
388
|
+
resolve("openclaw.json"),
|
|
389
|
+
resolve(".openclaw", "openclaw.json"),
|
|
390
|
+
join(homedir(), ".openclaw", "openclaw.json"),
|
|
391
|
+
];
|
|
392
|
+
for (const configPath of paths) {
|
|
393
|
+
try {
|
|
394
|
+
accessSync(configPath, constants.R_OK);
|
|
395
|
+
const backupPath = configPath + ".bak";
|
|
396
|
+
copyFileSync(configPath, backupPath);
|
|
397
|
+
return backupPath;
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
setHookPermissions(_pluginRoot) {
|
|
406
|
+
// OpenClaw uses TS plugin paradigm — no shell scripts to chmod
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
updatePluginRegistry(_pluginRoot, _version) {
|
|
410
|
+
// OpenClaw manages plugins through npm/openclaw.json — no separate registry
|
|
411
|
+
}
|
|
412
|
+
// ── Routing Instructions (soft enforcement) ────────────
|
|
413
|
+
getRoutingInstructionsConfig() {
|
|
414
|
+
return {
|
|
415
|
+
fileName: "AGENTS.md",
|
|
416
|
+
globalPath: resolve(homedir(), ".openclaw", "AGENTS.md"),
|
|
417
|
+
projectRelativePath: "AGENTS.md",
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
writeRoutingInstructions(projectDir, pluginRoot) {
|
|
421
|
+
const config = this.getRoutingInstructionsConfig();
|
|
422
|
+
const targetPath = resolve(projectDir, config.projectRelativePath);
|
|
423
|
+
const sourcePath = resolve(pluginRoot, "configs", "openclaw", config.fileName);
|
|
424
|
+
try {
|
|
425
|
+
let content;
|
|
426
|
+
try {
|
|
427
|
+
content = readFileSync(sourcePath, "utf-8");
|
|
428
|
+
}
|
|
429
|
+
catch {
|
|
430
|
+
// Fall back to opencode config if openclaw-specific doesn't exist yet
|
|
431
|
+
const fallbackPath = resolve(pluginRoot, "configs", "opencode", config.fileName);
|
|
432
|
+
content = readFileSync(fallbackPath, "utf-8");
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
const existing = readFileSync(targetPath, "utf-8");
|
|
436
|
+
if (existing.includes("context-mode"))
|
|
437
|
+
return null;
|
|
438
|
+
writeFileSync(targetPath, existing.trimEnd() + "\n\n" + content, "utf-8");
|
|
439
|
+
return targetPath;
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
writeFileSync(targetPath, content, "utf-8");
|
|
443
|
+
return targetPath;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// ── Internal helpers ───────────────────────────────────
|
|
451
|
+
/**
|
|
452
|
+
* Extract session ID from OpenClaw hook input.
|
|
453
|
+
*/
|
|
454
|
+
extractSessionId(input) {
|
|
455
|
+
if (input.sessionId)
|
|
456
|
+
return input.sessionId;
|
|
457
|
+
return `pid-${process.ppid}`;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClawSessionDB — OpenClaw-specific extension of SessionDB.
|
|
3
|
+
*
|
|
4
|
+
* Adds session_key mapping (openclaw_session_map table) and session
|
|
5
|
+
* rename support needed for OpenClaw's gateway restart re-keying.
|
|
6
|
+
*
|
|
7
|
+
* The shared SessionDB remains unaware of session_key; all OpenClaw-specific
|
|
8
|
+
* session mapping lives here.
|
|
9
|
+
*/
|
|
10
|
+
import { SessionDB } from "../../session/db.js";
|
|
11
|
+
/** Row from the openclaw_session_map table. */
|
|
12
|
+
export interface SessionMapRow {
|
|
13
|
+
session_key: string;
|
|
14
|
+
session_id: string;
|
|
15
|
+
created_at: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class OpenClawSessionDB extends SessionDB {
|
|
18
|
+
/**
|
|
19
|
+
* OpenClaw-specific prepared statements, separate from the parent's
|
|
20
|
+
* private statement cache. Created in prepareStatements() after
|
|
21
|
+
* super.prepareStatements() finishes.
|
|
22
|
+
*
|
|
23
|
+
* `declare` prevents TypeScript from emitting a field initializer
|
|
24
|
+
* that would wipe the value set during the base constructor's
|
|
25
|
+
* prepareStatements() call chain.
|
|
26
|
+
*/
|
|
27
|
+
private ocStmts;
|
|
28
|
+
protected initSchema(): void;
|
|
29
|
+
protected prepareStatements(): void;
|
|
30
|
+
/** Shorthand to retrieve an OpenClaw-specific cached statement. */
|
|
31
|
+
private oc;
|
|
32
|
+
/**
|
|
33
|
+
* Ensure a session metadata entry exists with an associated session_key.
|
|
34
|
+
* Calls the parent's 2-param ensureSession and also records the mapping
|
|
35
|
+
* in openclaw_session_map.
|
|
36
|
+
*/
|
|
37
|
+
ensureSessionWithKey(sessionId: string, projectDir: string, sessionKey: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Get the session_id of the most recently mapped session for a given sessionKey.
|
|
40
|
+
* Returns null if no sessions exist for that key.
|
|
41
|
+
*/
|
|
42
|
+
getMostRecentSession(sessionKey: string): string | null;
|
|
43
|
+
/**
|
|
44
|
+
* Rename a session ID in-place across all tables (session_meta, session_events,
|
|
45
|
+
* session_resume, openclaw_session_map), preserving all events, metadata,
|
|
46
|
+
* and resume snapshots. Used when OpenClaw re-keys session IDs on gateway
|
|
47
|
+
* restart so accumulated events survive the re-key.
|
|
48
|
+
*/
|
|
49
|
+
renameSession(oldId: string, newId: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Remove a session_key mapping from openclaw_session_map.
|
|
52
|
+
* Called on command:stop to clean up agent session tracking.
|
|
53
|
+
*/
|
|
54
|
+
removeSessionKey(sessionKey: string): void;
|
|
55
|
+
}
|