@rubytech/create-maxy-code 0.1.174 → 0.1.175
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 +1 -1
- package/payload/platform/neo4j/schema.cypher +49 -10
- package/payload/platform/plugins/docs/references/voice-mirror-guide.md +18 -16
- package/payload/platform/scripts/setup-account.sh +8 -0
- package/payload/platform/scripts/verify-skill-tool-surface.sh +27 -8
- package/payload/premium-plugins/writer-craft/PLUGIN.md +11 -7
- package/payload/premium-plugins/writer-craft/hooks/__tests__/voice-session-end-text.test.sh +284 -0
- package/payload/premium-plugins/writer-craft/hooks/voice-session-end-text.sh +75 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.d.ts +3 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.js +58 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js +95 -18
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts +47 -6
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js +69 -6
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts +8 -2
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js +82 -53
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.d.ts +23 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.js +191 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.d.ts +6 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.js +14 -6
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts +12 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js +23 -12
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.d.ts +3 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.js +33 -5
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/scripts/smoke.mjs +289 -28
- package/payload/premium-plugins/writer-craft/mcp/src/cli/ingest-session-text.ts +65 -0
- package/payload/premium-plugins/writer-craft/mcp/src/index.ts +108 -18
- package/payload/premium-plugins/writer-craft/mcp/src/lib/voice-corpus.ts +78 -6
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-distil-profile.ts +115 -56
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-ingest-session-text.ts +229 -0
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-record-feedback.ts +20 -6
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +38 -13
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-tag-content.ts +42 -5
- package/payload/premium-plugins/writer-craft/skills/voice-mirror/SKILL.md +65 -31
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-distil-profile.d.ts","sourceRoot":"","sources":["../../src/tools/voice-distil-profile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"voice-distil-profile.d.ts","sourceRoot":"","sources":["../../src/tools/voice-distil-profile.ts"],"names":[],"mappings":"AA2BA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,wBAAwB,CAAC;AA0BhC,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1B,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EACP,iBAAiB,GACjB,QAAQ,GACR,cAAc,GACd,oBAAoB,GACpB,0BAA0B,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAClC,uFAAuF;IACvF,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AA6MD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,wBAAwB,GAAG,wBAAwB,EAAE,CAAC,CAmDhE"}
|
|
@@ -2,20 +2,30 @@
|
|
|
2
2
|
* voice-distil-profile — walk the operator's `human-only` corpus and
|
|
3
3
|
* persist a `:VoiceProfile` style card.
|
|
4
4
|
*
|
|
5
|
+
* Task 462: one `:VoiceProfile` per `(accountId, userId, format)`.
|
|
6
|
+
* `format` ∈ `{text, email, social-post, article, marketing-copy, note}`.
|
|
7
|
+
*
|
|
8
|
+
* - When `format` is supplied, distils that format only.
|
|
9
|
+
* - When `format` is omitted, enumerates distinct format values present
|
|
10
|
+
* in the corpus (nodes where `n.format IS NOT NULL`) and distils each
|
|
11
|
+
* that fires the cadence guard. Returns `skipReason: 'no-format-tagged-content'`
|
|
12
|
+
* when enumeration yields zero formats.
|
|
13
|
+
*
|
|
5
14
|
* Cadence guard: re-runs on ≥20% corpus growth since last profile, OR
|
|
6
15
|
* ≥30 days since `generatedAt`. `force: true` overrides both.
|
|
7
16
|
*
|
|
8
17
|
* Body-text sampling caps at ~120k chars (rough proxy for tokens at
|
|
9
18
|
* 4 chars/token = 30k tokens) — well inside Haiku's 200k context window
|
|
10
19
|
* with headroom for the system prompt + tool schema.
|
|
20
|
+
*
|
|
21
|
+
* Task 424 — the Haiku style-card composition moved into the calling agent's
|
|
22
|
+
* turn. The agent reads the corpus exemplars and feedback intents (this tool
|
|
23
|
+
* returns them when called with `mode='sample'`), composes the style-card
|
|
24
|
+
* YAML, then calls this tool again with `mode='write'` + `styleCardYaml` set.
|
|
11
25
|
*/
|
|
12
26
|
import neo4j from "neo4j-driver";
|
|
13
27
|
import { getSession } from "../lib/neo4j.js";
|
|
14
|
-
import { VOICE_CORPUS_WHERE } from "../lib/voice-corpus.js";
|
|
15
|
-
// Task 424 — the Haiku style-card composition moved into the calling agent's
|
|
16
|
-
// turn. The agent reads the corpus exemplars and feedback intents (this tool
|
|
17
|
-
// returns them when called with `mode='sample'`), composes the style-card YAML,
|
|
18
|
-
// then calls this tool again with `mode='write'` + `styleCardYaml` set.
|
|
28
|
+
import { VOICE_CORPUS_WHERE, voiceCorpusWhereWithFormat, FORMAT_VALUES, } from "../lib/voice-corpus.js";
|
|
19
29
|
/**
|
|
20
30
|
* Coerce a neo4j cypher RETURN value to a plain JS number.
|
|
21
31
|
*
|
|
@@ -45,34 +55,32 @@ const RE_RUN_AGE_DAYS = 30;
|
|
|
45
55
|
// sentenceLengthDistribution, register, favoured, avoided, taboos,
|
|
46
56
|
// openingPattern, closingPattern, exemplarSentences
|
|
47
57
|
// Exemplar sentences should be quoted verbatim from the returned corpus.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
58
|
+
/**
|
|
59
|
+
* Single-format distil. Opens a new session for each call.
|
|
60
|
+
*/
|
|
61
|
+
async function distilForFormat(params) {
|
|
62
|
+
const { accountId, userId, format, force = false } = params;
|
|
53
63
|
const session = getSession();
|
|
54
64
|
const now = new Date();
|
|
55
65
|
const nowIso = now.toISOString();
|
|
56
66
|
try {
|
|
57
|
-
// 1. Read existing profile (if any) for cadence-guard comparison.
|
|
67
|
+
// 1. Read existing per-format profile (if any) for cadence-guard comparison.
|
|
58
68
|
const existing = await session.run(`MATCH (a:AdminUser {accountId: $accountId, userId: $userId})
|
|
59
|
-
OPTIONAL MATCH (a)-[:HAS_VOICE_PROFILE]->(p:VoiceProfile)
|
|
69
|
+
OPTIONAL MATCH (a)-[:HAS_VOICE_PROFILE]->(p:VoiceProfile {accountId: $accountId, userId: $userId, format: $format})
|
|
60
70
|
RETURN elementId(p) AS profileId, p.corpusSize AS corpusSize,
|
|
61
|
-
p.generatedAt AS generatedAt`, { accountId, userId });
|
|
71
|
+
p.generatedAt AS generatedAt`, { accountId, userId, format });
|
|
62
72
|
const existingRow = existing.records[0];
|
|
63
73
|
const prevProfileId = existingRow?.get("profileId") ?? null;
|
|
64
74
|
const prevCorpusSize = toJsNumber(existingRow?.get("corpusSize"));
|
|
65
75
|
const prevGeneratedAt = existingRow?.get("generatedAt") ?? null;
|
|
66
|
-
// 2. Count corpus
|
|
67
|
-
|
|
68
|
-
// :Section chunks (chat archives — children of a :KnowledgeDocument
|
|
69
|
-
// with `conversationIdentity` set). See VOICE_CORPUS_WHERE above.
|
|
76
|
+
// 2. Count corpus for this specific format.
|
|
77
|
+
const corpusWhere = voiceCorpusWhereWithFormat(format);
|
|
70
78
|
const countResult = await session.run(`MATCH (n)
|
|
71
|
-
WHERE ${
|
|
72
|
-
RETURN count(n) AS c`, { accountId });
|
|
79
|
+
WHERE ${corpusWhere}
|
|
80
|
+
RETURN count(n) AS c`, { accountId, format });
|
|
73
81
|
const corpusSize = toJsNumber(countResult.records[0]?.get("c")) ?? 0;
|
|
74
82
|
if (corpusSize === 0) {
|
|
75
|
-
process.stderr.write(`[voice-distil] adminUser=${userId} skip reason=empty-corpus\n`);
|
|
83
|
+
process.stderr.write(`[voice-distil] adminUser=${userId} format=${format} skip reason=empty-corpus\n`);
|
|
76
84
|
return {
|
|
77
85
|
profileId: prevProfileId,
|
|
78
86
|
corpusSize: 0,
|
|
@@ -86,9 +94,6 @@ export async function voiceDistilProfile(params) {
|
|
|
86
94
|
if (!force && prevProfileId && prevCorpusSize !== null && prevGeneratedAt) {
|
|
87
95
|
const ageMs = now.getTime() - new Date(prevGeneratedAt).getTime();
|
|
88
96
|
const ageDays = ageMs / (24 * 60 * 60 * 1000);
|
|
89
|
-
// prevCorpusSize is non-null here (the guard above checks). When it's
|
|
90
|
-
// zero we treat growth as infinite — first real corpus always triggers
|
|
91
|
-
// a re-run.
|
|
92
97
|
const growth = prevCorpusSize > 0
|
|
93
98
|
? (corpusSize - prevCorpusSize) / prevCorpusSize
|
|
94
99
|
: 1;
|
|
@@ -96,7 +101,7 @@ export async function voiceDistilProfile(params) {
|
|
|
96
101
|
const growthTrigger = growth >= RE_RUN_GROWTH_FRACTION;
|
|
97
102
|
if (!ageTrigger && !growthTrigger) {
|
|
98
103
|
const reason = ageDays < 1 ? "recent" : "below-threshold";
|
|
99
|
-
process.stderr.write(`[voice-distil] adminUser=${userId} skip reason=${reason} corpusSize=${corpusSize} prevCorpus=${prevCorpusSize} ageDays=${ageDays.toFixed(1)}\n`);
|
|
104
|
+
process.stderr.write(`[voice-distil] adminUser=${userId} format=${format} skip reason=${reason} corpusSize=${corpusSize} prevCorpus=${prevCorpusSize} ageDays=${ageDays.toFixed(1)}\n`);
|
|
100
105
|
return {
|
|
101
106
|
profileId: prevProfileId,
|
|
102
107
|
corpusSize,
|
|
@@ -108,11 +113,8 @@ export async function voiceDistilProfile(params) {
|
|
|
108
113
|
}
|
|
109
114
|
}
|
|
110
115
|
// 4. Sample corpus bodies up to the char cap. Pick recent first.
|
|
111
|
-
// Timestamp coalesces include `firstMessageAt` for conversation-document
|
|
112
|
-
// :Section chunks so chat archives sort by when the conversation
|
|
113
|
-
// actually happened.
|
|
114
116
|
const sampled = await session.run(`MATCH (n)
|
|
115
|
-
WHERE ${
|
|
117
|
+
WHERE ${corpusWhere}
|
|
116
118
|
WITH n, coalesce(n.dateSent, n.datePublished, n.firstMessageAt, n.dateCreated, n.createdAt) AS ts
|
|
117
119
|
OPTIONAL MATCH (parent:KnowledgeDocument)-[:HAS_SECTION]->(n)
|
|
118
120
|
WHERE 'Section' IN labels(n) AND parent.conversationIdentity IS NOT NULL
|
|
@@ -122,16 +124,12 @@ export async function voiceDistilProfile(params) {
|
|
|
122
124
|
parent.conversationIdentity IS NOT NULL AS isConversationChunk,
|
|
123
125
|
ts
|
|
124
126
|
ORDER BY ts IS NULL, ts DESC
|
|
125
|
-
LIMIT 500`, { accountId });
|
|
127
|
+
LIMIT 500`, { accountId, format });
|
|
126
128
|
const samples = [];
|
|
127
129
|
let charBudget = SAMPLE_CHAR_CAP;
|
|
128
130
|
for (const r of sampled.records) {
|
|
129
131
|
const body = r.get("body") ?? "";
|
|
130
132
|
const labels = r.get("labels") ?? [];
|
|
131
|
-
// Conversation-document sections carry `:Section` only after the Task
|
|
132
|
-
// 324 collapse; surface "Conversation" via the parent's
|
|
133
|
-
// `conversationIdentity` so the LLM exemplar header still says "this
|
|
134
|
-
// is chat" rather than the bare base label.
|
|
135
133
|
const isConversationChunk = Boolean(r.get("isConversationChunk"));
|
|
136
134
|
const label = isConversationChunk
|
|
137
135
|
? "Conversation"
|
|
@@ -144,21 +142,16 @@ export async function voiceDistilProfile(params) {
|
|
|
144
142
|
if (charBudget <= 0)
|
|
145
143
|
break;
|
|
146
144
|
}
|
|
147
|
-
// 5. Pull feedback intents from existing :VoiceEdit nodes for this
|
|
148
|
-
const feedback = await session.run(`MATCH (
|
|
149
|
-
|
|
150
|
-
RETURN e.intent AS intent ORDER BY e.occurredAt DESC LIMIT 50`, { accountId, userId });
|
|
145
|
+
// 5. Pull feedback intents from existing :VoiceEdit nodes for this format.
|
|
146
|
+
const feedback = await session.run(`MATCH (e:VoiceEdit {userId: $userId, accountId: $accountId, format: $format})
|
|
147
|
+
RETURN e.intent AS intent ORDER BY e.occurredAt DESC LIMIT 50`, { accountId, userId, format });
|
|
151
148
|
const feedbackIntents = feedback.records
|
|
152
149
|
.map((r) => r.get("intent"))
|
|
153
150
|
.filter((x) => typeof x === "string" && x.length > 0);
|
|
154
151
|
// 6. Mode branch (Task 424).
|
|
155
|
-
// - `sample` (default): return the exemplars + feedback intents to the
|
|
156
|
-
// calling agent. The agent composes the YAML style card in its own
|
|
157
|
-
// turn and re-invokes with mode='write'.
|
|
158
|
-
// - `write`: persist the agent-produced style card.
|
|
159
152
|
const mode = params.mode ?? "sample";
|
|
160
153
|
if (mode === "sample") {
|
|
161
|
-
process.stderr.write(`[voice-distil] adminUser=${userId} mode=sample corpusSize=${corpusSize} ` +
|
|
154
|
+
process.stderr.write(`[voice-distil] adminUser=${userId} format=${format} mode=sample corpusSize=${corpusSize} ` +
|
|
162
155
|
`exemplars=${samples.length} feedbackEntries=${feedbackIntents.length}\n`);
|
|
163
156
|
return {
|
|
164
157
|
profileId: prevProfileId,
|
|
@@ -175,15 +168,10 @@ export async function voiceDistilProfile(params) {
|
|
|
175
168
|
if (!styleCardYaml) {
|
|
176
169
|
throw new Error("voice-distil-profile: mode='write' requires styleCardYaml (the YAML you composed from the sample-mode response).");
|
|
177
170
|
}
|
|
178
|
-
// 7. Write the :VoiceProfile node
|
|
179
|
-
//
|
|
180
|
-
// The :AdminUser must already exist (onboarding/auth is the only
|
|
181
|
-
// legitimate origin for :AdminUser). MATCH not MERGE: a typo in
|
|
182
|
-
// userId, an env mismatch, or a dropped credential surfaces
|
|
183
|
-
// here as a typed error rather than silently minting an orphan
|
|
184
|
-
// AdminUser that bypasses the platform's label-origin protection.
|
|
171
|
+
// 7. Write the :VoiceProfile node — keyed on (accountId, userId, format) —
|
|
172
|
+
// plus LEARNED_FROM edges to every sampled node.
|
|
185
173
|
const writeResult = await session.run(`MATCH (a:AdminUser {accountId: $accountId, userId: $userId})
|
|
186
|
-
MERGE (a)-[:HAS_VOICE_PROFILE]->(p:VoiceProfile {accountId: $accountId, userId: $userId})
|
|
174
|
+
MERGE (a)-[:HAS_VOICE_PROFILE]->(p:VoiceProfile {accountId: $accountId, userId: $userId, format: $format})
|
|
187
175
|
ON CREATE SET p.createdAt = $now
|
|
188
176
|
SET p.styleCard = $styleCard,
|
|
189
177
|
p.generatedAt = $now,
|
|
@@ -197,19 +185,18 @@ export async function voiceDistilProfile(params) {
|
|
|
197
185
|
RETURN elementId(p) AS profileId`, {
|
|
198
186
|
accountId,
|
|
199
187
|
userId,
|
|
188
|
+
format,
|
|
200
189
|
styleCard: styleCardYaml,
|
|
201
190
|
now: nowIso,
|
|
202
191
|
corpusSize,
|
|
203
192
|
feedbackEntries: feedbackIntents.length,
|
|
204
193
|
sampleIds: samples.map((s) => s.id),
|
|
205
194
|
});
|
|
206
|
-
// MATCH-on-:AdminUser failure yields zero records. Throw the typed
|
|
207
|
-
// error here rather than returning a half-baked result.
|
|
208
195
|
if (writeResult.records.length === 0) {
|
|
209
196
|
throw new Error(`voice-mirror: :AdminUser {accountId='${accountId}', userId='${userId}'} not found. Onboarding must promote the operator to :AdminUser before distillation.`);
|
|
210
197
|
}
|
|
211
198
|
const profileId = writeResult.records[0].get("profileId");
|
|
212
|
-
process.stderr.write(`[voice-distil] adminUser=${userId} mode=write corpusSize=${corpusSize} generatedAt=${nowIso} feedbackEntries=${feedbackIntents.length}\n`);
|
|
199
|
+
process.stderr.write(`[voice-distil] adminUser=${userId} format=${format} mode=write corpusSize=${corpusSize} generatedAt=${nowIso} feedbackEntries=${feedbackIntents.length}\n`);
|
|
213
200
|
return {
|
|
214
201
|
profileId,
|
|
215
202
|
corpusSize,
|
|
@@ -222,4 +209,46 @@ export async function voiceDistilProfile(params) {
|
|
|
222
209
|
await session.close();
|
|
223
210
|
}
|
|
224
211
|
}
|
|
212
|
+
export async function voiceDistilProfile(params) {
|
|
213
|
+
const { accountId, userId, force = false } = params;
|
|
214
|
+
if (!accountId || !userId) {
|
|
215
|
+
throw new Error("voice-distil-profile: accountId and userId required");
|
|
216
|
+
}
|
|
217
|
+
// Single-format path.
|
|
218
|
+
if (params.format !== undefined) {
|
|
219
|
+
return distilForFormat({ ...params, format: params.format, force });
|
|
220
|
+
}
|
|
221
|
+
// Multi-format path: enumerate distinct format values present in the corpus.
|
|
222
|
+
const session = getSession();
|
|
223
|
+
let formats = [];
|
|
224
|
+
try {
|
|
225
|
+
const enumResult = await session.run(`MATCH (n)
|
|
226
|
+
WHERE ${VOICE_CORPUS_WHERE} AND n.format IS NOT NULL
|
|
227
|
+
RETURN DISTINCT n.format AS fmt`, { accountId });
|
|
228
|
+
formats = enumResult.records
|
|
229
|
+
.map((r) => r.get("fmt"))
|
|
230
|
+
.filter((f) => FORMAT_VALUES.includes(f));
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
await session.close();
|
|
234
|
+
}
|
|
235
|
+
if (formats.length === 0) {
|
|
236
|
+
process.stderr.write(`[voice-distil] adminUser=${userId} skip reason=no-format-tagged-content\n`);
|
|
237
|
+
return {
|
|
238
|
+
profileId: null,
|
|
239
|
+
corpusSize: 0,
|
|
240
|
+
generatedAt: null,
|
|
241
|
+
feedbackEntries: 0,
|
|
242
|
+
skipped: true,
|
|
243
|
+
skipReason: "no-format-tagged-content",
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
// Distil each discovered format sequentially.
|
|
247
|
+
const results = [];
|
|
248
|
+
for (const format of formats) {
|
|
249
|
+
const result = await distilForFormat({ ...params, format, force });
|
|
250
|
+
results.push(result);
|
|
251
|
+
}
|
|
252
|
+
return results;
|
|
253
|
+
}
|
|
225
254
|
//# sourceMappingURL=voice-distil-profile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-distil-profile.js","sourceRoot":"","sources":["../../src/tools/voice-distil-profile.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"voice-distil-profile.js","sourceRoot":"","sources":["../../src/tools/voice-distil-profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface VoiceIngestSessionTextParams {
|
|
2
|
+
sessionJsonlPath: string;
|
|
3
|
+
accountId: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
sessionId?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface VoiceIngestSessionTextResult {
|
|
8
|
+
ingestedCount: number;
|
|
9
|
+
skippedCount: number;
|
|
10
|
+
sessionId: string | null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Extracts text content from a "real operator" user record.
|
|
14
|
+
* Returns null when the record should be skipped.
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractOperatorText(record: unknown): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Parse a session JSONL file and return all real operator turn texts.
|
|
19
|
+
* Exported for testability (no DB required).
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractOperatorTurns(jsonlPath: string): Promise<string[]>;
|
|
22
|
+
export declare function voiceIngestSessionText(params: VoiceIngestSessionTextParams): Promise<VoiceIngestSessionTextResult>;
|
|
23
|
+
//# sourceMappingURL=voice-ingest-session-text.d.ts.map
|
package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-ingest-session-text.d.ts","sourceRoot":"","sources":["../../src/tools/voice-ingest-session-text.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,4BAA4B;IAC3C,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,4BAA4B;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CA8ClE;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAwB/E;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,4BAA4B,GACnC,OAAO,CAAC,4BAA4B,CAAC,CAsGvC"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* voice-ingest-session-text — extract operator turns from a Claude Code
|
|
3
|
+
* session JSONL and write them as :Message {format:'text', source:'admin-pty'}
|
|
4
|
+
* corpus nodes.
|
|
5
|
+
*
|
|
6
|
+
* Called by the SessionEnd hook (via the CLI wrapper) after each admin PTY
|
|
7
|
+
* session. Re-running against the same JSONL is a no-op — MERGE on
|
|
8
|
+
* (contentHash, accountId) is the deduplication primitive.
|
|
9
|
+
*
|
|
10
|
+
* After ingest, triggers voice-distil-profile {format:'text'} so the cadence
|
|
11
|
+
* guard decides whether distillation runs.
|
|
12
|
+
*
|
|
13
|
+
* JSONL record shape expected (Claude Code session format):
|
|
14
|
+
* { "type": "user" | "assistant", "message": { "role": "...", "content": ... }, ... }
|
|
15
|
+
*
|
|
16
|
+
* "Real operator text" extraction rules (excludes):
|
|
17
|
+
* - type != "user" or message.role != "user" → not the operator
|
|
18
|
+
* - content is tool_result-only → tool reply, not turn
|
|
19
|
+
* - text starts with "/" → slash command
|
|
20
|
+
* - text contains "<system-reminder>" → harness injection
|
|
21
|
+
* - text.length >= 4096 → likely pasted content
|
|
22
|
+
* - text is empty after trimming → nothing to ingest
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
import { createReadStream } from "node:fs";
|
|
26
|
+
import { createInterface } from "node:readline";
|
|
27
|
+
import { getSession } from "../lib/neo4j.js";
|
|
28
|
+
import { voiceDistilProfile } from "./voice-distil-profile.js";
|
|
29
|
+
/**
|
|
30
|
+
* Extracts text content from a "real operator" user record.
|
|
31
|
+
* Returns null when the record should be skipped.
|
|
32
|
+
*/
|
|
33
|
+
export function extractOperatorText(record) {
|
|
34
|
+
if (!record || typeof record !== "object")
|
|
35
|
+
return null;
|
|
36
|
+
const rec = record;
|
|
37
|
+
if (rec["type"] !== "user")
|
|
38
|
+
return null;
|
|
39
|
+
const msg = rec["message"];
|
|
40
|
+
if (!msg || typeof msg !== "object")
|
|
41
|
+
return null;
|
|
42
|
+
const message = msg;
|
|
43
|
+
if (message["role"] !== "user")
|
|
44
|
+
return null;
|
|
45
|
+
const content = message["content"];
|
|
46
|
+
let text = "";
|
|
47
|
+
if (typeof content === "string") {
|
|
48
|
+
text = content;
|
|
49
|
+
}
|
|
50
|
+
else if (Array.isArray(content)) {
|
|
51
|
+
// Must have at least one non-tool_result block to be a real turn.
|
|
52
|
+
const nonToolResult = content.filter((b) => typeof b === "object" && b !== null && b["type"] !== "tool_result");
|
|
53
|
+
if (nonToolResult.length === 0)
|
|
54
|
+
return null;
|
|
55
|
+
// Collect text from all text-type content blocks.
|
|
56
|
+
const parts = [];
|
|
57
|
+
for (const block of content) {
|
|
58
|
+
if (typeof block !== "object" || block === null)
|
|
59
|
+
continue;
|
|
60
|
+
const b = block;
|
|
61
|
+
if (b["type"] === "text" && typeof b["text"] === "string") {
|
|
62
|
+
parts.push(b["text"]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
text = parts.join("");
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
text = text.trim();
|
|
71
|
+
if (text.length === 0)
|
|
72
|
+
return null;
|
|
73
|
+
// Exclusions.
|
|
74
|
+
if (text.startsWith("/"))
|
|
75
|
+
return null; // slash command
|
|
76
|
+
if (text.includes("<system-reminder>"))
|
|
77
|
+
return null; // harness injection
|
|
78
|
+
if (text.length >= 4096)
|
|
79
|
+
return null; // paste block
|
|
80
|
+
return text;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Parse a session JSONL file and return all real operator turn texts.
|
|
84
|
+
* Exported for testability (no DB required).
|
|
85
|
+
*/
|
|
86
|
+
export async function extractOperatorTurns(jsonlPath) {
|
|
87
|
+
const turns = [];
|
|
88
|
+
const rl = createInterface({
|
|
89
|
+
input: createReadStream(jsonlPath, { encoding: "utf-8" }),
|
|
90
|
+
crlfDelay: Infinity,
|
|
91
|
+
});
|
|
92
|
+
for await (const line of rl) {
|
|
93
|
+
const trimmed = line.trim();
|
|
94
|
+
if (!trimmed)
|
|
95
|
+
continue;
|
|
96
|
+
let record;
|
|
97
|
+
try {
|
|
98
|
+
record = JSON.parse(trimmed);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const text = extractOperatorText(record);
|
|
104
|
+
if (text !== null) {
|
|
105
|
+
turns.push(text);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return turns;
|
|
109
|
+
}
|
|
110
|
+
export async function voiceIngestSessionText(params) {
|
|
111
|
+
const { sessionJsonlPath, accountId, userId, sessionId = null } = params;
|
|
112
|
+
if (!accountId || !userId) {
|
|
113
|
+
throw new Error("voice-ingest-session-text: accountId and userId required");
|
|
114
|
+
}
|
|
115
|
+
if (!sessionJsonlPath) {
|
|
116
|
+
throw new Error("voice-ingest-session-text: sessionJsonlPath required");
|
|
117
|
+
}
|
|
118
|
+
// 1. Extract operator turns from the JSONL.
|
|
119
|
+
let allTurns;
|
|
120
|
+
try {
|
|
121
|
+
allTurns = await extractOperatorTurns(sessionJsonlPath);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
throw new Error(`voice-ingest-session-text: failed to read JSONL at '${sessionJsonlPath}': ${err instanceof Error ? err.message : String(err)}`);
|
|
125
|
+
}
|
|
126
|
+
if (allTurns.length === 0) {
|
|
127
|
+
process.stderr.write(`[voice-ingest-session-text] sessionId=${sessionId ?? "unknown"} adminUser=${userId} turns=0 bytes=0\n`);
|
|
128
|
+
return { ingestedCount: 0, skippedCount: 0, sessionId };
|
|
129
|
+
}
|
|
130
|
+
// 2. Write each turn as a :Message node. MERGE on contentHash+accountId dedupes.
|
|
131
|
+
const session = getSession();
|
|
132
|
+
const now = new Date().toISOString();
|
|
133
|
+
let ingestedCount = 0;
|
|
134
|
+
let skippedCount = 0;
|
|
135
|
+
let totalBytes = 0;
|
|
136
|
+
try {
|
|
137
|
+
for (const body of allTurns) {
|
|
138
|
+
const contentHash = createHash("sha256")
|
|
139
|
+
.update(body, "utf-8")
|
|
140
|
+
.digest("hex");
|
|
141
|
+
const result = await session.run(`MATCH (a:AdminUser {accountId: $accountId, userId: $userId})
|
|
142
|
+
MERGE (n:Message {contentHash: $contentHash, accountId: $accountId})
|
|
143
|
+
ON CREATE SET
|
|
144
|
+
n.userId = $userId,
|
|
145
|
+
n.body = $body,
|
|
146
|
+
n.occurredAt = $now,
|
|
147
|
+
n.sessionId = $sessionId,
|
|
148
|
+
n.source = 'admin-pty',
|
|
149
|
+
n.authorshipMode = 'human-only',
|
|
150
|
+
n.format = 'text',
|
|
151
|
+
n.createdAt = $now
|
|
152
|
+
MERGE (a)-[:AUTHORED]->(n)
|
|
153
|
+
RETURN n.createdAt = $now AS isNew`, {
|
|
154
|
+
accountId,
|
|
155
|
+
userId,
|
|
156
|
+
contentHash,
|
|
157
|
+
body,
|
|
158
|
+
now,
|
|
159
|
+
sessionId,
|
|
160
|
+
});
|
|
161
|
+
if (result.records.length === 0) {
|
|
162
|
+
// :AdminUser not found — abort rather than orphan nodes.
|
|
163
|
+
throw new Error(`voice-ingest-session-text: :AdminUser {accountId='${accountId}', userId='${userId}'} not found. Onboarding must promote the operator to :AdminUser before ingest.`);
|
|
164
|
+
}
|
|
165
|
+
const isNew = result.records[0].get("isNew") === true;
|
|
166
|
+
if (isNew) {
|
|
167
|
+
ingestedCount++;
|
|
168
|
+
totalBytes += body.length;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
skippedCount++;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
await session.close();
|
|
177
|
+
}
|
|
178
|
+
process.stderr.write(`[voice-ingest-session-text] sessionId=${sessionId ?? "unknown"} adminUser=${userId} turns=${ingestedCount} bytes=${totalBytes} skipped=${skippedCount}\n`);
|
|
179
|
+
// 3. Trigger text-format distillation (cadence guard decides whether to run).
|
|
180
|
+
if (ingestedCount > 0) {
|
|
181
|
+
try {
|
|
182
|
+
await voiceDistilProfile({ accountId, userId, format: "text" });
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
// Distil failure must not fail the ingest.
|
|
186
|
+
process.stderr.write(`[voice-ingest-session-text] distil-after-ingest error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return { ingestedCount, skippedCount, sessionId };
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=voice-ingest-session-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-ingest-session-text.js","sourceRoot":"","sources":["../../src/tools/voice-ingest-session-text.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import type { VoiceFormat } from "../lib/voice-corpus.js";
|
|
1
2
|
export interface VoiceRecordFeedbackParams {
|
|
2
3
|
accountId: string;
|
|
3
4
|
userId: string;
|
|
4
5
|
originalText: string;
|
|
5
6
|
editedText: string;
|
|
7
|
+
/**
|
|
8
|
+
* Writing format of the draft being edited. Routes feedback to the
|
|
9
|
+
* matching per-format :VoiceProfile.
|
|
10
|
+
*/
|
|
11
|
+
format: VoiceFormat;
|
|
6
12
|
/**
|
|
7
13
|
* One sentence describing what changed and what voice preference it reveals.
|
|
8
14
|
* The calling agent summarises this from `originalText` vs `editedText` in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-record-feedback.d.ts","sourceRoot":"","sources":["../../src/tools/voice-record-feedback.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"voice-record-feedback.d.ts","sourceRoot":"","sources":["../../src/tools/voice-record-feedback.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,yBAAyB,CAAC,CAyEpC"}
|
|
@@ -7,21 +7,27 @@
|
|
|
7
7
|
* and passes the resulting `intent` string as a required parameter. The
|
|
8
8
|
* tool now does the graph writes only.
|
|
9
9
|
*
|
|
10
|
+
* Task 462: `format` is required. `:VoiceEdit` carries the format; the
|
|
11
|
+
* `FEEDBACK_FOR` edge targets the per-format `:VoiceProfile`.
|
|
12
|
+
*
|
|
10
13
|
* Side effects:
|
|
11
|
-
* 1. Write `(:VoiceEdit)-[:FEEDBACK_FOR]->(:VoiceProfile)` if the
|
|
12
|
-
* exists; otherwise write the :VoiceEdit alone so the
|
|
13
|
-
* back-fills the edge.
|
|
14
|
+
* 1. Write `(:VoiceEdit)-[:FEEDBACK_FOR]->(:VoiceProfile {format})` if the
|
|
15
|
+
* per-format profile exists; otherwise write the :VoiceEdit alone so the
|
|
16
|
+
* next distillation back-fills the edge.
|
|
14
17
|
*/
|
|
15
18
|
import { randomUUID } from "node:crypto";
|
|
16
19
|
import { getSession } from "../lib/neo4j.js";
|
|
17
20
|
export async function voiceRecordFeedback(params) {
|
|
18
|
-
const { accountId, userId, originalText, editedText, intent: rawIntent, context } = params;
|
|
21
|
+
const { accountId, userId, originalText, editedText, format, intent: rawIntent, context } = params;
|
|
19
22
|
if (!accountId || !userId) {
|
|
20
23
|
throw new Error("voice-record-feedback: accountId and userId required");
|
|
21
24
|
}
|
|
22
25
|
if (!originalText || !editedText) {
|
|
23
26
|
throw new Error("voice-record-feedback: originalText and editedText required");
|
|
24
27
|
}
|
|
28
|
+
if (!format) {
|
|
29
|
+
throw new Error("voice-record-feedback: format is required");
|
|
30
|
+
}
|
|
25
31
|
const intent = (rawIntent ?? "").trim();
|
|
26
32
|
if (!intent) {
|
|
27
33
|
throw new Error("voice-record-feedback: intent is required — summarise what the operator changed in-turn before calling this tool (Task 424).");
|
|
@@ -36,6 +42,7 @@ export async function voiceRecordFeedback(params) {
|
|
|
36
42
|
editId: $editId,
|
|
37
43
|
accountId: $accountId,
|
|
38
44
|
userId: $userId,
|
|
45
|
+
format: $format,
|
|
39
46
|
originalText: $originalText,
|
|
40
47
|
editedText: $editedText,
|
|
41
48
|
intent: $intent,
|
|
@@ -45,13 +52,14 @@ export async function voiceRecordFeedback(params) {
|
|
|
45
52
|
})
|
|
46
53
|
CREATE (a)-[:AUTHORED]->(e)
|
|
47
54
|
WITH e
|
|
48
|
-
OPTIONAL MATCH (:AdminUser {accountId: $accountId, userId: $userId})-[:HAS_VOICE_PROFILE]->(p:VoiceProfile)
|
|
55
|
+
OPTIONAL MATCH (:AdminUser {accountId: $accountId, userId: $userId})-[:HAS_VOICE_PROFILE]->(p:VoiceProfile {accountId: $accountId, userId: $userId, format: $format})
|
|
49
56
|
FOREACH (_ IN CASE WHEN p IS NULL THEN [] ELSE [1] END |
|
|
50
57
|
MERGE (e)-[:FEEDBACK_FOR]->(p)
|
|
51
58
|
)
|
|
52
59
|
RETURN elementId(e) AS editId, (p IS NOT NULL) AS hadProfile`, {
|
|
53
60
|
accountId,
|
|
54
61
|
userId,
|
|
62
|
+
format,
|
|
55
63
|
editId,
|
|
56
64
|
originalText,
|
|
57
65
|
editedText,
|
|
@@ -64,7 +72,7 @@ export async function voiceRecordFeedback(params) {
|
|
|
64
72
|
throw new Error(`voice-mirror: :AdminUser {accountId='${accountId}', userId='${userId}'} not found. Onboarding must promote the operator to :AdminUser before feedback can be recorded.`);
|
|
65
73
|
}
|
|
66
74
|
const hadProfile = result.records[0].get("hadProfile") === true;
|
|
67
|
-
process.stderr.write(`[voice-record-feedback] adminUser=${userId} intent="${cappedIntent.replace(/"/g, "'")}" diffBytes=${editedText.length - originalText.length}\n`);
|
|
75
|
+
process.stderr.write(`[voice-record-feedback] adminUser=${userId} format=${format} intent="${cappedIntent.replace(/"/g, "'")}" diffBytes=${editedText.length - originalText.length}\n`);
|
|
68
76
|
return { editId, intent: cappedIntent, occurredAt, hadProfile };
|
|
69
77
|
}
|
|
70
78
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-record-feedback.js","sourceRoot":"","sources":["../../src/tools/voice-record-feedback.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"voice-record-feedback.js","sourceRoot":"","sources":["../../src/tools/voice-record-feedback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAkC7C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAiC;IAEjC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IACnG,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,8HAA8H,CAC/H,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5C,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAE1C,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B;;;;;;;;;;;;;;;;;;;oEAmB8D,EAC9D;YACE,SAAS;YACT,MAAM;YACN,MAAM;YACN,MAAM;YACN,YAAY;YACZ,UAAU;YACV,MAAM,EAAE,YAAY;YACpB,UAAU;YACV,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,IAAI;YAC7B,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,IAAI;SAC9B,CACF,CAAC;QACF,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,wCAAwC,SAAS,cAAc,MAAM,mGAAmG,CACzK,CAAC;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;QAEhE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qCAAqC,MAAM,WAAW,MAAM,YAAY,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,eAAe,UAAU,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,IAAI,CAClK,CAAC;QAEF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;IAClE,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
|
package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import { type VoiceFormat } from "../lib/voice-corpus.js";
|
|
1
2
|
export interface VoiceRetrieveConditioningParams {
|
|
2
3
|
accountId: string;
|
|
3
4
|
userId: string;
|
|
4
5
|
brief: {
|
|
5
6
|
topic?: string;
|
|
6
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Writing format for corpus and profile scoping (required, Task 462).
|
|
9
|
+
* One :VoiceProfile per (accountId, userId, format).
|
|
10
|
+
*/
|
|
11
|
+
format: VoiceFormat;
|
|
12
|
+
/**
|
|
13
|
+
* K-selector: 'short' = K=5 (16k token budget, email/post);
|
|
14
|
+
* 'long' = K=15 (96k token budget, brochure/prospectus).
|
|
15
|
+
* Renamed from `format` in Task 462 to free the name for VoiceFormat.
|
|
16
|
+
*/
|
|
17
|
+
length?: "short" | "long";
|
|
7
18
|
register?: string;
|
|
8
19
|
};
|
|
9
20
|
tokenBudget?: number;
|