@rubytech/create-maxy-code 0.1.171 → 0.1.174

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 (48) hide show
  1. package/dist/index.js +12 -1
  2. package/package.json +1 -1
  3. package/payload/platform/scripts/verify-skill-tool-surface.sh +98 -0
  4. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  5. package/payload/platform/services/claude-session-manager/dist/http-server.js +24 -8
  6. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  7. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +15 -1
  8. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  9. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +78 -8
  10. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  11. package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -1
  12. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +1 -0
  13. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -1
  14. package/payload/platform/templates/specialists/agents/content-producer.md +4 -4
  15. package/payload/platform/templates/specialists/agents/librarian.md +2 -2
  16. package/payload/platform/templates/specialists/agents/personal-assistant.md +4 -4
  17. package/payload/platform/templates/specialists/agents/project-manager.md +2 -2
  18. package/payload/platform/templates/specialists/agents/research-assistant.md +2 -2
  19. package/payload/premium-plugins/writer-craft/mcp/dist/index.js +21 -21
  20. package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
  21. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts +1 -1
  22. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
  23. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js +18 -18
  24. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js.map +1 -1
  25. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.d.ts +1 -1
  26. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.d.ts.map +1 -1
  27. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.js +9 -9
  28. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.js.map +1 -1
  29. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts +1 -1
  30. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +1 -1
  31. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js +6 -6
  32. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -1
  33. package/payload/premium-plugins/writer-craft/mcp/scripts/smoke.mjs +2 -2
  34. package/payload/premium-plugins/writer-craft/mcp/src/index.ts +21 -21
  35. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-distil-profile.ts +19 -19
  36. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-record-feedback.ts +10 -10
  37. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +7 -7
  38. package/payload/server/public/assets/{admin-Dal5FWaD.js → admin-DH5jp-1u.js} +1 -1
  39. package/payload/server/public/assets/{data-DbSORCl4.js → data-CtanHhM2.js} +1 -1
  40. package/payload/server/public/assets/{graph-ys_joP2o.js → graph-6DPD6be1.js} +1 -1
  41. package/payload/server/public/assets/graph-labels-BMU8LsOD.js +1 -0
  42. package/payload/server/public/assets/{page-CwmZ76U7.js → page-0lzgQWnS.js} +1 -1
  43. package/payload/server/public/assets/{page--bl5Y7ZI.js → page-C29S3cBt.js} +1 -1
  44. package/payload/server/public/data.html +3 -3
  45. package/payload/server/public/graph.html +3 -3
  46. package/payload/server/public/index.html +4 -4
  47. package/payload/server/server.js +27 -3
  48. package/payload/server/public/assets/graph-labels-BBB5jsZk.js +0 -1
@@ -19,9 +19,9 @@ const server = new McpServer({
19
19
  // refusal preserves account isolation. Mirrors the memory plugin's
20
20
  // pattern (Task 202).
21
21
  const accountId = process.env.ACCOUNT_ID ?? null;
22
- const adminUserIdEnv = process.env.ADMIN_USER_ID ?? null;
22
+ const userIdEnv = process.env.ADMIN_USER_ID ?? null;
23
23
  process.stderr.write(
24
- `[writer-craft] boot accountId=${accountId ?? "null"} adminUserId=${adminUserIdEnv ?? "null"}\n`,
24
+ `[writer-craft] boot accountId=${accountId ?? "null"} userId=${userIdEnv ?? "null"}\n`,
25
25
  );
26
26
 
27
27
  function refuseNoAccount(toolName: string) {
@@ -39,11 +39,11 @@ function refuseNoAccount(toolName: string) {
39
39
  };
40
40
  }
41
41
 
42
- function resolveAdminUserId(): string | null {
42
+ function resolveUserId(): string | null {
43
43
  // ADMIN_USER_ID is stamped by the admin session manager at spawn time.
44
44
  // When absent (e.g. specialist subagents), the operator must pass it as
45
45
  // a tool argument or the tools refuse rather than orphan a profile.
46
- return adminUserIdEnv;
46
+ return userIdEnv;
47
47
  }
48
48
 
49
49
  server.tool(
@@ -95,7 +95,7 @@ server.tool(
95
95
  "voice-distil-profile",
96
96
  "Two-mode tool (Task 424). mode='sample' (default) walks the operator's human-only corpus, returns the exemplars + recent operator-edit intents for the agent to read. mode='write' persists the YAML style card the agent composed from the sample-mode response. Cadence-guarded: returns skip in 'write' mode without writing if corpus didn't grow ≥20% AND <30 days passed (pass force=true to override).",
97
97
  {
98
- adminUserId: z
98
+ userId: z
99
99
  .string()
100
100
  .optional()
101
101
  .describe(
@@ -114,16 +114,16 @@ server.tool(
114
114
  .optional()
115
115
  .describe("Required for mode='write'. The YAML style card the agent composed from the sample-mode exemplars + feedback intents."),
116
116
  },
117
- async ({ adminUserId, force, mode, styleCardYaml }) => {
117
+ async ({ userId, force, mode, styleCardYaml }) => {
118
118
  if (!accountId) return refuseNoAccount("voice-distil-profile");
119
- const resolvedAdminUserId = adminUserId ?? resolveAdminUserId();
120
- if (!resolvedAdminUserId) {
119
+ const resolvedUserId = userId ?? resolveUserId();
120
+ if (!resolvedUserId) {
121
121
  return {
122
122
  content: [
123
123
  {
124
124
  type: "text" as const,
125
125
  text:
126
- "voice-distil-profile: adminUserId required (no ADMIN_USER_ID in spawn env).",
126
+ "voice-distil-profile: userId required (no ADMIN_USER_ID in spawn env).",
127
127
  },
128
128
  ],
129
129
  isError: true,
@@ -132,7 +132,7 @@ server.tool(
132
132
  try {
133
133
  const result = await voiceDistilProfile({
134
134
  accountId,
135
- adminUserId: resolvedAdminUserId,
135
+ userId: resolvedUserId,
136
136
  force: force === true,
137
137
  mode,
138
138
  styleCardYaml,
@@ -161,7 +161,7 @@ server.tool(
161
161
  "voice-retrieve-conditioning",
162
162
  "Return the operator's style card and K voice-matched exemplars for a drafting brief. K=5 short-form, K=15 long-form. Token-budget bounded. Returns empty {styleCard:null,exemplars:[]} when no profile exists.",
163
163
  {
164
- adminUserId: z
164
+ userId: z
165
165
  .string()
166
166
  .optional()
167
167
  .describe("Operator AdminUser identifier. Defaults to ADMIN_USER_ID env."),
@@ -175,10 +175,10 @@ server.tool(
175
175
  register: z.string().optional().describe("Optional register hint"),
176
176
  tokenBudget: z.number().int().positive().optional(),
177
177
  },
178
- async ({ adminUserId, topic, format, register, tokenBudget }) => {
178
+ async ({ userId, topic, format, register, tokenBudget }) => {
179
179
  if (!accountId) return refuseNoAccount("voice-retrieve-conditioning");
180
- const resolvedAdminUserId = adminUserId ?? resolveAdminUserId();
181
- if (!resolvedAdminUserId) {
180
+ const resolvedUserId = userId ?? resolveUserId();
181
+ if (!resolvedUserId) {
182
182
  return {
183
183
  content: [
184
184
  {
@@ -191,7 +191,7 @@ server.tool(
191
191
  try {
192
192
  const result = await voiceRetrieveConditioning({
193
193
  accountId,
194
- adminUserId: resolvedAdminUserId,
194
+ userId: resolvedUserId,
195
195
  brief: { topic, format, register },
196
196
  tokenBudget,
197
197
  });
@@ -222,7 +222,7 @@ server.tool(
222
222
  "voice-record-feedback",
223
223
  "Capture an operator's edit on an agent draft. Writes :VoiceEdit linked to :VoiceProfile via :FEEDBACK_FOR. The calling agent supplies `intent` (one sentence describing what the operator changed and what voice preference it reveals); Task 424 moved the diff-intent summarisation into the agent's turn.",
224
224
  {
225
- adminUserId: z.string().optional(),
225
+ userId: z.string().optional(),
226
226
  originalText: z.string().min(1),
227
227
  editedText: z.string().min(1),
228
228
  intent: z
@@ -232,15 +232,15 @@ server.tool(
232
232
  skill: z.string().optional().describe("Calling skill name, for provenance"),
233
233
  brief: z.string().optional().describe("Drafting brief that produced the original"),
234
234
  },
235
- async ({ adminUserId, originalText, editedText, intent, skill, brief }) => {
235
+ async ({ userId, originalText, editedText, intent, skill, brief }) => {
236
236
  if (!accountId) return refuseNoAccount("voice-record-feedback");
237
- const resolvedAdminUserId = adminUserId ?? resolveAdminUserId();
238
- if (!resolvedAdminUserId) {
237
+ const resolvedUserId = userId ?? resolveUserId();
238
+ if (!resolvedUserId) {
239
239
  return {
240
240
  content: [
241
241
  {
242
242
  type: "text" as const,
243
- text: "voice-record-feedback: adminUserId required (no ADMIN_USER_ID in spawn env).",
243
+ text: "voice-record-feedback: userId required (no ADMIN_USER_ID in spawn env).",
244
244
  },
245
245
  ],
246
246
  isError: true,
@@ -249,7 +249,7 @@ server.tool(
249
249
  try {
250
250
  const result = await voiceRecordFeedback({
251
251
  accountId,
252
- adminUserId: resolvedAdminUserId,
252
+ userId: resolvedUserId,
253
253
  originalText,
254
254
  editedText,
255
255
  intent,
@@ -44,7 +44,7 @@ const RE_RUN_AGE_DAYS = 30;
44
44
 
45
45
  export interface VoiceDistilProfileParams {
46
46
  accountId: string;
47
- adminUserId: string;
47
+ userId: string;
48
48
  force?: boolean;
49
49
  /**
50
50
  * Operation mode (Task 424):
@@ -87,9 +87,9 @@ export interface VoiceDistilProfileResult {
87
87
  export async function voiceDistilProfile(
88
88
  params: VoiceDistilProfileParams,
89
89
  ): Promise<VoiceDistilProfileResult> {
90
- const { accountId, adminUserId, force = false } = params;
91
- if (!accountId || !adminUserId) {
92
- throw new Error("voice-distil-profile: accountId and adminUserId required");
90
+ const { accountId, userId, force = false } = params;
91
+ if (!accountId || !userId) {
92
+ throw new Error("voice-distil-profile: accountId and userId required");
93
93
  }
94
94
  const session = getSession();
95
95
  const now = new Date();
@@ -98,11 +98,11 @@ export async function voiceDistilProfile(
98
98
  try {
99
99
  // 1. Read existing profile (if any) for cadence-guard comparison.
100
100
  const existing = await session.run(
101
- `MATCH (a:AdminUser {accountId: $accountId, adminUserId: $adminUserId})
101
+ `MATCH (a:AdminUser {accountId: $accountId, userId: $userId})
102
102
  OPTIONAL MATCH (a)-[:HAS_VOICE_PROFILE]->(p:VoiceProfile)
103
103
  RETURN elementId(p) AS profileId, p.corpusSize AS corpusSize,
104
104
  p.generatedAt AS generatedAt`,
105
- { accountId, adminUserId },
105
+ { accountId, userId },
106
106
  );
107
107
  const existingRow = existing.records[0];
108
108
  const prevProfileId = (existingRow?.get("profileId") as string | null) ?? null;
@@ -123,7 +123,7 @@ export async function voiceDistilProfile(
123
123
 
124
124
  if (corpusSize === 0) {
125
125
  process.stderr.write(
126
- `[voice-distil] adminUser=${adminUserId} skip reason=empty-corpus\n`,
126
+ `[voice-distil] adminUser=${userId} skip reason=empty-corpus\n`,
127
127
  );
128
128
  return {
129
129
  profileId: prevProfileId,
@@ -151,7 +151,7 @@ export async function voiceDistilProfile(
151
151
  if (!ageTrigger && !growthTrigger) {
152
152
  const reason = ageDays < 1 ? "recent" : "below-threshold";
153
153
  process.stderr.write(
154
- `[voice-distil] adminUser=${adminUserId} skip reason=${reason} corpusSize=${corpusSize} prevCorpus=${prevCorpusSize} ageDays=${ageDays.toFixed(1)}\n`,
154
+ `[voice-distil] adminUser=${userId} skip reason=${reason} corpusSize=${corpusSize} prevCorpus=${prevCorpusSize} ageDays=${ageDays.toFixed(1)}\n`,
155
155
  );
156
156
  return {
157
157
  profileId: prevProfileId,
@@ -179,7 +179,7 @@ export async function voiceDistilProfile(
179
179
  coalesce(n.body, n.abstract, n.subject, n.title, n.summary, '') AS body,
180
180
  parent.conversationIdentity IS NOT NULL AS isConversationChunk,
181
181
  ts
182
- ORDER BY ts DESC NULLS LAST
182
+ ORDER BY ts IS NULL, ts DESC
183
183
  LIMIT 500`,
184
184
  { accountId },
185
185
  );
@@ -207,10 +207,10 @@ export async function voiceDistilProfile(
207
207
 
208
208
  // 5. Pull feedback intents from existing :VoiceEdit nodes for this operator.
209
209
  const feedback = await session.run(
210
- `MATCH (a:AdminUser {accountId: $accountId, adminUserId: $adminUserId})
211
- MATCH (e:VoiceEdit {adminUserId: $adminUserId, accountId: $accountId})
210
+ `MATCH (a:AdminUser {accountId: $accountId, userId: $userId})
211
+ MATCH (e:VoiceEdit {userId: $userId, accountId: $accountId})
212
212
  RETURN e.intent AS intent ORDER BY e.occurredAt DESC LIMIT 50`,
213
- { accountId, adminUserId },
213
+ { accountId, userId },
214
214
  );
215
215
  const feedbackIntents = feedback.records
216
216
  .map((r) => r.get("intent") as string | null)
@@ -224,7 +224,7 @@ export async function voiceDistilProfile(
224
224
  const mode = params.mode ?? "sample";
225
225
  if (mode === "sample") {
226
226
  process.stderr.write(
227
- `[voice-distil] adminUser=${adminUserId} mode=sample corpusSize=${corpusSize} ` +
227
+ `[voice-distil] adminUser=${userId} mode=sample corpusSize=${corpusSize} ` +
228
228
  `exemplars=${samples.length} feedbackEntries=${feedbackIntents.length}\n`,
229
229
  );
230
230
  return {
@@ -250,12 +250,12 @@ export async function voiceDistilProfile(
250
250
  //
251
251
  // The :AdminUser must already exist (onboarding/auth is the only
252
252
  // legitimate origin for :AdminUser). MATCH not MERGE: a typo in
253
- // adminUserId, an env mismatch, or a dropped credential surfaces
253
+ // userId, an env mismatch, or a dropped credential surfaces
254
254
  // here as a typed error rather than silently minting an orphan
255
255
  // AdminUser that bypasses the platform's label-origin protection.
256
256
  const writeResult = await session.run(
257
- `MATCH (a:AdminUser {accountId: $accountId, adminUserId: $adminUserId})
258
- MERGE (a)-[:HAS_VOICE_PROFILE]->(p:VoiceProfile {accountId: $accountId, adminUserId: $adminUserId})
257
+ `MATCH (a:AdminUser {accountId: $accountId, userId: $userId})
258
+ MERGE (a)-[:HAS_VOICE_PROFILE]->(p:VoiceProfile {accountId: $accountId, userId: $userId})
259
259
  ON CREATE SET p.createdAt = $now
260
260
  SET p.styleCard = $styleCard,
261
261
  p.generatedAt = $now,
@@ -269,7 +269,7 @@ export async function voiceDistilProfile(
269
269
  RETURN elementId(p) AS profileId`,
270
270
  {
271
271
  accountId,
272
- adminUserId,
272
+ userId,
273
273
  styleCard: styleCardYaml,
274
274
  now: nowIso,
275
275
  corpusSize,
@@ -281,13 +281,13 @@ export async function voiceDistilProfile(
281
281
  // error here rather than returning a half-baked result.
282
282
  if (writeResult.records.length === 0) {
283
283
  throw new Error(
284
- `voice-mirror: :AdminUser {accountId='${accountId}', adminUserId='${adminUserId}'} not found. Onboarding must promote the operator to :AdminUser before distillation.`,
284
+ `voice-mirror: :AdminUser {accountId='${accountId}', userId='${userId}'} not found. Onboarding must promote the operator to :AdminUser before distillation.`,
285
285
  );
286
286
  }
287
287
  const profileId = writeResult.records[0].get("profileId") as string;
288
288
 
289
289
  process.stderr.write(
290
- `[voice-distil] adminUser=${adminUserId} mode=write corpusSize=${corpusSize} generatedAt=${nowIso} feedbackEntries=${feedbackIntents.length}\n`,
290
+ `[voice-distil] adminUser=${userId} mode=write corpusSize=${corpusSize} generatedAt=${nowIso} feedbackEntries=${feedbackIntents.length}\n`,
291
291
  );
292
292
 
293
293
  return {
@@ -17,7 +17,7 @@ import { getSession } from "../lib/neo4j.js";
17
17
 
18
18
  export interface VoiceRecordFeedbackParams {
19
19
  accountId: string;
20
- adminUserId: string;
20
+ userId: string;
21
21
  originalText: string;
22
22
  editedText: string;
23
23
  /**
@@ -44,9 +44,9 @@ export interface VoiceRecordFeedbackResult {
44
44
  export async function voiceRecordFeedback(
45
45
  params: VoiceRecordFeedbackParams,
46
46
  ): Promise<VoiceRecordFeedbackResult> {
47
- const { accountId, adminUserId, originalText, editedText, intent: rawIntent, context } = params;
48
- if (!accountId || !adminUserId) {
49
- throw new Error("voice-record-feedback: accountId and adminUserId required");
47
+ const { accountId, userId, originalText, editedText, intent: rawIntent, context } = params;
48
+ if (!accountId || !userId) {
49
+ throw new Error("voice-record-feedback: accountId and userId required");
50
50
  }
51
51
  if (!originalText || !editedText) {
52
52
  throw new Error("voice-record-feedback: originalText and editedText required");
@@ -65,11 +65,11 @@ export async function voiceRecordFeedback(
65
65
  const session = getSession();
66
66
  try {
67
67
  const result = await session.run(
68
- `MATCH (a:AdminUser {accountId: $accountId, adminUserId: $adminUserId})
68
+ `MATCH (a:AdminUser {accountId: $accountId, userId: $userId})
69
69
  CREATE (e:VoiceEdit {
70
70
  editId: $editId,
71
71
  accountId: $accountId,
72
- adminUserId: $adminUserId,
72
+ userId: $userId,
73
73
  originalText: $originalText,
74
74
  editedText: $editedText,
75
75
  intent: $intent,
@@ -79,14 +79,14 @@ export async function voiceRecordFeedback(
79
79
  })
80
80
  CREATE (a)-[:AUTHORED]->(e)
81
81
  WITH e
82
- OPTIONAL MATCH (:AdminUser {accountId: $accountId, adminUserId: $adminUserId})-[:HAS_VOICE_PROFILE]->(p:VoiceProfile)
82
+ OPTIONAL MATCH (:AdminUser {accountId: $accountId, userId: $userId})-[:HAS_VOICE_PROFILE]->(p:VoiceProfile)
83
83
  FOREACH (_ IN CASE WHEN p IS NULL THEN [] ELSE [1] END |
84
84
  MERGE (e)-[:FEEDBACK_FOR]->(p)
85
85
  )
86
86
  RETURN elementId(e) AS editId, (p IS NOT NULL) AS hadProfile`,
87
87
  {
88
88
  accountId,
89
- adminUserId,
89
+ userId,
90
90
  editId,
91
91
  originalText,
92
92
  editedText,
@@ -98,13 +98,13 @@ export async function voiceRecordFeedback(
98
98
  );
99
99
  if (result.records.length === 0) {
100
100
  throw new Error(
101
- `voice-mirror: :AdminUser {accountId='${accountId}', adminUserId='${adminUserId}'} not found. Onboarding must promote the operator to :AdminUser before feedback can be recorded.`,
101
+ `voice-mirror: :AdminUser {accountId='${accountId}', userId='${userId}'} not found. Onboarding must promote the operator to :AdminUser before feedback can be recorded.`,
102
102
  );
103
103
  }
104
104
  const hadProfile = result.records[0].get("hadProfile") === true;
105
105
 
106
106
  process.stderr.write(
107
- `[voice-record-feedback] adminUser=${adminUserId} intent="${cappedIntent.replace(/"/g, "'")}" diffBytes=${editedText.length - originalText.length}\n`,
107
+ `[voice-record-feedback] adminUser=${userId} intent="${cappedIntent.replace(/"/g, "'")}" diffBytes=${editedText.length - originalText.length}\n`,
108
108
  );
109
109
 
110
110
  return { editId, intent: cappedIntent, occurredAt, hadProfile };
@@ -26,7 +26,7 @@ const CHARS_PER_TOKEN = 4;
26
26
 
27
27
  export interface VoiceRetrieveConditioningParams {
28
28
  accountId: string;
29
- adminUserId: string;
29
+ userId: string;
30
30
  brief: {
31
31
  topic?: string;
32
32
  format: "short" | "long";
@@ -52,8 +52,8 @@ const EMPTY: VoiceRetrieveConditioningResult = { styleCard: null, exemplars: []
52
52
  export async function voiceRetrieveConditioning(
53
53
  params: VoiceRetrieveConditioningParams,
54
54
  ): Promise<VoiceRetrieveConditioningResult> {
55
- const { accountId, adminUserId, brief } = params;
56
- if (!accountId || !adminUserId) return EMPTY;
55
+ const { accountId, userId, brief } = params;
56
+ if (!accountId || !userId) return EMPTY;
57
57
 
58
58
  const k = brief.format === "long" ? 15 : 5;
59
59
  const tokenBudget =
@@ -65,10 +65,10 @@ export async function voiceRetrieveConditioning(
65
65
  try {
66
66
  // 1. Style card.
67
67
  const profile = await session.run(
68
- `MATCH (a:AdminUser {accountId: $accountId, adminUserId: $adminUserId})
68
+ `MATCH (a:AdminUser {accountId: $accountId, userId: $userId})
69
69
  OPTIONAL MATCH (a)-[:HAS_VOICE_PROFILE]->(p:VoiceProfile)
70
70
  RETURN p.styleCard AS styleCard`,
71
- { accountId, adminUserId },
71
+ { accountId, userId },
72
72
  );
73
73
  const styleCard = (profile.records[0]?.get("styleCard") as string | null) ?? null;
74
74
 
@@ -86,7 +86,7 @@ export async function voiceRetrieveConditioning(
86
86
  coalesce(n.body, n.abstract, n.subject, n.title, n.summary, '') AS body,
87
87
  coalesce(n.subject, n.title, n.name, '') AS source,
88
88
  ts
89
- ORDER BY ts DESC NULLS LAST
89
+ ORDER BY ts IS NULL, ts DESC
90
90
  LIMIT $k`
91
91
  : `MATCH (n)
92
92
  WHERE ${VOICE_CORPUS_WHERE}
@@ -96,7 +96,7 @@ export async function voiceRetrieveConditioning(
96
96
  coalesce(n.body, n.abstract, n.subject, n.title, n.summary, '') AS body,
97
97
  coalesce(n.subject, n.title, n.name, '') AS source,
98
98
  ts
99
- ORDER BY ts DESC NULLS LAST
99
+ ORDER BY ts IS NULL, ts DESC
100
100
  LIMIT $k`;
101
101
 
102
102
  const result = await session.run(cypher, {