shieldcortex 2.19.1 → 2.20.0
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/README.md +14 -10
- package/dashboard/.next/standalone/dashboard/.next/BUILD_ID +1 -1
- package/dashboard/.next/standalone/dashboard/.next/build-manifest.json +2 -2
- package/dashboard/.next/standalone/dashboard/.next/prerender-manifest.json +3 -3
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.rsc +3 -3
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/page/react-loadable-manifest.json +3 -3
- package/dashboard/.next/standalone/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/chunks/ssr/dashboard_25b1b286._.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/pages/404.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/pages/500.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.json +1 -1
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/26118d592a545e00.js +3 -0
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/6a6ccfb7834de00a.js +9 -0
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/6b11a7d29e9abffd.js +1 -0
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/ab13d81ce0e121f2.css +3 -0
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/cf05262adfab5818.js +1 -0
- package/dist/api/control.d.ts +28 -11
- package/dist/api/control.d.ts.map +1 -1
- package/dist/api/control.js +120 -19
- package/dist/api/control.js.map +1 -1
- package/dist/api/events.d.ts +12 -1
- package/dist/api/events.d.ts.map +1 -1
- package/dist/api/events.js +9 -0
- package/dist/api/events.js.map +1 -1
- package/dist/api/visualization-server.d.ts.map +1 -1
- package/dist/api/visualization-server.js +306 -39
- package/dist/api/visualization-server.js.map +1 -1
- package/dist/cli/doctor.d.ts +6 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +469 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cloud/quarantine-sync.d.ts +3 -0
- package/dist/cloud/quarantine-sync.d.ts.map +1 -1
- package/dist/cloud/quarantine-sync.js +7 -0
- package/dist/cloud/quarantine-sync.js.map +1 -1
- package/dist/cloud/sync.d.ts +11 -0
- package/dist/cloud/sync.d.ts.map +1 -1
- package/dist/cloud/sync.js +37 -0
- package/dist/cloud/sync.js.map +1 -1
- package/dist/database/init.d.ts +8 -0
- package/dist/database/init.d.ts.map +1 -1
- package/dist/database/init.js +208 -4
- package/dist/database/init.js.map +1 -1
- package/dist/defence/index.d.ts +2 -0
- package/dist/defence/index.d.ts.map +1 -1
- package/dist/defence/index.js +2 -0
- package/dist/defence/index.js.map +1 -1
- package/dist/defence/input-sanitisation/index.d.ts +28 -0
- package/dist/defence/input-sanitisation/index.d.ts.map +1 -0
- package/dist/defence/input-sanitisation/index.js +71 -0
- package/dist/defence/input-sanitisation/index.js.map +1 -0
- package/dist/defence/iron-dome/config.js +5 -5
- package/dist/defence/iron-dome/config.js.map +1 -1
- package/dist/defence/iron-dome/index.d.ts +6 -0
- package/dist/defence/iron-dome/index.d.ts.map +1 -1
- package/dist/defence/iron-dome/index.js +19 -0
- package/dist/defence/iron-dome/index.js.map +1 -1
- package/dist/defence/pipeline.d.ts.map +1 -1
- package/dist/defence/pipeline.js +21 -11
- package/dist/defence/pipeline.js.map +1 -1
- package/dist/events/webhooks.d.ts +21 -0
- package/dist/events/webhooks.d.ts.map +1 -0
- package/dist/events/webhooks.js +61 -0
- package/dist/events/webhooks.js.map +1 -0
- package/dist/graph/backfill.d.ts +6 -2
- package/dist/graph/backfill.d.ts.map +1 -1
- package/dist/graph/backfill.js +32 -4
- package/dist/graph/backfill.js.map +1 -1
- package/dist/graph/extract.d.ts.map +1 -1
- package/dist/graph/extract.js +105 -37
- package/dist/graph/extract.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -5
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +2 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +2 -0
- package/dist/lib.js.map +1 -1
- package/dist/memory/consolidate.d.ts +23 -0
- package/dist/memory/consolidate.d.ts.map +1 -1
- package/dist/memory/consolidate.js +239 -2
- package/dist/memory/consolidate.js.map +1 -1
- package/dist/memory/decay.d.ts.map +1 -1
- package/dist/memory/decay.js +9 -0
- package/dist/memory/decay.js.map +1 -1
- package/dist/memory/embedding-cache.d.ts +21 -0
- package/dist/memory/embedding-cache.d.ts.map +1 -0
- package/dist/memory/embedding-cache.js +92 -0
- package/dist/memory/embedding-cache.js.map +1 -0
- package/dist/memory/embedding.d.ts +37 -0
- package/dist/memory/embedding.d.ts.map +1 -0
- package/dist/memory/embedding.js +86 -0
- package/dist/memory/embedding.js.map +1 -0
- package/dist/memory/expiry.d.ts +26 -0
- package/dist/memory/expiry.d.ts.map +1 -0
- package/dist/memory/expiry.js +109 -0
- package/dist/memory/expiry.js.map +1 -0
- package/dist/memory/store.d.ts +14 -0
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js +82 -0
- package/dist/memory/store.js.map +1 -1
- package/dist/memory/types.d.ts +1 -0
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +193 -45
- package/dist/server.js.map +1 -1
- package/dist/tools/context.d.ts +4 -4
- package/dist/tools/forget.d.ts +4 -4
- package/dist/tools/recall.d.ts +9 -9
- package/dist/tools/recall.d.ts.map +1 -1
- package/dist/tools/recall.js +25 -1
- package/dist/tools/recall.js.map +1 -1
- package/dist/tools/remember.d.ts +6 -6
- package/hooks/openclaw/cortex-memory/handler.ts +8 -18
- package/package.json +1 -1
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/1a71c9a52f0c9b16.css +0 -3
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/6bf7d89d34068ecb.js +0 -9
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/a3989d0e6629bcf8.js +0 -3
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/d0dcb5e0e04ae015.js +0 -1
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/fc2dbf641aad1448.js +0 -1
- /package/dashboard/.next/standalone/dashboard/.next/static/{vxPliVFK4FIBIPl1JPL0U → aFo1BShJENvQZgqpWRJaw}/_buildManifest.js +0 -0
- /package/dashboard/.next/standalone/dashboard/.next/static/{vxPliVFK4FIBIPl1JPL0U → aFo1BShJENvQZgqpWRJaw}/_clientMiddlewareManifest.json +0 -0
- /package/dashboard/.next/standalone/dashboard/.next/static/{vxPliVFK4FIBIPl1JPL0U → aFo1BShJENvQZgqpWRJaw}/_ssgManifest.js +0 -0
package/dist/server.js
CHANGED
|
@@ -26,6 +26,7 @@ import { resolveSource } from './defence/trust/env-detector.js';
|
|
|
26
26
|
import { logAudit } from './defence/audit/logger.js';
|
|
27
27
|
import { scanToolResponse, shouldScanToolResponse } from './defence/tool-response-scanner.js';
|
|
28
28
|
import { getToolResponseScanConfig } from './cloud/config.js';
|
|
29
|
+
import { isKillSwitchActive, getKillSwitchMeta, assertOperationAllowed, activateKillSwitch, deactivateKillSwitch, KillSwitchError, } from './api/control.js';
|
|
29
30
|
// Shared source schema for access control on MCP tools
|
|
30
31
|
const sourceParam = z.object({
|
|
31
32
|
type: z.enum(['user', 'cli', 'hook', 'email', 'web', 'agent', 'file', 'api', 'tool_response']),
|
|
@@ -96,6 +97,55 @@ function resolveToolSource(declaredSource, toolName) {
|
|
|
96
97
|
}
|
|
97
98
|
return source;
|
|
98
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Wrap an MCP tool handler to enforce kill switch lockdown.
|
|
102
|
+
* If the kill switch is active and the operation kind is blocked,
|
|
103
|
+
* returns a lockdown error instead of executing the handler.
|
|
104
|
+
*/
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
106
|
+
function withKillSwitchGuard(kind, handler) {
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
108
|
+
return async (...handlerArgs) => {
|
|
109
|
+
try {
|
|
110
|
+
assertOperationAllowed(kind);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
if (e instanceof KillSwitchError) {
|
|
114
|
+
const meta = e.meta;
|
|
115
|
+
return {
|
|
116
|
+
content: [{
|
|
117
|
+
type: 'text',
|
|
118
|
+
text: `## KILL SWITCH ACTIVE\n\nAll operations are locked down.\n\n` +
|
|
119
|
+
`**Triggered:** ${meta?.triggeredAt ?? 'unknown'}\n` +
|
|
120
|
+
`**Source:** ${meta?.source ?? 'unknown'}\n` +
|
|
121
|
+
(meta?.phrase ? `**Phrase:** "${meta.phrase}"\n` : '') +
|
|
122
|
+
`\nUse \`iron_dome_resume\` to resume after investigation.`,
|
|
123
|
+
}],
|
|
124
|
+
isError: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
throw e;
|
|
128
|
+
}
|
|
129
|
+
return handler(...handlerArgs);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check text for kill phrase and trigger kill switch if detected.
|
|
134
|
+
* Returns true if kill switch was activated.
|
|
135
|
+
*/
|
|
136
|
+
function checkAndTriggerKillSwitch(text, source) {
|
|
137
|
+
if (isKillSwitchActive())
|
|
138
|
+
return false; // already active
|
|
139
|
+
try {
|
|
140
|
+
// Lazy import to avoid circular deps
|
|
141
|
+
const ironDome = require('./defence/iron-dome/index.js');
|
|
142
|
+
const result = ironDome.checkKillPhrase(text);
|
|
143
|
+
return result.triggered;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
99
149
|
/**
|
|
100
150
|
* Create and configure the MCP server
|
|
101
151
|
*/
|
|
@@ -151,13 +201,22 @@ Content is scanned through the defence pipeline before storage. Suspicious conte
|
|
|
151
201
|
transferable: z.boolean().optional()
|
|
152
202
|
.describe('Whether this memory can be transferred to other projects'),
|
|
153
203
|
source: sourceParam,
|
|
154
|
-
}, { title: 'Store Memory', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, async (args) => {
|
|
204
|
+
}, { title: 'Store Memory', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, withKillSwitchGuard('memory_write', async (args) => {
|
|
205
|
+
// Check for kill phrase in content before storing
|
|
206
|
+
const textToCheck = `${args.title ?? ''} ${args.content ?? ''}`;
|
|
207
|
+
if (checkAndTriggerKillSwitch(textToCheck, 'remember')) {
|
|
208
|
+
const meta = getKillSwitchMeta();
|
|
209
|
+
return {
|
|
210
|
+
content: [{ type: 'text', text: `## KILL SWITCH ACTIVATED\n\nKill phrase detected in memory content. All operations locked down.\n\nTriggered: ${meta?.triggeredAt}\nUse \`iron_dome_resume\` to resume after investigation.` }],
|
|
211
|
+
isError: true,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
155
214
|
const source = resolveToolSource(args.source, 'remember');
|
|
156
215
|
const result = await executeRemember({ ...args, source });
|
|
157
216
|
return {
|
|
158
217
|
content: [{ type: 'text', text: formatRememberResult(result) }],
|
|
159
218
|
};
|
|
160
|
-
});
|
|
219
|
+
}));
|
|
161
220
|
// Recall - Search and retrieve memories
|
|
162
221
|
server.tool('recall', `Search and retrieve memories. Use this to:
|
|
163
222
|
- Find relevant context ("What do I know about auth?")
|
|
@@ -183,13 +242,13 @@ Modes: search (query-based), recent (by time), important (by salience)`, {
|
|
|
183
242
|
mode: z.enum(['search', 'recent', 'important']).optional().default('search')
|
|
184
243
|
.describe('Recall mode'),
|
|
185
244
|
source: sourceParam,
|
|
186
|
-
}, { title: 'Search Memories', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withResponseScan('recall', async (args) => {
|
|
245
|
+
}, { title: 'Search Memories', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('memory_read', withResponseScan('recall', async (args) => {
|
|
187
246
|
const source = resolveToolSource(args.source, 'recall');
|
|
188
247
|
const result = await executeRecall({ ...args, source });
|
|
189
248
|
return {
|
|
190
249
|
content: [{ type: 'text', text: formatRecallResult(result, true) }],
|
|
191
250
|
};
|
|
192
|
-
}));
|
|
251
|
+
})));
|
|
193
252
|
// Forget - Delete memories
|
|
194
253
|
server.tool('forget', `Delete memories. Use dryRun: true to preview, confirm: true for bulk.`, {
|
|
195
254
|
id: z.number().optional().describe('Memory ID to delete'),
|
|
@@ -207,13 +266,13 @@ Modes: search (query-based), recent (by time), important (by salience)`, {
|
|
|
207
266
|
confirm: z.boolean().optional().default(false)
|
|
208
267
|
.describe('Confirm bulk delete'),
|
|
209
268
|
source: sourceParam,
|
|
210
|
-
}, { title: 'Delete Memories', readOnlyHint: false, destructiveHint: true, idempotentHint: false }, async (args) => {
|
|
269
|
+
}, { title: 'Delete Memories', readOnlyHint: false, destructiveHint: true, idempotentHint: false }, withKillSwitchGuard('memory_write', async (args) => {
|
|
211
270
|
const source = resolveToolSource(args.source, 'forget');
|
|
212
271
|
const result = await executeForget({ ...args, source });
|
|
213
272
|
return {
|
|
214
273
|
content: [{ type: 'text', text: formatForgetResult(result) }],
|
|
215
274
|
};
|
|
216
|
-
});
|
|
275
|
+
}));
|
|
217
276
|
// Get Context - THE KEY TOOL
|
|
218
277
|
server.tool('get_context', `Get relevant context from memory. THE KEY TOOL for maintaining context.
|
|
219
278
|
|
|
@@ -224,7 +283,7 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
224
283
|
format: z.enum(['summary', 'detailed', 'raw']).optional().default('summary')
|
|
225
284
|
.describe('Output format'),
|
|
226
285
|
source: sourceParam,
|
|
227
|
-
}, { title: 'Get Project Context', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withResponseScan('get_context', async (args) => {
|
|
286
|
+
}, { title: 'Get Project Context', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('memory_read', withResponseScan('get_context', async (args) => {
|
|
228
287
|
const source = resolveToolSource(args.source, 'get_context');
|
|
229
288
|
const result = await executeGetContext({ ...args, source });
|
|
230
289
|
return {
|
|
@@ -233,11 +292,11 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
233
292
|
text: result.success ? result.context : `Error: ${result.error}`
|
|
234
293
|
}],
|
|
235
294
|
};
|
|
236
|
-
}));
|
|
295
|
+
})));
|
|
237
296
|
// Start Session
|
|
238
297
|
server.tool('start_session', 'Start a new coding session. Returns relevant context.', {
|
|
239
298
|
project: z.string().optional().describe('Project scope. Auto-detected if not provided. Use "*" for global.'),
|
|
240
|
-
}, { title: 'Start Session', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, async (args) => {
|
|
299
|
+
}, { title: 'Start Session', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, withKillSwitchGuard('memory_write', async (args) => {
|
|
241
300
|
const result = await executeStartSession(args);
|
|
242
301
|
return {
|
|
243
302
|
content: [{
|
|
@@ -247,12 +306,12 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
247
306
|
: `Error: ${result.error}`
|
|
248
307
|
}],
|
|
249
308
|
};
|
|
250
|
-
});
|
|
309
|
+
}));
|
|
251
310
|
// End Session
|
|
252
311
|
server.tool('end_session', 'End session and trigger consolidation.', {
|
|
253
312
|
sessionId: z.number().describe('Session ID'),
|
|
254
313
|
summary: z.string().optional().describe('Session summary'),
|
|
255
|
-
}, { title: 'End Session', readOnlyHint: false, destructiveHint: false, idempotentHint: true }, async (args) => {
|
|
314
|
+
}, { title: 'End Session', readOnlyHint: false, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('memory_write', async (args) => {
|
|
256
315
|
const result = executeEndSession(args);
|
|
257
316
|
if (!result.success) {
|
|
258
317
|
return { content: [{ type: 'text', text: `Error: ${result.error}` }] };
|
|
@@ -264,12 +323,12 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
264
323
|
text: `Session ended. Consolidation: ${r.consolidated} promoted, ${r.decayed} decayed, ${r.deleted} deleted.`
|
|
265
324
|
}],
|
|
266
325
|
};
|
|
267
|
-
});
|
|
326
|
+
}));
|
|
268
327
|
// Consolidate
|
|
269
328
|
server.tool('consolidate', 'Run memory consolidation (like brain sleep). Promotes STM to LTM, decays old memories. Use dryRun to preview.', {
|
|
270
329
|
force: z.boolean().optional().default(false).describe('Force consolidation'),
|
|
271
330
|
dryRun: z.boolean().optional().default(false).describe('Preview what would happen without doing it'),
|
|
272
|
-
}, { title: 'Run Memory Consolidation', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, async (args) => {
|
|
331
|
+
}, { title: 'Run Memory Consolidation', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, withKillSwitchGuard('consolidation', async (args) => {
|
|
273
332
|
const result = executeConsolidate(args);
|
|
274
333
|
if (!result.success) {
|
|
275
334
|
return { content: [{ type: 'text', text: `Error: ${result.error}` }] };
|
|
@@ -301,11 +360,11 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
301
360
|
text: `Consolidation: ${r.consolidated} promoted, ${r.decayed} updated, ${r.deleted} deleted.`
|
|
302
361
|
}],
|
|
303
362
|
};
|
|
304
|
-
});
|
|
363
|
+
}));
|
|
305
364
|
// Stats
|
|
306
365
|
server.tool('memory_stats', 'Get memory statistics.', {
|
|
307
366
|
project: z.string().optional().describe('Project scope. Auto-detected if not provided. Use "*" for all projects.'),
|
|
308
|
-
}, { title: 'Memory Statistics', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, async (args) => {
|
|
367
|
+
}, { title: 'Memory Statistics', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('status', async (args) => {
|
|
309
368
|
const result = executeStats(args);
|
|
310
369
|
return {
|
|
311
370
|
content: [{
|
|
@@ -313,12 +372,12 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
313
372
|
text: result.success ? formatStats(result.stats) : `Error: ${result.error}`
|
|
314
373
|
}],
|
|
315
374
|
};
|
|
316
|
-
});
|
|
375
|
+
}));
|
|
317
376
|
// Get Memory by ID
|
|
318
377
|
server.tool('get_memory', 'Get a specific memory by ID.', {
|
|
319
378
|
id: z.number().describe('Memory ID'),
|
|
320
379
|
source: sourceParam,
|
|
321
|
-
}, { title: 'Get Memory by ID', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withResponseScan('get_memory', async (args) => {
|
|
380
|
+
}, { title: 'Get Memory by ID', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('memory_read', withResponseScan('get_memory', async (args) => {
|
|
322
381
|
const source = resolveToolSource(args.source, 'get_memory');
|
|
323
382
|
const result = executeGetMemory({ ...args, source });
|
|
324
383
|
return {
|
|
@@ -327,11 +386,11 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
327
386
|
text: result.success ? formatMemory(result.memory, true) : `Error: ${result.error}`
|
|
328
387
|
}],
|
|
329
388
|
};
|
|
330
|
-
}));
|
|
389
|
+
})));
|
|
331
390
|
// Export memories
|
|
332
391
|
server.tool('export_memories', 'Export memories as JSON for backup.', {
|
|
333
392
|
project: z.string().optional().describe('Project scope. Auto-detected if not provided. Use "*" for all projects.'),
|
|
334
|
-
}, { title: 'Export Memories', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withResponseScan('export_memories', async (args) => {
|
|
393
|
+
}, { title: 'Export Memories', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('memory_read', withResponseScan('export_memories', async (args) => {
|
|
335
394
|
const result = executeExport(args);
|
|
336
395
|
return {
|
|
337
396
|
content: [{
|
|
@@ -341,11 +400,29 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
341
400
|
: `Error: ${result.error}`
|
|
342
401
|
}],
|
|
343
402
|
};
|
|
344
|
-
}));
|
|
403
|
+
})));
|
|
345
404
|
// Import memories
|
|
346
405
|
server.tool('import_memories', 'Import memories from JSON.', {
|
|
347
406
|
data: z.string().describe('JSON data'),
|
|
348
|
-
}, { title: 'Import Memories', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, async (args) => {
|
|
407
|
+
}, { title: 'Import Memories', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, withKillSwitchGuard('memory_write', async (args) => {
|
|
408
|
+
// Check imported content for kill phrase
|
|
409
|
+
try {
|
|
410
|
+
const parsed = JSON.parse(args.data);
|
|
411
|
+
const entries = Array.isArray(parsed) ? parsed : (parsed.memories ?? []);
|
|
412
|
+
for (const entry of entries) {
|
|
413
|
+
const text = `${entry.title ?? ''} ${entry.content ?? ''}`;
|
|
414
|
+
if (checkAndTriggerKillSwitch(text, 'import_memories')) {
|
|
415
|
+
const meta = getKillSwitchMeta();
|
|
416
|
+
return {
|
|
417
|
+
content: [{ type: 'text', text: `## KILL SWITCH ACTIVATED\n\nKill phrase detected in imported content. All operations locked down.\n\nTriggered: ${meta?.triggeredAt}\nUse \`iron_dome_resume\` to resume after investigation.` }],
|
|
418
|
+
isError: true,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
// JSON parse failures will be caught by executeImport
|
|
425
|
+
}
|
|
349
426
|
const result = executeImport(args);
|
|
350
427
|
return {
|
|
351
428
|
content: [{
|
|
@@ -355,11 +432,11 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
355
432
|
: `Error: ${result.error}`
|
|
356
433
|
}],
|
|
357
434
|
};
|
|
358
|
-
});
|
|
435
|
+
}));
|
|
359
436
|
// Get Related Memories
|
|
360
437
|
server.tool('get_related', 'Get memories related to a specific memory. Shows connections and relationships.', {
|
|
361
438
|
id: z.number().describe('Memory ID to find relationships for'),
|
|
362
|
-
}, { title: 'Get Related Memories', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withResponseScan('get_related', async (args) => {
|
|
439
|
+
}, { title: 'Get Related Memories', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('memory_read', withResponseScan('get_related', async (args) => {
|
|
363
440
|
const related = getRelatedMemories(args.id);
|
|
364
441
|
if (related.length === 0) {
|
|
365
442
|
return { content: [{ type: 'text', text: 'No related memories found.' }] };
|
|
@@ -371,7 +448,7 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
371
448
|
lines.push(` ID: ${r.memory.id} | ${r.memory.category} | ${(r.memory.salience * 100).toFixed(0)}% salience`);
|
|
372
449
|
}
|
|
373
450
|
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
374
|
-
}));
|
|
451
|
+
})));
|
|
375
452
|
// Link Memories
|
|
376
453
|
server.tool('link_memories', 'Create a relationship link between two memories.', {
|
|
377
454
|
sourceId: z.number().describe('Source memory ID'),
|
|
@@ -380,17 +457,17 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
380
457
|
.describe('Type of relationship'),
|
|
381
458
|
strength: z.number().min(0).max(1).optional().default(0.5)
|
|
382
459
|
.describe('Relationship strength (0-1)'),
|
|
383
|
-
}, { title: 'Link Memories', readOnlyHint: false, destructiveHint: false, idempotentHint: true }, async (args) => {
|
|
460
|
+
}, { title: 'Link Memories', readOnlyHint: false, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('memory_write', async (args) => {
|
|
384
461
|
const link = createMemoryLink(args.sourceId, args.targetId, args.relationship, args.strength);
|
|
385
462
|
if (!link) {
|
|
386
463
|
return { content: [{ type: 'text', text: 'Failed to create link. Memories may not exist or link already exists.' }] };
|
|
387
464
|
}
|
|
388
465
|
return { content: [{ type: 'text', text: `✓ Linked memory ${args.sourceId} → ${args.targetId} (${args.relationship})` }] };
|
|
389
|
-
});
|
|
466
|
+
}));
|
|
390
467
|
// Set Project - Switch active project context
|
|
391
468
|
server.tool('set_project', `Switch active project context. Use "${GLOBAL_PROJECT_SENTINEL}" for global/all projects.`, {
|
|
392
469
|
project: z.string().describe(`Project name, or "${GLOBAL_PROJECT_SENTINEL}" for global scope`),
|
|
393
|
-
}, { title: 'Switch Project', readOnlyHint: false, destructiveHint: false, idempotentHint: true }, async (args) => {
|
|
470
|
+
}, { title: 'Switch Project', readOnlyHint: false, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('config', async (args) => {
|
|
394
471
|
const oldProject = getActiveProject();
|
|
395
472
|
setActiveProject(args.project === GLOBAL_PROJECT_SENTINEL ? null : args.project);
|
|
396
473
|
const newProject = getActiveProject();
|
|
@@ -400,8 +477,8 @@ Returns: architecture decisions, patterns, pending items, recent activity.`, {
|
|
|
400
477
|
text: `Project context changed: ${oldProject || 'global'} → ${newProject || 'global'}`
|
|
401
478
|
}]
|
|
402
479
|
};
|
|
403
|
-
});
|
|
404
|
-
// Get Project - Show current project scope
|
|
480
|
+
}));
|
|
481
|
+
// Get Project - Show current project scope (status — allowed during lockdown)
|
|
405
482
|
server.tool('get_project', 'Show current project scope and detection info.', {}, { title: 'Show Current Project', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, async () => {
|
|
406
483
|
const info = getProjectContextInfo();
|
|
407
484
|
const lines = [
|
|
@@ -432,7 +509,7 @@ but you can use this tool to check for new contradictions at any time.`, {
|
|
|
432
509
|
.describe('Minimum contradiction score (0-1, default 0.4)'),
|
|
433
510
|
limit: z.number().min(1).max(50).optional().default(10)
|
|
434
511
|
.describe('Maximum results to return'),
|
|
435
|
-
}, { title: 'Detect Contradictions', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withResponseScan('detect_contradictions', async (args) => {
|
|
512
|
+
}, { title: 'Detect Contradictions', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('consolidation', withResponseScan('detect_contradictions', async (args) => {
|
|
436
513
|
const project = args.project ?? getActiveProject() ?? undefined;
|
|
437
514
|
const contradictions = detectContradictions({
|
|
438
515
|
project,
|
|
@@ -455,7 +532,7 @@ but you can use this tool to check for new contradictions at any time.`, {
|
|
|
455
532
|
}
|
|
456
533
|
lines.push(`\n*Found ${contradictions.length} potential contradiction(s). Use \`get_related\` to see linked contradictions.*`);
|
|
457
534
|
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
458
|
-
}));
|
|
535
|
+
})));
|
|
459
536
|
// ============================================
|
|
460
537
|
// KNOWLEDGE GRAPH TOOLS
|
|
461
538
|
// ============================================
|
|
@@ -464,19 +541,19 @@ but you can use this tool to check for new contradictions at any time.`, {
|
|
|
464
541
|
entity: z.string().describe('Entity name to start from'),
|
|
465
542
|
depth: z.number().optional().describe('Max traversal depth (default 2)'),
|
|
466
543
|
predicates: z.array(z.string()).optional().describe('Filter by predicate types'),
|
|
467
|
-
}, { title: 'Knowledge Graph Query', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withResponseScan('graph_query', async (args) => handleGraphQuery(args)));
|
|
544
|
+
}, { title: 'Knowledge Graph Query', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('graph', withResponseScan('graph_query', async (args) => handleGraphQuery(args))));
|
|
468
545
|
// Graph Entities - List known entities
|
|
469
546
|
server.tool('graph_entities', 'List known entities in the knowledge graph, optionally filtered by type.', {
|
|
470
547
|
type: z.string().optional().describe('Filter by entity type (person, tool, concept, file, language, service, pattern)'),
|
|
471
548
|
minMentions: z.number().optional().describe('Minimum memory references (default 1)'),
|
|
472
549
|
limit: z.number().optional().describe('Max results (default 50)'),
|
|
473
|
-
}, { title: 'List Graph Entities', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withResponseScan('graph_entities', async (args) => handleGraphEntities(args)));
|
|
550
|
+
}, { title: 'List Graph Entities', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('graph', withResponseScan('graph_entities', async (args) => handleGraphEntities(args))));
|
|
474
551
|
// Graph Explain - Find paths between entities
|
|
475
552
|
server.tool('graph_explain', 'Explain the relationship between two entities by finding paths connecting them in the knowledge graph.', {
|
|
476
553
|
from: z.string().describe('Source entity name'),
|
|
477
554
|
to: z.string().describe('Target entity name'),
|
|
478
555
|
maxDepth: z.number().optional().describe('Max path length (default 4)'),
|
|
479
|
-
}, { title: 'Explain Entity Relationship', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withResponseScan('graph_explain', async (args) => handleGraphExplain(args)));
|
|
556
|
+
}, { title: 'Explain Entity Relationship', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('graph', withResponseScan('graph_explain', async (args) => handleGraphExplain(args))));
|
|
480
557
|
// ============================================
|
|
481
558
|
// DEFENCE TOOLS
|
|
482
559
|
// ============================================
|
|
@@ -499,7 +576,7 @@ but you can use this tool to check for new contradictions at any time.`, {
|
|
|
499
576
|
quarantineId: z.number().optional().describe('ID for approve/reject'),
|
|
500
577
|
sourceIdentifier: z.string().optional().describe('Source identifier for batch approve (e.g. "user-spawned>task-1")'),
|
|
501
578
|
notes: z.string().optional(),
|
|
502
|
-
}, { title: 'Review Quarantined Memories', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, async (args) => {
|
|
579
|
+
}, { title: 'Review Quarantined Memories', readOnlyHint: false, destructiveHint: false, idempotentHint: false }, withKillSwitchGuard('memory_write', async (args) => {
|
|
503
580
|
const db = (await import('./database/init.js')).getDatabase();
|
|
504
581
|
if (args.action === 'list') {
|
|
505
582
|
const items = db.prepare('SELECT * FROM quarantine WHERE status = ? ORDER BY created_at DESC LIMIT 50').all('pending');
|
|
@@ -545,8 +622,8 @@ but you can use this tool to check for new contradictions at any time.`, {
|
|
|
545
622
|
return { content: [{ type: 'text', text: `Batch approved ${items.length} items from "${args.sourceIdentifier}" (${promoted} promoted to memory).` }] };
|
|
546
623
|
}
|
|
547
624
|
return { content: [{ type: 'text', text: 'Invalid action or missing required parameters.' }] };
|
|
548
|
-
});
|
|
549
|
-
// Defence Stats
|
|
625
|
+
}));
|
|
626
|
+
// Defence Stats (allowed during lockdown)
|
|
550
627
|
server.tool('defence_stats', 'Get defence system statistics', {
|
|
551
628
|
timeRange: z.enum(['24h', '7d', '30d']).default('24h'),
|
|
552
629
|
}, { title: 'Defence Statistics', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, async (args) => {
|
|
@@ -573,6 +650,14 @@ Runs injection detection (40+ patterns) and credential leak scanning (25+ provid
|
|
|
573
650
|
mode: z.enum(['advisory', 'enforce']).optional()
|
|
574
651
|
.describe('Scan mode: advisory (log only) or enforce (can redact). Default: advisory'),
|
|
575
652
|
}, { title: 'Scan Tool Response', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, async (args) => {
|
|
653
|
+
// Check for kill phrase in scanned content
|
|
654
|
+
if (checkAndTriggerKillSwitch(args.content, 'scan_tool_response')) {
|
|
655
|
+
const meta = getKillSwitchMeta();
|
|
656
|
+
return {
|
|
657
|
+
content: [{ type: 'text', text: `## KILL SWITCH ACTIVATED\n\nKill phrase detected in tool response. All operations locked down.\n\nTriggered: ${meta?.triggeredAt}\nUse \`iron_dome_resume\` to resume after investigation.` }],
|
|
658
|
+
isError: true,
|
|
659
|
+
};
|
|
660
|
+
}
|
|
576
661
|
const scan = scanToolResponse(args.toolName, args.content, args.mode);
|
|
577
662
|
const lines = [
|
|
578
663
|
`## Tool Response Scan: ${args.toolName}`,
|
|
@@ -664,15 +749,29 @@ Runs injection detection (40+ patterns) and credential leak scanning (25+ provid
|
|
|
664
749
|
// ============================================
|
|
665
750
|
// IRON DOME TOOLS
|
|
666
751
|
// ============================================
|
|
667
|
-
// Iron Dome Status
|
|
668
|
-
server.tool('iron_dome_status', 'Check if Iron Dome is active
|
|
752
|
+
// Iron Dome Status (allowed during lockdown — forensic access)
|
|
753
|
+
server.tool('iron_dome_status', 'Check if Iron Dome is active and show kill switch state. Shows config summary including profile, trusted channels, and approval rules.', {}, { title: 'Iron Dome Status', readOnlyHint: true, destructiveHint: false, idempotentHint: true }, async () => {
|
|
669
754
|
const { getIronDomeStatus } = await import('./defence/iron-dome/index.js');
|
|
670
755
|
const status = getIronDomeStatus();
|
|
756
|
+
const killMeta = getKillSwitchMeta();
|
|
671
757
|
const lines = [
|
|
672
758
|
`## Iron Dome Status`,
|
|
673
759
|
'',
|
|
674
|
-
`**Active:** ${status.enabled ? 'Yes' : 'No'}`,
|
|
675
760
|
];
|
|
761
|
+
// Kill switch state (most important — show first)
|
|
762
|
+
if (isKillSwitchActive() && killMeta) {
|
|
763
|
+
lines.push(`**KILL SWITCH: ACTIVE**`);
|
|
764
|
+
lines.push(`**Triggered:** ${killMeta.triggeredAt}`);
|
|
765
|
+
lines.push(`**Source:** ${killMeta.source}`);
|
|
766
|
+
if (killMeta.phrase)
|
|
767
|
+
lines.push(`**Phrase:** "${killMeta.phrase}"`);
|
|
768
|
+
if (killMeta.memoryCountAtTrigger != null)
|
|
769
|
+
lines.push(`**Memories at trigger:** ${killMeta.memoryCountAtTrigger}`);
|
|
770
|
+
lines.push('');
|
|
771
|
+
lines.push('> All operations are locked down. Use `iron_dome_resume` to resume.');
|
|
772
|
+
lines.push('');
|
|
773
|
+
}
|
|
774
|
+
lines.push(`**Iron Dome Active:** ${status.enabled ? 'Yes' : 'No'}`);
|
|
676
775
|
if (status.enabled) {
|
|
677
776
|
const c = status.config;
|
|
678
777
|
lines.push(`**Profile:** ${c.profile ?? 'custom'}`);
|
|
@@ -740,7 +839,7 @@ Runs injection detection (40+ patterns) and credential leak scanning (25+ provid
|
|
|
740
839
|
server.tool('iron_dome_activate', 'Activate Iron Dome with a profile. Profiles: school (GDPR strict), enterprise (financial protection), personal (lighter touch), paranoid (everything requires approval).', {
|
|
741
840
|
profile: z.enum(['school', 'enterprise', 'personal', 'paranoid']).optional()
|
|
742
841
|
.describe('Security profile to activate'),
|
|
743
|
-
}, { title: 'Activate Iron Dome', readOnlyHint: false, destructiveHint: false, idempotentHint: true }, async (args) => {
|
|
842
|
+
}, { title: 'Activate Iron Dome', readOnlyHint: false, destructiveHint: false, idempotentHint: true }, withKillSwitchGuard('config', async (args) => {
|
|
744
843
|
const { activateIronDome } = await import('./defence/iron-dome/index.js');
|
|
745
844
|
const config = activateIronDome(args.profile);
|
|
746
845
|
const lines = [
|
|
@@ -753,12 +852,50 @@ Runs injection detection (40+ patterns) and credential leak scanning (25+ provid
|
|
|
753
852
|
`**Auto-Approve:** ${config.autoApprove.join(', ')}`,
|
|
754
853
|
];
|
|
755
854
|
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
855
|
+
}));
|
|
856
|
+
// ============================================
|
|
857
|
+
// IRON DOME EMERGENCY TOOLS
|
|
858
|
+
// ============================================
|
|
859
|
+
// Emergency Stop — NOT guarded (must always work)
|
|
860
|
+
server.tool('iron_dome_emergency_stop', 'Emergency kill switch — immediately locks down ALL agent operations. Use when you detect suspicious activity. Reversible via iron_dome_resume.', {}, { title: 'Emergency Stop', readOnlyHint: false, destructiveHint: true, idempotentHint: true }, async () => {
|
|
861
|
+
activateKillSwitch({ source: 'mcp_tool' });
|
|
862
|
+
const meta = getKillSwitchMeta();
|
|
863
|
+
return {
|
|
864
|
+
content: [{
|
|
865
|
+
type: 'text',
|
|
866
|
+
text: `## KILL SWITCH ACTIVATED\n\nAll operations locked down.\n\n` +
|
|
867
|
+
`**Triggered:** ${meta?.triggeredAt}\n` +
|
|
868
|
+
`**Source:** MCP tool\n\n` +
|
|
869
|
+
`Memory creation, recall, graph queries, and all other operations are blocked.\n` +
|
|
870
|
+
`Use \`iron_dome_resume\` with a reason to resume after investigation.`,
|
|
871
|
+
}],
|
|
872
|
+
};
|
|
873
|
+
});
|
|
874
|
+
// Resume — NOT guarded (this IS the unlock)
|
|
875
|
+
server.tool('iron_dome_resume', 'Resume agent operations after kill switch investigation. Only use after confirming the threat has been addressed.', {
|
|
876
|
+
reason: z.string().describe('Why operations are being resumed (required for audit trail)'),
|
|
877
|
+
}, { title: 'Resume Operations', readOnlyHint: false, destructiveHint: false, idempotentHint: true }, async (args) => {
|
|
878
|
+
if (!isKillSwitchActive()) {
|
|
879
|
+
return {
|
|
880
|
+
content: [{ type: 'text', text: 'Kill switch is not active. No action needed.' }],
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
deactivateKillSwitch(args.reason);
|
|
884
|
+
return {
|
|
885
|
+
content: [{
|
|
886
|
+
type: 'text',
|
|
887
|
+
text: `## Operations Resumed\n\nKill switch deactivated.\n**Reason:** ${args.reason}\n\nAll tools are now operational. Iron Dome continues protecting.`,
|
|
888
|
+
}],
|
|
889
|
+
};
|
|
756
890
|
});
|
|
757
891
|
// ============================================
|
|
758
892
|
// RESOURCES
|
|
759
893
|
// ============================================
|
|
760
|
-
// Project context resource
|
|
894
|
+
// Project context resource (blocked during kill switch)
|
|
761
895
|
server.resource('memory://context', 'memory://context', async () => {
|
|
896
|
+
if (isKillSwitchActive()) {
|
|
897
|
+
return { contents: [{ uri: 'memory://context', mimeType: 'text/plain', text: '[KILL SWITCH ACTIVE] Memory access blocked.' }] };
|
|
898
|
+
}
|
|
762
899
|
const summary = await generateContextSummary();
|
|
763
900
|
return {
|
|
764
901
|
contents: [{
|
|
@@ -768,8 +905,11 @@ Runs injection detection (40+ patterns) and credential leak scanning (25+ provid
|
|
|
768
905
|
}],
|
|
769
906
|
};
|
|
770
907
|
});
|
|
771
|
-
// Important memories resource
|
|
908
|
+
// Important memories resource (blocked during kill switch)
|
|
772
909
|
server.resource('memory://important', 'memory://important', async () => {
|
|
910
|
+
if (isKillSwitchActive()) {
|
|
911
|
+
return { contents: [{ uri: 'memory://important', mimeType: 'text/plain', text: '[KILL SWITCH ACTIVE] Memory access blocked.' }] };
|
|
912
|
+
}
|
|
773
913
|
const memories = getHighPriorityMemories(20);
|
|
774
914
|
const text = memories.map(m => `## ${m.title}\n${m.content}\n*${m.category} | ${(m.salience * 100).toFixed(0)}% salience*\n`).join('\n');
|
|
775
915
|
return {
|
|
@@ -780,8 +920,11 @@ Runs injection detection (40+ patterns) and credential leak scanning (25+ provid
|
|
|
780
920
|
}],
|
|
781
921
|
};
|
|
782
922
|
});
|
|
783
|
-
// Recent memories resource
|
|
923
|
+
// Recent memories resource (blocked during kill switch)
|
|
784
924
|
server.resource('memory://recent', 'memory://recent', async () => {
|
|
925
|
+
if (isKillSwitchActive()) {
|
|
926
|
+
return { contents: [{ uri: 'memory://recent', mimeType: 'text/plain', text: '[KILL SWITCH ACTIVE] Memory access blocked.' }] };
|
|
927
|
+
}
|
|
785
928
|
const memories = getRecentMemories(15);
|
|
786
929
|
const text = memories.map(m => `- **${m.title}** (${m.category}): ${m.content.slice(0, 100)}...`).join('\n');
|
|
787
930
|
return {
|
|
@@ -795,8 +938,13 @@ Runs injection detection (40+ patterns) and credential leak scanning (25+ provid
|
|
|
795
938
|
// ============================================
|
|
796
939
|
// PROMPTS
|
|
797
940
|
// ============================================
|
|
798
|
-
// Context restoration prompt
|
|
941
|
+
// Context restoration prompt (blocked during kill switch)
|
|
799
942
|
server.prompt('restore_context', 'Restore context after compaction or at session start', async () => {
|
|
943
|
+
if (isKillSwitchActive()) {
|
|
944
|
+
return {
|
|
945
|
+
messages: [{ role: 'user', content: { type: 'text', text: '[KILL SWITCH ACTIVE] Context restoration blocked. Use iron_dome_resume to resume.' } }],
|
|
946
|
+
};
|
|
947
|
+
}
|
|
800
948
|
const summary = await generateContextSummary();
|
|
801
949
|
const context = formatContextSummary(summary);
|
|
802
950
|
return {
|