@fenglimg/fabric-server 1.6.0 → 1.7.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.
@@ -2,26 +2,25 @@ import {
2
2
  AGENTS_MD_RESOURCE_URI,
3
3
  AgentsMetaFileMissingError,
4
4
  AgentsMetaInvalidError,
5
+ EVENT_LEDGER_PATH,
5
6
  LEDGER_PATH,
6
7
  LEGACY_LEDGER_PATH,
7
- appendLedgerEntry,
8
- approveHumanLock,
8
+ appendEventLedgerEvent,
9
9
  contextCache,
10
- ensureParentDirectory,
10
+ getEventLedgerPath,
11
11
  getLedgerPath,
12
12
  getLegacyLedgerPath,
13
13
  getRules,
14
+ isNodeError,
14
15
  readAgentsMeta,
15
- readHumanLock,
16
- readHumanLockEntry,
17
- readLedger,
18
- resolveLedgerPaths,
19
- runDoctorReport
20
- } from "./chunk-TZCE2K4D.js";
16
+ readEventLedger,
17
+ runDoctorReport,
18
+ sha256
19
+ } from "./chunk-PTFSYO4Y.js";
21
20
 
22
21
  // src/http.ts
23
- import { randomUUID } from "crypto";
24
- import { appendFile, readFile as readFile2 } from "fs/promises";
22
+ import { randomUUID as randomUUID2 } from "crypto";
23
+ import { readFile as readFile3 } from "fs/promises";
25
24
  import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
26
25
  import {
27
26
  StreamableHTTPServerTransport
@@ -117,21 +116,186 @@ function registerDoctorApi(app, projectRoot) {
117
116
  }
118
117
 
119
118
  // src/api/events.ts
120
- import { createHash } from "crypto";
121
- import { open, readFile, stat } from "fs/promises";
119
+ import { open, readFile as readFile2, stat } from "fs/promises";
122
120
  import { join } from "path";
123
121
  import {
124
122
  agentsMetaSchema,
125
123
  fabricEventSchema,
126
124
  forensicReportSchema,
127
- humanLockFileSchema,
128
- ledgerEntrySchema
125
+ ledgerEntrySchema as ledgerEntrySchema2
129
126
  } from "@fenglimg/fabric-shared";
127
+ import { eventLedgerEventSchema } from "@fenglimg/fabric-shared";
130
128
  import chokidar from "chokidar";
129
+
130
+ // src/services/read-ledger.ts
131
+ import { randomUUID } from "crypto";
132
+ import { access, copyFile, readFile, rm } from "fs/promises";
133
+ import { ledgerEntrySchema } from "@fenglimg/fabric-shared";
134
+ async function resolveLedgerPaths(projectRoot) {
135
+ const primaryPath = getLedgerPath(projectRoot);
136
+ const legacyPath = getLegacyLedgerPath(projectRoot);
137
+ const [primaryExists, legacyExists] = await Promise.all([
138
+ pathExists(primaryPath),
139
+ pathExists(legacyPath)
140
+ ]);
141
+ return {
142
+ primaryPath,
143
+ legacyPath,
144
+ readPath: primaryExists ? primaryPath : legacyPath,
145
+ usingLegacy: !primaryExists && legacyExists
146
+ };
147
+ }
148
+ async function readLedger(projectRoot, options = {}) {
149
+ const [legacyEntries, eventEntries] = await Promise.all([
150
+ readLegacyLedger(projectRoot),
151
+ readLedgerFromEventLedger(projectRoot)
152
+ ]);
153
+ const entries = mergeLedgerEntries(legacyEntries, eventEntries);
154
+ return entries.filter((entry) => options.source === void 0 || entry.source === options.source).filter((entry) => options.since === void 0 || entry.ts >= options.since);
155
+ }
156
+ async function readLegacyLedger(projectRoot) {
157
+ const { readPath } = await resolveLedgerPaths(projectRoot);
158
+ let raw;
159
+ try {
160
+ raw = await readFile(readPath, "utf8");
161
+ } catch (error) {
162
+ if (isNodeError(error) && error.code === "ENOENT") {
163
+ return [];
164
+ }
165
+ throw error;
166
+ }
167
+ return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line, index) => parseLedgerLine(line, index)).filter((entry) => entry !== null);
168
+ }
169
+ async function appendLedgerEntry(projectRoot, entry) {
170
+ const nextEntry = createStoredLedgerEntry(entry);
171
+ for (const affectedPath of nextEntry.affected_paths) {
172
+ await appendEventLedgerEvent(projectRoot, {
173
+ event_type: "edit_intent_checked",
174
+ ts: nextEntry.ts,
175
+ path: affectedPath,
176
+ compliant: true,
177
+ intent: nextEntry.intent,
178
+ ledger_entry_id: nextEntry.id,
179
+ ledger_source: nextEntry.source,
180
+ commit_sha: nextEntry.source === "ai" ? nextEntry.commit_sha : void 0,
181
+ parent_sha: nextEntry.source === "human" ? nextEntry.parent_sha : void 0,
182
+ parent_ledger_entry_id: nextEntry.source === "human" ? nextEntry.parent_ledger_entry_id : void 0,
183
+ diff_stat: nextEntry.source === "human" ? nextEntry.diff_stat : void 0,
184
+ annotation: nextEntry.source === "human" ? nextEntry.annotation : void 0,
185
+ matched_rule_context_ts: null,
186
+ window_ms: 0
187
+ });
188
+ }
189
+ return nextEntry;
190
+ }
191
+ function createStoredLedgerEntry(entry) {
192
+ return ledgerEntrySchema.parse({
193
+ ...entry,
194
+ id: entry.id ?? `ledger:${randomUUID()}`
195
+ });
196
+ }
197
+ function parseLedgerLine(line, index) {
198
+ try {
199
+ const parsed = JSON.parse(line);
200
+ if (parsed.kind === "mcp-event") {
201
+ return null;
202
+ }
203
+ const result = ledgerEntrySchema.safeParse(parsed);
204
+ if (!result.success) {
205
+ return null;
206
+ }
207
+ return {
208
+ ...result.data,
209
+ id: result.data.id ?? createDerivedId(index, line)
210
+ };
211
+ } catch {
212
+ return null;
213
+ }
214
+ }
215
+ async function readLedgerFromEventLedger(projectRoot) {
216
+ const events = await readEventLedger(projectRoot);
217
+ const grouped = /* @__PURE__ */ new Map();
218
+ for (const event of events) {
219
+ const entry = projectLedgerEvent(event);
220
+ if (entry === null) {
221
+ continue;
222
+ }
223
+ const existing = grouped.get(entry.id);
224
+ if (existing === void 0) {
225
+ grouped.set(entry.id, entry);
226
+ continue;
227
+ }
228
+ grouped.set(entry.id, {
229
+ ...existing,
230
+ ts: Math.min(existing.ts, entry.ts),
231
+ affected_paths: dedupeStrings([...existing.affected_paths, ...entry.affected_paths])
232
+ });
233
+ }
234
+ return Array.from(grouped.values());
235
+ }
236
+ function projectLedgerEvent(event) {
237
+ if (event.event_type !== "edit_intent_checked") {
238
+ return null;
239
+ }
240
+ const base = {
241
+ id: event.ledger_entry_id,
242
+ ts: event.ts,
243
+ intent: event.intent,
244
+ affected_paths: [event.path]
245
+ };
246
+ if (event.ledger_source === "human") {
247
+ return {
248
+ ...base,
249
+ source: "human",
250
+ parent_sha: event.parent_sha ?? event.ledger_entry_id,
251
+ parent_ledger_entry_id: event.parent_ledger_entry_id,
252
+ diff_stat: event.diff_stat ?? "event-ledger",
253
+ annotation: event.annotation
254
+ };
255
+ }
256
+ return {
257
+ ...base,
258
+ source: "ai",
259
+ commit_sha: event.commit_sha
260
+ };
261
+ }
262
+ function mergeLedgerEntries(legacyEntries, eventEntries) {
263
+ const byId = /* @__PURE__ */ new Map();
264
+ for (const entry of [...legacyEntries, ...eventEntries]) {
265
+ if (!byId.has(entry.id)) {
266
+ byId.set(entry.id, entry);
267
+ }
268
+ }
269
+ return Array.from(byId.values()).sort((left, right) => left.ts - right.ts);
270
+ }
271
+ function dedupeStrings(values) {
272
+ return Array.from(new Set(values));
273
+ }
274
+ function createDerivedId(index, line) {
275
+ return `ledger:${index + 1}:${sha256(line).slice("sha256:".length)}`;
276
+ }
277
+ async function pathExists(path) {
278
+ try {
279
+ await access(path);
280
+ return true;
281
+ } catch (error) {
282
+ if (isNodeError(error) && error.code === "ENOENT") {
283
+ return false;
284
+ }
285
+ throw error;
286
+ }
287
+ }
288
+
289
+ // src/api/events.ts
131
290
  var AGENTS_META_PATH = ".fabric/agents.meta.json";
132
- var HUMAN_LOCK_PATH = ".fabric/human-lock.json";
133
291
  var FORENSIC_PATH = ".fabric/forensic.json";
134
- var WATCHED_PATHS = [AGENTS_META_PATH, HUMAN_LOCK_PATH, FORENSIC_PATH, LEDGER_PATH, LEGACY_LEDGER_PATH];
292
+ var WATCHED_PATHS = [
293
+ AGENTS_META_PATH,
294
+ FORENSIC_PATH,
295
+ EVENT_LEDGER_PATH,
296
+ LEDGER_PATH,
297
+ LEGACY_LEDGER_PATH
298
+ ];
135
299
  var CONNECTION_LIMIT = 10;
136
300
  var HEARTBEAT_INTERVAL_MS = 3e4;
137
301
  var WATCH_DEBOUNCE_MS = 75;
@@ -173,7 +337,8 @@ function createEventsHandler(options) {
173
337
  activeLedgerPath: getLedgerPath(projectRoot),
174
338
  ledgerOffset: 0,
175
339
  ledgerRemainder: "",
176
- humanLockSnapshot: createEmptyHumanLockSnapshot(),
340
+ eventLedgerOffset: 0,
341
+ eventLedgerRemainder: "",
177
342
  nextEventId: 1,
178
343
  ringBuffer: new RingBuffer(RING_BUFFER_CAPACITY)
179
344
  };
@@ -252,7 +417,8 @@ async function ensureWatcher(state, projectRoot) {
252
417
  state.activeLedgerPath = ledgerState.path;
253
418
  state.ledgerOffset = ledgerState.size;
254
419
  state.ledgerRemainder = "";
255
- state.humanLockSnapshot = await readHumanLockSnapshot(projectRoot);
420
+ state.eventLedgerOffset = await readFileSize(getEventLedgerPath(projectRoot));
421
+ state.eventLedgerRemainder = "";
256
422
  const watcher = chokidar.watch([...WATCHED_PATHS], {
257
423
  cwd: projectRoot,
258
424
  ignoreInitial: true,
@@ -306,13 +472,13 @@ async function readEventsForFile(state, projectRoot, relativePath) {
306
472
  const event = await readMetaUpdatedEvent(projectRoot);
307
473
  return event === null ? [] : [event];
308
474
  }
309
- if (relativePath === HUMAN_LOCK_PATH) {
310
- return await readHumanLockEvents(state, projectRoot);
311
- }
312
475
  if (relativePath === FORENSIC_PATH) {
313
476
  const event = await readDriftDetectedEvent(projectRoot);
314
477
  return event === null ? [] : [event];
315
478
  }
479
+ if (relativePath === EVENT_LEDGER_PATH) {
480
+ return await readEventLedgerAppendedEvents(state, projectRoot);
481
+ }
316
482
  if (relativePath === LEDGER_PATH || relativePath === LEGACY_LEDGER_PATH) {
317
483
  return await readLedgerAppendedEvents(state, projectRoot);
318
484
  }
@@ -342,40 +508,6 @@ async function readDriftDetectedEvent(projectRoot) {
342
508
  payload: parsed
343
509
  };
344
510
  }
345
- async function readHumanLockEvents(state, projectRoot) {
346
- const previousSnapshot = state.humanLockSnapshot;
347
- const currentSnapshot = await readHumanLockSnapshot(projectRoot);
348
- state.humanLockSnapshot = currentSnapshot;
349
- const changedEntries = currentSnapshot.locked.filter((entry) => {
350
- const key = getHumanLockKey(entry);
351
- return previousSnapshot.hashByKey.get(key) !== entry.hash;
352
- });
353
- const approvedEntries = changedEntries.filter((entry) => {
354
- const key = getHumanLockKey(entry);
355
- return currentSnapshot.actualHashByKey.get(key) === entry.hash;
356
- });
357
- const driftChanged = !areSetsEqual(previousSnapshot.driftedKeys, currentSnapshot.driftedKeys);
358
- const events = [];
359
- if (approvedEntries.length > 0 || changedEntries.length > 0 && currentSnapshot.drifted.length === 0) {
360
- events.push({
361
- type: "lock:approved",
362
- payload: {
363
- locked: currentSnapshot.locked,
364
- approved: approvedEntries.length > 0 ? approvedEntries : changedEntries
365
- }
366
- });
367
- }
368
- if (currentSnapshot.drifted.length > 0 && (driftChanged || approvedEntries.length === 0)) {
369
- events.push({
370
- type: "lock:drift",
371
- payload: {
372
- locked: currentSnapshot.locked,
373
- drifted: currentSnapshot.drifted
374
- }
375
- });
376
- }
377
- return events;
378
- }
379
511
  async function readLedgerAppendedEvents(state, projectRoot) {
380
512
  const ledgerState = await resolveLedgerWatchState(projectRoot);
381
513
  const ledgerPath = ledgerState.path;
@@ -407,6 +539,31 @@ async function readLedgerAppendedEvents(state, projectRoot) {
407
539
  await handle.close();
408
540
  }
409
541
  }
542
+ async function readEventLedgerAppendedEvents(state, projectRoot) {
543
+ const eventLedgerPath = getEventLedgerPath(projectRoot);
544
+ const nextSize = await readFileSize(eventLedgerPath);
545
+ if (nextSize < state.eventLedgerOffset) {
546
+ state.eventLedgerOffset = 0;
547
+ state.eventLedgerRemainder = "";
548
+ }
549
+ if (nextSize === state.eventLedgerOffset) {
550
+ return [];
551
+ }
552
+ const startOffset = state.eventLedgerOffset;
553
+ state.eventLedgerOffset = nextSize;
554
+ const handle = await open(eventLedgerPath, "r");
555
+ try {
556
+ const length = nextSize - startOffset;
557
+ const buffer = Buffer.alloc(length);
558
+ await handle.read(buffer, 0, length, startOffset);
559
+ const chunk = `${state.eventLedgerRemainder}${buffer.toString("utf8")}`;
560
+ const lines = chunk.split(/\r?\n/);
561
+ state.eventLedgerRemainder = chunk.endsWith("\n") ? "" : lines.pop() ?? "";
562
+ return lines.map((line) => line.trim()).filter((line) => line.length > 0).map(parseEventLedgerAppendedEvent).filter((event) => event !== null);
563
+ } finally {
564
+ await handle.close();
565
+ }
566
+ }
410
567
  async function resolveLedgerWatchState(projectRoot) {
411
568
  const paths = await resolveLedgerPaths(projectRoot);
412
569
  const path = paths.usingLegacy ? paths.legacyPath : paths.primaryPath;
@@ -419,7 +576,7 @@ function parseLedgerAppendedEvent(line) {
419
576
  if (parsed.kind === "mcp-event") {
420
577
  return null;
421
578
  }
422
- const validation = ledgerEntrySchema.safeParse(parsed);
579
+ const validation = ledgerEntrySchema2.safeParse(parsed);
423
580
  if (!validation.success) {
424
581
  return null;
425
582
  }
@@ -431,6 +588,26 @@ function parseLedgerAppendedEvent(line) {
431
588
  return null;
432
589
  }
433
590
  }
591
+ function parseEventLedgerAppendedEvent(line) {
592
+ try {
593
+ const parsed = eventLedgerEventSchema.safeParse(JSON.parse(line));
594
+ if (!parsed.success || parsed.data.event_type !== "edit_intent_checked") {
595
+ return null;
596
+ }
597
+ return {
598
+ type: "ledger:appended",
599
+ payload: {
600
+ id: parsed.data.ledger_entry_id,
601
+ ts: parsed.data.ts,
602
+ source: "ai",
603
+ intent: parsed.data.intent,
604
+ affected_paths: [parsed.data.path]
605
+ }
606
+ };
607
+ } catch {
608
+ return null;
609
+ }
610
+ }
434
611
  function broadcastEvent(state, event) {
435
612
  const payload = fabricEventSchema.parse(event);
436
613
  const eventId = state.nextEventId++;
@@ -460,68 +637,6 @@ data: ${data}
460
637
  }
461
638
  }
462
639
  }
463
- async function readHumanLockSnapshot(projectRoot) {
464
- const humanLockPath = join(projectRoot, HUMAN_LOCK_PATH);
465
- const raw = await readUtf8File(humanLockPath);
466
- if (raw === null) {
467
- return createEmptyHumanLockSnapshot();
468
- }
469
- const parsed = humanLockFileSchema.parse(JSON.parse(raw));
470
- const locked = parsed.locked ?? [];
471
- const actualHashByKey = await readActualHumanLockHashes(projectRoot, locked);
472
- const drifted = locked.filter((entry) => actualHashByKey.get(getHumanLockKey(entry)) !== entry.hash);
473
- return {
474
- locked,
475
- drifted,
476
- driftedKeys: new Set(drifted.map((entry) => getHumanLockKey(entry))),
477
- hashByKey: new Map(locked.map((entry) => [getHumanLockKey(entry), entry.hash])),
478
- actualHashByKey
479
- };
480
- }
481
- async function readActualHumanLockHashes(projectRoot, locked) {
482
- const uniqueFiles = Array.from(new Set(locked.map((entry) => entry.file)));
483
- const fileContents = await Promise.all(
484
- uniqueFiles.map(async (file) => {
485
- const raw = await readUtf8File(join(projectRoot, file));
486
- return [file, raw];
487
- })
488
- );
489
- const contentByFile = new Map(fileContents);
490
- return new Map(
491
- locked.map((entry) => {
492
- const content = contentByFile.get(entry.file);
493
- return [getHumanLockKey(entry), content == null ? "missing" : hashLockedContent(content, entry)];
494
- })
495
- );
496
- }
497
- function hashLockedContent(content, entry) {
498
- const lines = content.split(/\r?\n/);
499
- const slice = lines.slice(Math.max(entry.start_line - 1, 0), Math.max(entry.end_line, 0)).join("\n");
500
- return `sha256:${createHash("sha256").update(slice).digest("hex")}`;
501
- }
502
- function getHumanLockKey(entry) {
503
- return `${entry.file}:${entry.start_line}:${entry.end_line}`;
504
- }
505
- function createEmptyHumanLockSnapshot() {
506
- return {
507
- locked: [],
508
- drifted: [],
509
- driftedKeys: /* @__PURE__ */ new Set(),
510
- hashByKey: /* @__PURE__ */ new Map(),
511
- actualHashByKey: /* @__PURE__ */ new Map()
512
- };
513
- }
514
- function areSetsEqual(left, right) {
515
- if (left.size !== right.size) {
516
- return false;
517
- }
518
- for (const value of left) {
519
- if (!right.has(value)) {
520
- return false;
521
- }
522
- }
523
- return true;
524
- }
525
640
  function readLastEventId(req) {
526
641
  const header = req.headers["last-event-id"];
527
642
  const headerValue = Array.isArray(header) ? header[0] : header;
@@ -542,9 +657,9 @@ function normalizePath(value) {
542
657
  }
543
658
  async function readUtf8File(path) {
544
659
  try {
545
- return await readFile(path, "utf8");
660
+ return await readFile2(path, "utf8");
546
661
  } catch (error) {
547
- if (isNodeError(error) && error.code === "ENOENT") {
662
+ if (isNodeError2(error) && error.code === "ENOENT") {
548
663
  return null;
549
664
  }
550
665
  throw error;
@@ -555,13 +670,13 @@ async function readFileSize(path) {
555
670
  const fileStat = await stat(path);
556
671
  return fileStat.size;
557
672
  } catch (error) {
558
- if (isNodeError(error) && error.code === "ENOENT") {
673
+ if (isNodeError2(error) && error.code === "ENOENT") {
559
674
  return 0;
560
675
  }
561
676
  throw error;
562
677
  }
563
678
  }
564
- function isNodeError(error) {
679
+ function isNodeError2(error) {
565
680
  return error instanceof Error;
566
681
  }
567
682
 
@@ -746,58 +861,6 @@ function registerHistoryApi(app, projectRoot) {
746
861
  });
747
862
  }
748
863
 
749
- // src/api/human-lock.ts
750
- import { humanLockApproveRequestSchema, humanLockFileParamsSchema } from "@fenglimg/fabric-shared";
751
- function registerHumanLockApi(app, projectRoot) {
752
- app.get("/api/human-lock", async (_req, res) => {
753
- try {
754
- await readAgentsMeta(projectRoot);
755
- res.json(await readHumanLock(projectRoot));
756
- } catch (error) {
757
- sendUnknownError(res, error);
758
- }
759
- });
760
- app.get(/^\/api\/human-lock\/(.+)$/, async (req, res) => {
761
- const rawFile = typeof req.params[0] === "string" ? decodeURIComponent(req.params[0]) : "";
762
- const validation = humanLockFileParamsSchema.safeParse({
763
- file: rawFile
764
- });
765
- if (!validation.success) {
766
- sendValidationError(res, "Invalid human-lock file path", validation.error.flatten());
767
- return;
768
- }
769
- try {
770
- await readAgentsMeta(projectRoot);
771
- const entry = await readHumanLockEntry(projectRoot, validation.data.file);
772
- if (entry === null) {
773
- sendError(
774
- res,
775
- 404,
776
- "HUMAN_LOCK_ENTRY_NOT_FOUND",
777
- `Cannot find human lock entry: ${validation.data.file}`
778
- );
779
- return;
780
- }
781
- res.json(entry);
782
- } catch (error) {
783
- sendUnknownError(res, error);
784
- }
785
- });
786
- app.post("/api/human-lock/approve", async (req, res) => {
787
- const validation = humanLockApproveRequestSchema.safeParse(req.body);
788
- if (!validation.success) {
789
- sendValidationError(res, "Invalid human-lock approval payload", validation.error.flatten());
790
- return;
791
- }
792
- try {
793
- await readAgentsMeta(projectRoot);
794
- res.json(await approveHumanLock(projectRoot, validation.data));
795
- } catch (error) {
796
- sendUnknownError(res, error);
797
- }
798
- });
799
- }
800
-
801
864
  // src/api/intent.ts
802
865
  import { annotateIntentRequestSchema } from "@fenglimg/fabric-shared";
803
866
 
@@ -1065,7 +1128,7 @@ function warnMissingDashboard(staticDir) {
1065
1128
  }
1066
1129
 
1067
1130
  // src/middleware/bearer-auth.ts
1068
- import { createHash as createHash2, timingSafeEqual } from "crypto";
1131
+ import { createHash, timingSafeEqual } from "crypto";
1069
1132
  function createBearerAuthMiddleware(token) {
1070
1133
  const expectedDigest = hashToken(token);
1071
1134
  return function bearerAuthMiddleware(req, res, next) {
@@ -1098,30 +1161,25 @@ function tokensMatch(token, expectedDigest) {
1098
1161
  return timingSafeEqual(hashToken(token), expectedDigest);
1099
1162
  }
1100
1163
  function hashToken(token) {
1101
- return createHash2("sha256").update(token, "utf8").digest();
1164
+ return createHash("sha256").update(token, "utf8").digest();
1102
1165
  }
1103
1166
 
1104
1167
  // src/http.ts
1105
1168
  var DEFAULT_HOST = "127.0.0.1";
1106
1169
  var NOTIFY_DEBOUNCE_MS = 200;
1107
1170
  var JsonlEventStore = class {
1108
- constructor(projectRoot, ledgerPath) {
1171
+ constructor(projectRoot) {
1109
1172
  this.projectRoot = projectRoot;
1110
- this.ledgerPath = ledgerPath;
1111
1173
  }
1112
1174
  projectRoot;
1113
- ledgerPath;
1114
1175
  async storeEvent(streamId, message) {
1115
- const eventId = randomUUID();
1116
- const entry = {
1117
- kind: "mcp-event",
1118
- eventId,
1119
- streamId,
1176
+ const eventId = randomUUID2();
1177
+ await appendEventLedgerEvent(this.projectRoot, {
1178
+ event_type: "mcp_event",
1179
+ mcp_event_id: eventId,
1180
+ stream_id: streamId,
1120
1181
  message
1121
- };
1122
- await ensureParentDirectory(this.ledgerPath);
1123
- await appendFile(this.ledgerPath, `${JSON.stringify(entry)}
1124
- `, "utf8");
1182
+ });
1125
1183
  return eventId;
1126
1184
  }
1127
1185
  async getStreamIdForEventId(eventId) {
@@ -1147,15 +1205,30 @@ var JsonlEventStore = class {
1147
1205
  return streamId;
1148
1206
  }
1149
1207
  async readEvents() {
1208
+ const eventLedgerEvents = await readEventLedger(this.projectRoot);
1209
+ const projectedEvents = eventLedgerEvents.flatMap((event) => {
1210
+ if (event.event_type !== "mcp_event") {
1211
+ return [];
1212
+ }
1213
+ return [{
1214
+ kind: "mcp-event",
1215
+ eventId: event.mcp_event_id,
1216
+ streamId: event.stream_id,
1217
+ message: event.message
1218
+ }];
1219
+ });
1220
+ if (projectedEvents.length > 0) {
1221
+ return projectedEvents;
1222
+ }
1150
1223
  let raw;
1151
1224
  try {
1152
- raw = await readFile2(this.ledgerPath, "utf8");
1225
+ raw = await readFile3(getLedgerPath(this.projectRoot), "utf8");
1153
1226
  } catch (error) {
1154
- if (isNodeError2(error) && error.code === "ENOENT") {
1227
+ if (isNodeError3(error) && error.code === "ENOENT") {
1155
1228
  try {
1156
- raw = await readFile2(getLegacyLedgerPath(this.projectRoot), "utf8");
1229
+ raw = await readFile3(getLegacyLedgerPath(this.projectRoot), "utf8");
1157
1230
  } catch (legacyError) {
1158
- if (isNodeError2(legacyError) && legacyError.code === "ENOENT") {
1231
+ if (isNodeError3(legacyError) && legacyError.code === "ENOENT") {
1159
1232
  return [];
1160
1233
  }
1161
1234
  throw legacyError;
@@ -1170,8 +1243,7 @@ var JsonlEventStore = class {
1170
1243
  function createFabricHttpApp(options) {
1171
1244
  const { projectRoot, host = DEFAULT_HOST, authToken, dashboardDistPath, dev } = options;
1172
1245
  const app = createMcpExpressApp({ host });
1173
- const ledgerPath = getLedgerPath(projectRoot);
1174
- const eventStore = new JsonlEventStore(projectRoot, ledgerPath);
1246
+ const eventStore = new JsonlEventStore(projectRoot);
1175
1247
  const sessions = /* @__PURE__ */ new Map();
1176
1248
  process.env.FABRIC_PROJECT_ROOT = projectRoot;
1177
1249
  const cacheWatcher = chokidar2.watch(
@@ -1226,7 +1298,6 @@ function createFabricHttpApp(options) {
1226
1298
  registerHistoryApi(app, projectRoot);
1227
1299
  registerScanApi(app, projectRoot);
1228
1300
  registerDoctorApi(app, projectRoot);
1229
- registerHumanLockApi(app, projectRoot);
1230
1301
  registerIntentApi(app, projectRoot);
1231
1302
  app.get("/events", createEventsHandler({ projectRoot }));
1232
1303
  app.all("/mcp", async (req, res) => {
@@ -1268,7 +1339,7 @@ async function createSession(eventStore, sessions) {
1268
1339
  const { createFabricServer } = await import("./index.js");
1269
1340
  const server = createFabricServer();
1270
1341
  const transport = new StreamableHTTPServerTransport({
1271
- sessionIdGenerator: randomUUID,
1342
+ sessionIdGenerator: randomUUID2,
1272
1343
  enableJsonResponse: true,
1273
1344
  eventStore,
1274
1345
  onsessioninitialized: async (sessionId) => {
@@ -1331,7 +1402,7 @@ function writeJsonRpcError(res, status, code, message) {
1331
1402
  id: null
1332
1403
  });
1333
1404
  }
1334
- function isNodeError2(error) {
1405
+ function isNodeError3(error) {
1335
1406
  return error instanceof Error;
1336
1407
  }
1337
1408
  export {