@frostbridge/imdl 0.1.5 → 0.1.7

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 (2) hide show
  1. package/dist/index.js +133 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -726,6 +726,23 @@ function setPaused(paused) {
726
726
  }
727
727
  }
728
728
  }
729
+ var SESSION_FILE = join3(IMDL_DIR, ".last-session");
730
+ function getLastSessionId() {
731
+ try {
732
+ if (existsSync4(SESSION_FILE)) {
733
+ return readFileSync(SESSION_FILE, "utf-8").trim() || "unknown";
734
+ }
735
+ } catch {
736
+ }
737
+ return "unknown";
738
+ }
739
+ function saveLastSessionId(sessionId) {
740
+ try {
741
+ ensureImdlDir();
742
+ writeFileSync(SESSION_FILE, sessionId, { mode: 384 });
743
+ } catch {
744
+ }
745
+ }
729
746
 
730
747
  // src/mcp/reporter.ts
731
748
  var THROTTLE_FILE = join4(getImdlDir(), "last-mcp-report");
@@ -2784,11 +2801,12 @@ async function sendBatch(apiUrl, sessionId, events, developerId) {
2784
2801
  if (config.authToken) {
2785
2802
  headers["X-IMDL-Key"] = config.authToken;
2786
2803
  }
2804
+ const agentType = events.find((e) => e.agentType)?.agentType;
2787
2805
  try {
2788
2806
  const res = await fetch(`${apiUrl}/api/sessions/${sessionId}/events`, {
2789
2807
  method: "POST",
2790
2808
  headers,
2791
- body: JSON.stringify({ events, developerId }),
2809
+ body: JSON.stringify({ events, developerId, ...agentType && { agentType } }),
2792
2810
  signal: AbortSignal.timeout(FLUSH_TIMEOUT)
2793
2811
  });
2794
2812
  return res.ok;
@@ -3195,7 +3213,9 @@ var CHAT_DATA_KEYS = [
3195
3213
  "workbench.panel.aichat.view.aichat.chatdata",
3196
3214
  "workbench.panel.aichat.view.aichat.chatdata.v2",
3197
3215
  "composer.composerData",
3198
- "composer.allComposers"
3216
+ "composer.allComposers",
3217
+ "aiService.prompts",
3218
+ "aiService.generations"
3199
3219
  ];
3200
3220
  var CursorAdapter = class _CursorAdapter {
3201
3221
  agentType = "cursor";
@@ -3232,6 +3252,11 @@ var CursorAdapter = class _CursorAdapter {
3232
3252
  */
3233
3253
  findVscdbFiles(baseDir) {
3234
3254
  const files = [];
3255
+ const directDb = join16(baseDir, "state.vscdb");
3256
+ if (existsSync17(directDb)) {
3257
+ files.push(directDb);
3258
+ return files;
3259
+ }
3235
3260
  try {
3236
3261
  const entries = readdirSync4(baseDir);
3237
3262
  for (const entry of entries) {
@@ -3262,9 +3287,23 @@ var CursorAdapter = class _CursorAdapter {
3262
3287
  try {
3263
3288
  db = new Database(dbPath, { readonly: true, fileMustExist: true });
3264
3289
  const rows = this.queryAIChatRows(db);
3290
+ let hasGenerations = false;
3265
3291
  for (const row of rows) {
3266
3292
  const parsed = this.parseJsonValue(row.value);
3267
3293
  if (!parsed) continue;
3294
+ if (row.key === "aiService.generations") {
3295
+ const genEvents = this.extractFromGenerations(parsed, dbPath, since);
3296
+ events.push(...genEvents);
3297
+ if (genEvents.length > 0) hasGenerations = true;
3298
+ continue;
3299
+ }
3300
+ if (row.key === "aiService.prompts") {
3301
+ if (!hasGenerations) {
3302
+ const promptEvents = this.extractFromPrompts(parsed, dbPath, since);
3303
+ events.push(...promptEvents);
3304
+ }
3305
+ continue;
3306
+ }
3268
3307
  const conversations = this.extractConversations(parsed);
3269
3308
  for (const conv of conversations) {
3270
3309
  const convEvents = this.conversationToEvents(conv, dbPath, since);
@@ -3280,6 +3319,57 @@ var CursorAdapter = class _CursorAdapter {
3280
3319
  }
3281
3320
  return events;
3282
3321
  }
3322
+ /**
3323
+ * Extract events from aiService.generations format:
3324
+ * [{unixMs, generationUUID, type, textDescription}, ...]
3325
+ */
3326
+ extractFromGenerations(data, dbPath, since) {
3327
+ if (!Array.isArray(data)) return [];
3328
+ const events = [];
3329
+ const sessionId = this.sessionIdFromPath(dbPath);
3330
+ for (const gen of data) {
3331
+ if (!gen || typeof gen !== "object") continue;
3332
+ const { unixMs, textDescription, type } = gen;
3333
+ if (!textDescription || typeof textDescription !== "string") continue;
3334
+ if (!unixMs || typeof unixMs !== "number") continue;
3335
+ if (since && unixMs <= since.getTime()) continue;
3336
+ const genType = type;
3337
+ if (genType === "apply") continue;
3338
+ if (this.isNoisePrompt(textDescription)) continue;
3339
+ events.push({
3340
+ sessionId,
3341
+ type: "user_prompt",
3342
+ prompt: textDescription.slice(0, 1e4),
3343
+ timestamp: new Date(unixMs).toISOString(),
3344
+ agentType: "cursor"
3345
+ });
3346
+ }
3347
+ return events;
3348
+ }
3349
+ /**
3350
+ * Extract events from aiService.prompts format:
3351
+ * [{text, commandType}, ...]
3352
+ * No timestamps — only use if generations is empty.
3353
+ */
3354
+ extractFromPrompts(data, dbPath, _since) {
3355
+ if (!Array.isArray(data) || data.length === 0) return [];
3356
+ const events = [];
3357
+ const sessionId = this.sessionIdFromPath(dbPath);
3358
+ for (const prompt of data) {
3359
+ if (!prompt || typeof prompt !== "object") continue;
3360
+ const { text } = prompt;
3361
+ if (!text || typeof text !== "string") continue;
3362
+ if (this.isNoisePrompt(text)) continue;
3363
+ events.push({
3364
+ sessionId,
3365
+ type: "user_prompt",
3366
+ prompt: text.slice(0, 1e4),
3367
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3368
+ agentType: "cursor"
3369
+ });
3370
+ }
3371
+ return events;
3372
+ }
3283
3373
  /**
3284
3374
  * Query the ItemTable for rows with AI-related keys.
3285
3375
  */
@@ -3608,6 +3698,42 @@ async function collectPrompts() {
3608
3698
  }
3609
3699
  }
3610
3700
  }
3701
+ const cursorWorkspaces = CursorAdapter.getAllWorkspaces();
3702
+ if (cursorWorkspaces.length > 0) {
3703
+ const cursorAdapter = new CursorAdapter();
3704
+ for (const ws of cursorWorkspaces) {
3705
+ try {
3706
+ const events = await cursorAdapter.extractEvents({
3707
+ kind: "directory",
3708
+ path: ws,
3709
+ since: new Date(state.lastRun)
3710
+ });
3711
+ for (const event of events) {
3712
+ let violation;
3713
+ if (event.prompt) {
3714
+ const scan = scanPromptForSecrets(event.prompt);
3715
+ if (scan.hasSecrets) {
3716
+ violation = createPromptViolation(scan.findings);
3717
+ }
3718
+ }
3719
+ const buffered = {
3720
+ sessionId: event.sessionId,
3721
+ type: event.type,
3722
+ toolName: event.toolName,
3723
+ toolInput: event.toolInput ? redactObject(event.toolInput) : void 0,
3724
+ prompt: event.prompt ? redactObject(event.prompt) : void 0,
3725
+ timestamp: event.timestamp,
3726
+ cwd: event.cwd,
3727
+ agentType: "cursor",
3728
+ violation
3729
+ };
3730
+ appendEvent(buffered);
3731
+ collected++;
3732
+ }
3733
+ } catch {
3734
+ }
3735
+ }
3736
+ }
3611
3737
  if (collected > 0) {
3612
3738
  try {
3613
3739
  await tryFlush();
@@ -5690,8 +5816,9 @@ async function handleHook(type) {
5690
5816
  behaviorRiskScore = computeRiskScore(signals);
5691
5817
  }
5692
5818
  }
5819
+ const sessionId = event.session_id || getLastSessionId();
5693
5820
  const buffered = {
5694
- sessionId: event.session_id,
5821
+ sessionId,
5695
5822
  type: hookType,
5696
5823
  toolName: event.tool_name,
5697
5824
  toolInput: redactObject(event.tool_input),
@@ -5703,6 +5830,9 @@ async function handleHook(type) {
5703
5830
  behaviorSignals,
5704
5831
  behaviorRiskScore
5705
5832
  };
5833
+ if (event.session_id) {
5834
+ saveLastSessionId(event.session_id);
5835
+ }
5706
5836
  appendEvent(buffered);
5707
5837
  try {
5708
5838
  await tryFlush();
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "restricted"
5
5
  },
6
- "version": "0.1.5",
6
+ "version": "0.1.7",
7
7
  "description": "IMDL — Intelligent Mediation & Detection Layer. AI agent security CLI.",
8
8
  "files": [
9
9
  "dist"