@frostbridge/imdl 0.1.6 → 0.1.8

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 +262 -23
  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");
@@ -2671,7 +2688,7 @@ function resumeCommand() {
2671
2688
  import pc6 from "picocolors";
2672
2689
 
2673
2690
  // src/adapters/index.ts
2674
- import { existsSync as existsSync18, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
2691
+ import { existsSync as existsSync18, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
2675
2692
  import { join as join17 } from "path";
2676
2693
 
2677
2694
  // src/transport/buffer.ts
@@ -3180,7 +3197,7 @@ var ClaudeCodeAdapter = class {
3180
3197
  };
3181
3198
 
3182
3199
  // src/adapters/cursor.ts
3183
- import { existsSync as existsSync17, readdirSync as readdirSync4, statSync as statSync9 } from "fs";
3200
+ import { existsSync as existsSync17, readdirSync as readdirSync4, readFileSync as readFileSync12, statSync as statSync9 } from "fs";
3184
3201
  import { join as join16 } from "path";
3185
3202
  import { homedir as homedir13 } from "os";
3186
3203
  import { createRequire } from "module";
@@ -3196,7 +3213,9 @@ var CHAT_DATA_KEYS = [
3196
3213
  "workbench.panel.aichat.view.aichat.chatdata",
3197
3214
  "workbench.panel.aichat.view.aichat.chatdata.v2",
3198
3215
  "composer.composerData",
3199
- "composer.allComposers"
3216
+ "composer.allComposers",
3217
+ "aiService.prompts",
3218
+ "aiService.generations"
3200
3219
  ];
3201
3220
  var CursorAdapter = class _CursorAdapter {
3202
3221
  agentType = "cursor";
@@ -3233,6 +3252,11 @@ var CursorAdapter = class _CursorAdapter {
3233
3252
  */
3234
3253
  findVscdbFiles(baseDir) {
3235
3254
  const files = [];
3255
+ const directDb = join16(baseDir, "state.vscdb");
3256
+ if (existsSync17(directDb)) {
3257
+ files.push(directDb);
3258
+ return files;
3259
+ }
3236
3260
  try {
3237
3261
  const entries = readdirSync4(baseDir);
3238
3262
  for (const entry of entries) {
@@ -3263,9 +3287,23 @@ var CursorAdapter = class _CursorAdapter {
3263
3287
  try {
3264
3288
  db = new Database(dbPath, { readonly: true, fileMustExist: true });
3265
3289
  const rows = this.queryAIChatRows(db);
3290
+ let hasGenerations = false;
3266
3291
  for (const row of rows) {
3267
3292
  const parsed = this.parseJsonValue(row.value);
3268
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
+ }
3269
3307
  const conversations = this.extractConversations(parsed);
3270
3308
  for (const conv of conversations) {
3271
3309
  const convEvents = this.conversationToEvents(conv, dbPath, since);
@@ -3281,6 +3319,57 @@ var CursorAdapter = class _CursorAdapter {
3281
3319
  }
3282
3320
  return events;
3283
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
+ }
3284
3373
  /**
3285
3374
  * Query the ItemTable for rows with AI-related keys.
3286
3375
  */
@@ -3490,6 +3579,80 @@ var CursorAdapter = class _CursorAdapter {
3490
3579
  }
3491
3580
  return `cursor-${parts[parts.length - 2] || "unknown"}`;
3492
3581
  }
3582
+ /**
3583
+ * Scan Cursor's agent-transcripts JSONL files (real-time, like Claude Code).
3584
+ * Path: ~/.cursor/projects/{project}/agent-transcripts/{session}/{session}.jsonl
3585
+ * Uses offsets to only read new lines since last scan.
3586
+ */
3587
+ extractFromTranscripts(since, offsets) {
3588
+ const projectsDir = join16(homedir13(), ".cursor", "projects");
3589
+ if (!existsSync17(projectsDir)) return { events: [], newOffsets: offsets };
3590
+ const events = [];
3591
+ const newOffsets = { ...offsets };
3592
+ try {
3593
+ const projects = readdirSync4(projectsDir);
3594
+ for (const proj of projects) {
3595
+ const transcriptsDir = join16(projectsDir, proj, "agent-transcripts");
3596
+ if (!existsSync17(transcriptsDir)) continue;
3597
+ let sessionDirs;
3598
+ try {
3599
+ sessionDirs = readdirSync4(transcriptsDir);
3600
+ } catch {
3601
+ continue;
3602
+ }
3603
+ for (const sessionDir of sessionDirs) {
3604
+ const jsonlPath = join16(transcriptsDir, sessionDir, `${sessionDir}.jsonl`);
3605
+ if (!existsSync17(jsonlPath)) continue;
3606
+ if (since) {
3607
+ try {
3608
+ const stat = statSync9(jsonlPath);
3609
+ if (stat.mtimeMs < since.getTime()) continue;
3610
+ } catch {
3611
+ continue;
3612
+ }
3613
+ }
3614
+ try {
3615
+ const content = readFileSync12(jsonlPath, "utf-8");
3616
+ const lines = content.split("\n").filter((l) => l.trim());
3617
+ const lastOffset = offsets[jsonlPath] || 0;
3618
+ if (lines.length <= lastOffset) continue;
3619
+ const newLines = lines.slice(lastOffset);
3620
+ newOffsets[jsonlPath] = lines.length;
3621
+ const projectPath = "/" + proj.replace(/-/g, "/").replace(/^\//, "");
3622
+ for (const line of newLines) {
3623
+ let entry;
3624
+ try {
3625
+ entry = JSON.parse(line);
3626
+ } catch {
3627
+ continue;
3628
+ }
3629
+ if (entry.role !== "user") continue;
3630
+ const msg = entry.message?.content;
3631
+ if (!Array.isArray(msg)) continue;
3632
+ let text = "";
3633
+ for (const block of msg) {
3634
+ if (block?.type === "text" && block.text) {
3635
+ text += block.text.replace(/<user_query>\n?/g, "").replace(/\n?<\/user_query>/g, "").trim();
3636
+ }
3637
+ }
3638
+ if (!text || this.isNoisePrompt(text)) continue;
3639
+ events.push({
3640
+ sessionId: `cursor-${sessionDir.slice(0, 8)}`,
3641
+ type: "user_prompt",
3642
+ prompt: text.slice(0, 1e4),
3643
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3644
+ cwd: projectPath,
3645
+ agentType: "cursor"
3646
+ });
3647
+ }
3648
+ } catch {
3649
+ }
3650
+ }
3651
+ }
3652
+ } catch {
3653
+ }
3654
+ return { events, newOffsets };
3655
+ }
3493
3656
  /**
3494
3657
  * Returns the workspace storage path for Cursor, or null if not found.
3495
3658
  */
@@ -3521,6 +3684,13 @@ var CursorAdapter = class _CursorAdapter {
3521
3684
  return [];
3522
3685
  }
3523
3686
  }
3687
+ /**
3688
+ * Returns the Cursor projects directory for transcript scanning.
3689
+ */
3690
+ static getProjectsDir() {
3691
+ const dir = join16(homedir13(), ".cursor", "projects");
3692
+ return existsSync17(dir) ? dir : null;
3693
+ }
3524
3694
  };
3525
3695
 
3526
3696
  // src/adapters/index.ts
@@ -3528,7 +3698,7 @@ var STATE_FILE = join17(getImdlDir(), "adapter-state.json");
3528
3698
  function loadState() {
3529
3699
  try {
3530
3700
  if (existsSync18(STATE_FILE)) {
3531
- return JSON.parse(readFileSync12(STATE_FILE, "utf-8"));
3701
+ return JSON.parse(readFileSync13(STATE_FILE, "utf-8"));
3532
3702
  }
3533
3703
  } catch {
3534
3704
  }
@@ -3609,6 +3779,71 @@ async function collectPrompts() {
3609
3779
  }
3610
3780
  }
3611
3781
  }
3782
+ const cursorAdapter = new CursorAdapter();
3783
+ if (CursorAdapter.getProjectsDir()) {
3784
+ try {
3785
+ const { events: transcriptEvents, newOffsets } = cursorAdapter.extractFromTranscripts(new Date(state.lastRun), state.offsets);
3786
+ state.offsets = newOffsets;
3787
+ for (const event of transcriptEvents) {
3788
+ let violation;
3789
+ if (event.prompt) {
3790
+ const scan = scanPromptForSecrets(event.prompt);
3791
+ if (scan.hasSecrets) {
3792
+ violation = createPromptViolation(scan.findings);
3793
+ }
3794
+ }
3795
+ const buffered = {
3796
+ sessionId: event.sessionId,
3797
+ type: event.type,
3798
+ toolName: event.toolName,
3799
+ toolInput: event.toolInput ? redactObject(event.toolInput) : void 0,
3800
+ prompt: event.prompt ? redactObject(event.prompt) : void 0,
3801
+ timestamp: event.timestamp,
3802
+ cwd: event.cwd,
3803
+ agentType: "cursor",
3804
+ violation
3805
+ };
3806
+ appendEvent(buffered);
3807
+ collected++;
3808
+ }
3809
+ } catch {
3810
+ }
3811
+ }
3812
+ const cursorWorkspaces = CursorAdapter.getAllWorkspaces();
3813
+ if (cursorWorkspaces.length > 0) {
3814
+ for (const ws of cursorWorkspaces) {
3815
+ try {
3816
+ const events = await cursorAdapter.extractEvents({
3817
+ kind: "directory",
3818
+ path: ws,
3819
+ since: new Date(state.lastRun)
3820
+ });
3821
+ for (const event of events) {
3822
+ let violation;
3823
+ if (event.prompt) {
3824
+ const scan = scanPromptForSecrets(event.prompt);
3825
+ if (scan.hasSecrets) {
3826
+ violation = createPromptViolation(scan.findings);
3827
+ }
3828
+ }
3829
+ const buffered = {
3830
+ sessionId: event.sessionId,
3831
+ type: event.type,
3832
+ toolName: event.toolName,
3833
+ toolInput: event.toolInput ? redactObject(event.toolInput) : void 0,
3834
+ prompt: event.prompt ? redactObject(event.prompt) : void 0,
3835
+ timestamp: event.timestamp,
3836
+ cwd: event.cwd,
3837
+ agentType: "cursor",
3838
+ violation
3839
+ };
3840
+ appendEvent(buffered);
3841
+ collected++;
3842
+ }
3843
+ } catch {
3844
+ }
3845
+ }
3846
+ }
3612
3847
  if (collected > 0) {
3613
3848
  try {
3614
3849
  await tryFlush();
@@ -3633,7 +3868,7 @@ async function collectCommand() {
3633
3868
 
3634
3869
  // src/commands/daemon.ts
3635
3870
  import pc7 from "picocolors";
3636
- import { existsSync as existsSync19, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
3871
+ import { existsSync as existsSync19, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
3637
3872
  import { join as join18 } from "path";
3638
3873
  var INTERVAL_MS = 3e4;
3639
3874
  async function daemonCommand() {
@@ -3658,7 +3893,7 @@ async function daemonCommand() {
3658
3893
  async function ingestShellEvents() {
3659
3894
  const shellLog = join18(getBufferDir(), "shell-events.ndjson");
3660
3895
  if (!existsSync19(shellLog)) return 0;
3661
- const content = readFileSync13(shellLog, "utf-8").trim();
3896
+ const content = readFileSync14(shellLog, "utf-8").trim();
3662
3897
  if (!content) return 0;
3663
3898
  const config = loadConfig();
3664
3899
  const lines = content.split("\n");
@@ -3763,7 +3998,7 @@ async function requestCommand(options) {
3763
3998
  // src/commands/lock.ts
3764
3999
  import pc9 from "picocolors";
3765
4000
  import { createHash as createHash2 } from "crypto";
3766
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, existsSync as existsSync20 } from "fs";
4001
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, existsSync as existsSync20 } from "fs";
3767
4002
  import { join as join19, resolve as resolve3 } from "path";
3768
4003
  var LOCKFILE_NAME = "mcp-lock.json";
3769
4004
  function computeIntegrity(server, identity) {
@@ -3861,7 +4096,7 @@ async function verifyCommand(options) {
3861
4096
  }
3862
4097
  let lockfile;
3863
4098
  try {
3864
- lockfile = JSON.parse(readFileSync14(lockPath, "utf-8"));
4099
+ lockfile = JSON.parse(readFileSync15(lockPath, "utf-8"));
3865
4100
  } catch {
3866
4101
  console.log(pc9.red("Failed to parse lockfile."));
3867
4102
  process.exit(1);
@@ -4270,7 +4505,7 @@ async function checkCompliance(developerId) {
4270
4505
  }
4271
4506
 
4272
4507
  // src/permissions/fixer.ts
4273
- import { existsSync as existsSync21, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
4508
+ import { existsSync as existsSync21, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
4274
4509
  import { join as join20 } from "path";
4275
4510
  import { homedir as homedir14 } from "os";
4276
4511
  function buildFixesFromChanges(changes) {
@@ -4288,7 +4523,7 @@ function buildFixForAgent(change, home) {
4288
4523
  const configPath = join20(home, ".claude", "settings.json");
4289
4524
  if (!existsSync21(configPath)) return null;
4290
4525
  try {
4291
- const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4526
+ const settings = JSON.parse(readFileSync16(configPath, "utf-8"));
4292
4527
  const tools = settings.allowedTools || [];
4293
4528
  if (category === "shell") {
4294
4529
  if (!tools.includes("Bash") && !tools.some((t) => t.startsWith("Bash("))) return null;
@@ -4319,7 +4554,7 @@ function buildFixForAgent(change, home) {
4319
4554
  const configPath = join20(home, ".cursor", "settings.json");
4320
4555
  if (!existsSync21(configPath)) return null;
4321
4556
  try {
4322
- const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4557
+ const settings = JSON.parse(readFileSync16(configPath, "utf-8"));
4323
4558
  if ((category === "agentic_mode" || category === "shell") && settings["cursor.composer.yoloMode"] === true) {
4324
4559
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable YOLO mode" };
4325
4560
  }
@@ -4331,7 +4566,7 @@ function buildFixForAgent(change, home) {
4331
4566
  const configPath = join20(home, ".codex", "config.json");
4332
4567
  if (!existsSync21(configPath)) return null;
4333
4568
  try {
4334
- const config = JSON.parse(readFileSync15(configPath, "utf-8"));
4569
+ const config = JSON.parse(readFileSync16(configPath, "utf-8"));
4335
4570
  if ((category === "agentic_mode" || category === "shell") && config.sandbox_mode === "full_auto") {
4336
4571
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Switch to supervised mode" };
4337
4572
  }
@@ -4343,7 +4578,7 @@ function buildFixForAgent(change, home) {
4343
4578
  const configPath = join20(home, "Library", "Application Support", "Code", "User", "settings.json");
4344
4579
  if (!existsSync21(configPath)) return null;
4345
4580
  try {
4346
- const settings = JSON.parse(readFileSync15(configPath, "utf-8"));
4581
+ const settings = JSON.parse(readFileSync16(configPath, "utf-8"));
4347
4582
  if ((category === "agentic_mode" || category === "shell") && (settings["github.copilot.chat.agent.autoApprove"] || settings["chat.agent.autoApprove"])) {
4348
4583
  return { agentType, category: "agentic_mode", currentGrant: "unrestricted", newGrant: "confirmation", configPath, description: "Disable autoApprove" };
4349
4584
  }
@@ -4356,7 +4591,7 @@ function buildFixForAgent(change, home) {
4356
4591
  const configPath = join20(home, ".windsurf", "mcp.json");
4357
4592
  if (!existsSync21(configPath)) return null;
4358
4593
  try {
4359
- const config = JSON.parse(readFileSync15(configPath, "utf-8"));
4594
+ const config = JSON.parse(readFileSync16(configPath, "utf-8"));
4360
4595
  const servers = Object.keys(config.mcpServers || {});
4361
4596
  if (servers.length === 0) return null;
4362
4597
  return { agentType, category, currentGrant: "unrestricted", newGrant: "denied", configPath, description: `Remove MCP servers (${servers.join(", ")})` };
@@ -4369,7 +4604,7 @@ function buildFixForAgent(change, home) {
4369
4604
  }
4370
4605
  function applyFix(fix) {
4371
4606
  try {
4372
- const content = readFileSync15(fix.configPath, "utf-8");
4607
+ const content = readFileSync16(fix.configPath, "utf-8");
4373
4608
  const config = JSON.parse(content);
4374
4609
  let modified = false;
4375
4610
  if (fix.agentType === "claude-code") {
@@ -4660,7 +4895,7 @@ function grantLevel(grant) {
4660
4895
 
4661
4896
  // src/commands/gateway.ts
4662
4897
  import pc12 from "picocolors";
4663
- import { existsSync as existsSync22, readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync3 } from "fs";
4898
+ import { existsSync as existsSync22, readFileSync as readFileSync17, writeFileSync as writeFileSync10, mkdirSync as mkdirSync3 } from "fs";
4664
4899
  import { join as join21 } from "path";
4665
4900
  import { homedir as homedir15 } from "os";
4666
4901
  import { execSync as execSync2, spawn } from "child_process";
@@ -4806,7 +5041,7 @@ async function gatewayAlertsCommand(opts) {
4806
5041
  console.log(pc12.dim("No alerts logged yet."));
4807
5042
  return;
4808
5043
  }
4809
- const lines = readFileSync16(logPath, "utf-8").split("\n").filter(Boolean);
5044
+ const lines = readFileSync17(logPath, "utf-8").split("\n").filter(Boolean);
4810
5045
  const limit = opts.limit ? parseInt(opts.limit, 10) : 20;
4811
5046
  const recent = lines.slice(-limit);
4812
5047
  if (opts.json) {
@@ -4910,7 +5145,7 @@ async function getGatewayStatus() {
4910
5145
  function getRunningPid() {
4911
5146
  if (!existsSync22(GATEWAY_PID_FILE)) return null;
4912
5147
  try {
4913
- const pid = parseInt(readFileSync16(GATEWAY_PID_FILE, "utf-8").trim(), 10);
5148
+ const pid = parseInt(readFileSync17(GATEWAY_PID_FILE, "utf-8").trim(), 10);
4914
5149
  return isNaN(pid) ? null : pid;
4915
5150
  } catch {
4916
5151
  return null;
@@ -4927,7 +5162,7 @@ function isProcessAlive(pid) {
4927
5162
  function getConfiguredPort() {
4928
5163
  if (!existsSync22(GATEWAY_CONFIG)) return DEFAULT_PORT;
4929
5164
  try {
4930
- const content = readFileSync16(GATEWAY_CONFIG, "utf-8");
5165
+ const content = readFileSync17(GATEWAY_CONFIG, "utf-8");
4931
5166
  const match = content.match(/listen_port\s*=\s*(\d+)/);
4932
5167
  return match ? parseInt(match[1], 10) : DEFAULT_PORT;
4933
5168
  } catch {
@@ -4999,7 +5234,7 @@ function loadGatewayToml() {
4999
5234
  if (!existsSync22(GATEWAY_CONFIG)) {
5000
5235
  ensureConfig(DEFAULT_PORT);
5001
5236
  }
5002
- return readFileSync16(GATEWAY_CONFIG, "utf-8");
5237
+ return readFileSync17(GATEWAY_CONFIG, "utf-8");
5003
5238
  }
5004
5239
  function saveGatewayToml(content) {
5005
5240
  writeFileSync10(GATEWAY_CONFIG, content, { mode: 384 });
@@ -5332,7 +5567,7 @@ function ruleTypeToCategory(type) {
5332
5567
  }
5333
5568
 
5334
5569
  // src/transport/sync.ts
5335
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, existsSync as existsSync23 } from "fs";
5570
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync11, existsSync as existsSync23 } from "fs";
5336
5571
  import { join as join22 } from "path";
5337
5572
  var SYNC_INTERVAL = 6e4;
5338
5573
  var SYNC_TIMEOUT = 3e3;
@@ -5343,7 +5578,7 @@ function getLastSyncTime() {
5343
5578
  try {
5344
5579
  const file = getSyncStateFile();
5345
5580
  if (existsSync23(file)) {
5346
- const data = JSON.parse(readFileSync17(file, "utf-8"));
5581
+ const data = JSON.parse(readFileSync18(file, "utf-8"));
5347
5582
  return data.lastSync || 0;
5348
5583
  }
5349
5584
  } catch {
@@ -5691,8 +5926,9 @@ async function handleHook(type) {
5691
5926
  behaviorRiskScore = computeRiskScore(signals);
5692
5927
  }
5693
5928
  }
5929
+ const sessionId = event.session_id || getLastSessionId();
5694
5930
  const buffered = {
5695
- sessionId: event.session_id,
5931
+ sessionId,
5696
5932
  type: hookType,
5697
5933
  toolName: event.tool_name,
5698
5934
  toolInput: redactObject(event.tool_input),
@@ -5704,6 +5940,9 @@ async function handleHook(type) {
5704
5940
  behaviorSignals,
5705
5941
  behaviorRiskScore
5706
5942
  };
5943
+ if (event.session_id) {
5944
+ saveLastSessionId(event.session_id);
5945
+ }
5707
5946
  appendEvent(buffered);
5708
5947
  try {
5709
5948
  await tryFlush();
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "restricted"
5
5
  },
6
- "version": "0.1.6",
6
+ "version": "0.1.8",
7
7
  "description": "IMDL — Intelligent Mediation & Detection Layer. AI agent security CLI.",
8
8
  "files": [
9
9
  "dist"