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.
Files changed (59) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +4 -2
  3. package/.openclaw-plugin/index.ts +11 -0
  4. package/.openclaw-plugin/openclaw.plugin.json +23 -0
  5. package/.openclaw-plugin/package.json +28 -0
  6. package/README.md +165 -26
  7. package/build/adapters/antigravity/index.d.ts +49 -0
  8. package/build/adapters/antigravity/index.js +217 -0
  9. package/build/adapters/client-map.d.ts +10 -0
  10. package/build/adapters/client-map.js +18 -0
  11. package/build/adapters/detect.d.ts +8 -1
  12. package/build/adapters/detect.js +58 -1
  13. package/build/adapters/kiro/hooks.d.ts +32 -0
  14. package/build/adapters/kiro/hooks.js +47 -0
  15. package/build/adapters/kiro/index.d.ts +50 -0
  16. package/build/adapters/kiro/index.js +325 -0
  17. package/build/adapters/openclaw/config.d.ts +8 -0
  18. package/build/adapters/openclaw/config.js +8 -0
  19. package/build/adapters/openclaw/hooks.d.ts +50 -0
  20. package/build/adapters/openclaw/hooks.js +61 -0
  21. package/build/adapters/openclaw/index.d.ts +51 -0
  22. package/build/adapters/openclaw/index.js +459 -0
  23. package/build/adapters/openclaw/session-db.d.ts +55 -0
  24. package/build/adapters/openclaw/session-db.js +88 -0
  25. package/build/adapters/types.d.ts +1 -1
  26. package/build/cli.js +5 -3
  27. package/build/executor.js +99 -112
  28. package/build/openclaw/workspace-router.d.ts +29 -0
  29. package/build/openclaw/workspace-router.js +64 -0
  30. package/build/openclaw-plugin.d.ts +121 -0
  31. package/build/openclaw-plugin.js +525 -0
  32. package/build/server.js +45 -10
  33. package/build/session/db.d.ts +9 -0
  34. package/build/session/db.js +38 -0
  35. package/cli.bundle.mjs +136 -124
  36. package/configs/antigravity/GEMINI.md +58 -0
  37. package/configs/antigravity/mcp_config.json +7 -0
  38. package/configs/kiro/mcp_config.json +7 -0
  39. package/configs/openclaw/AGENTS.md +58 -0
  40. package/configs/openclaw/openclaw.json +13 -0
  41. package/hooks/core/routing.mjs +16 -8
  42. package/hooks/kiro/posttooluse.mjs +58 -0
  43. package/hooks/kiro/pretooluse.mjs +63 -0
  44. package/hooks/posttooluse.mjs +6 -5
  45. package/hooks/precompact.mjs +5 -4
  46. package/hooks/session-db.bundle.mjs +57 -0
  47. package/hooks/session-extract.bundle.mjs +1 -0
  48. package/hooks/session-helpers.mjs +41 -3
  49. package/hooks/session-loaders.mjs +28 -0
  50. package/hooks/session-snapshot.bundle.mjs +14 -0
  51. package/hooks/sessionstart.mjs +6 -5
  52. package/hooks/userpromptsubmit.mjs +6 -5
  53. package/hooks/vscode-copilot/posttooluse.mjs +5 -4
  54. package/hooks/vscode-copilot/precompact.mjs +5 -4
  55. package/hooks/vscode-copilot/sessionstart.mjs +5 -4
  56. package/openclaw.plugin.json +23 -0
  57. package/package.json +13 -2
  58. package/server.bundle.mjs +94 -82
  59. 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
+ }