@rubytech/create-maxy 1.0.665 → 1.0.666
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/package.json
CHANGED
|
@@ -308,9 +308,19 @@ OPTIONS {
|
|
|
308
308
|
// Linked via (Message)-[:NEXT]->(Message) to the next message in
|
|
309
309
|
// insertion order (Task 621). The chain is linear — one outgoing
|
|
310
310
|
// NEXT per message — so a Conversation with N messages has N-1
|
|
311
|
-
// NEXT edges.
|
|
312
|
-
//
|
|
313
|
-
//
|
|
311
|
+
// NEXT edges.
|
|
312
|
+
//
|
|
313
|
+
// Chain-fork invariant (Task 624): the write path in
|
|
314
|
+
// platform/ui/app/lib/neo4j-store.ts#persistMessage holds a
|
|
315
|
+
// per-conversationId async mutex around the Cypher that observes
|
|
316
|
+
// the tail and CREATEs the new [:NEXT] edge. Without this, two
|
|
317
|
+
// concurrent writes under READ_COMMITTED both see the same tail
|
|
318
|
+
// and each CREATE an outgoing NEXT → two edges from one node.
|
|
319
|
+
// Scope: single node-process only; multi-replica coordination is
|
|
320
|
+
// a follow-up (see .docs/neo4j.md). Any new write path that creates
|
|
321
|
+
// Message nodes on the chain (bulk import, migration, rehydrate)
|
|
322
|
+
// MUST go through persistMessage or hold an equivalent lock.
|
|
323
|
+
//
|
|
314
324
|
// Vector-indexed for semantic search over conversation history.
|
|
315
325
|
// ----------------------------------------------------------
|
|
316
326
|
|
package/payload/server/server.js
CHANGED
|
@@ -5281,6 +5281,7 @@ async function persistToolCall(record) {
|
|
|
5281
5281
|
}
|
|
5282
5282
|
}
|
|
5283
5283
|
var SUMMARY_MAX_LEN = 200;
|
|
5284
|
+
var persistMessageLocks = /* @__PURE__ */ new Map();
|
|
5284
5285
|
async function persistMessage(conversationId, role, content, accountId, tokens, createdAt, sender) {
|
|
5285
5286
|
if (!content) return null;
|
|
5286
5287
|
const messageId = randomUUID();
|
|
@@ -5291,6 +5292,15 @@ async function persistMessage(conversationId, role, content, accountId, tokens,
|
|
|
5291
5292
|
} catch (err) {
|
|
5292
5293
|
console.error(`[persist] Embedding failed, storing without: ${err instanceof Error ? err.message : String(err)}`);
|
|
5293
5294
|
}
|
|
5295
|
+
const prev = persistMessageLocks.get(conversationId);
|
|
5296
|
+
const waited = prev !== void 0;
|
|
5297
|
+
let release;
|
|
5298
|
+
const mine = new Promise((resolve31) => {
|
|
5299
|
+
release = resolve31;
|
|
5300
|
+
});
|
|
5301
|
+
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
5302
|
+
persistMessageLocks.set(conversationId, chained);
|
|
5303
|
+
await prev;
|
|
5294
5304
|
const session = getSession();
|
|
5295
5305
|
try {
|
|
5296
5306
|
const result = await session.run(
|
|
@@ -5353,7 +5363,7 @@ async function persistMessage(conversationId, role, content, accountId, tokens,
|
|
|
5353
5363
|
const summarySetThisWrite = record.get("summarySetThisWrite") === true;
|
|
5354
5364
|
const chainLenRaw = record.get("chainLen");
|
|
5355
5365
|
const chainLen = typeof chainLenRaw === "bigint" ? Number(chainLenRaw) : typeof chainLenRaw?.toNumber === "function" ? chainLenRaw.toNumber() : Number(chainLenRaw ?? 0);
|
|
5356
|
-
console.error(`[neo4j-store] append-message conversationId=${conversationId.slice(0, 8)}\u2026 messageId=${messageId.slice(0, 8)}\u2026 prev=${prevMessageId ? prevMessageId.slice(0, 8) + "\u2026" : "null"} chainLen=${chainLen}`);
|
|
5366
|
+
console.error(`[neo4j-store] append-message conversationId=${conversationId.slice(0, 8)}\u2026 messageId=${messageId.slice(0, 8)}\u2026 prev=${prevMessageId ? prevMessageId.slice(0, 8) + "\u2026" : "null"} chainLen=${chainLen} waited=${waited}`);
|
|
5357
5367
|
if (summarySetThisWrite) {
|
|
5358
5368
|
console.error(`[neo4j-store] conversation-summary-set conversationId=${conversationId.slice(0, 8)}\u2026 len=${summary.length}`);
|
|
5359
5369
|
}
|
|
@@ -5363,6 +5373,10 @@ async function persistMessage(conversationId, role, content, accountId, tokens,
|
|
|
5363
5373
|
console.error(`[persist] Neo4j write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5364
5374
|
return null;
|
|
5365
5375
|
} finally {
|
|
5376
|
+
release();
|
|
5377
|
+
if (persistMessageLocks.get(conversationId) === chained) {
|
|
5378
|
+
persistMessageLocks.delete(conversationId);
|
|
5379
|
+
}
|
|
5366
5380
|
await session.close();
|
|
5367
5381
|
}
|
|
5368
5382
|
}
|