@kivimedia/kmhub 2.0.0 → 2.9.1
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/README.md +6 -5
- package/bin/kmhub.mjs +20 -7
- package/coach-book-output-guard.mjs +760 -0
- package/index.mjs +2 -0
- package/package.json +8 -3
- package/prompts/briefing.md +29 -0
- package/prompts/luxury.md +70 -0
- package/prompts/play.md +49 -0
- package/prompts/run.md +36 -0
- package/prompts/setup.md +33 -0
- package/prompts/vs-booked.md +46 -0
- package/prompts/what-can-you-do.md +40 -0
- package/prompts.mjs +110 -0
- package/read-only-tools.json +142 -0
- package/remote.mjs +815 -99
- package/tools/balloon-costing.mjs +80 -0
- package/tools/booking-equipment.mjs +110 -0
- package/tools/bridges.mjs +54 -0
- package/tools/calendar.mjs +9 -0
- package/tools/capabilities.mjs +155 -0
- package/tools/catalog.mjs +288 -0
- package/tools/clubs.mjs +176 -0
- package/tools/coach.mjs +771 -0
- package/tools/compare.mjs +76 -0
- package/tools/core.mjs +21 -0
- package/tools/crm.mjs +12 -3
- package/tools/dubsado.mjs +137 -0
- package/tools/exports.mjs +128 -0
- package/tools/fact-review.mjs +125 -0
- package/tools/flows.mjs +261 -0
- package/tools/forms.mjs +158 -0
- package/tools/gols.mjs +134 -0
- package/tools/hr.mjs +162 -0
- package/tools/knowledge.mjs +4 -3
- package/tools/marketing.mjs +396 -0
- package/tools/meta.mjs +2 -2
- package/tools/military.mjs +244 -0
- package/tools/outreach.mjs +27 -4
- package/tools/pending.mjs +122 -0
- package/tools/photos.mjs +140 -0
- package/tools/plays.mjs +1 -1
- package/tools/profile.mjs +118 -0
- package/tools/radar.mjs +173 -0
- package/tools/recurring-invoices.mjs +149 -0
- package/tools/reengage.mjs +434 -0
- package/tools/schedules.mjs +55 -0
- package/tools/setup.mjs +168 -0
- package/tools/sops-bridges.mjs +86 -0
- package/tools/sops.mjs +314 -0
- package/tools/sourcing.mjs +50 -2
- package/tools/strategy.mjs +146 -0
- package/tools/studio.mjs +132 -0
- package/tools/venueradar.mjs +151 -0
- package/tools/voice.mjs +134 -0
- package/tools.mjs +70 -12
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
const FORBIDDEN_OUTPUT_KEYS = new Set([
|
|
2
|
+
'credential', 'credentials', 'accesstoken', 'refreshtoken', 'apikey',
|
|
3
|
+
'password', 'secret', 'secretvalue', 'clientsecret', 'token',
|
|
4
|
+
'authorization', 'bearer', 'privatekey', 'signature',
|
|
5
|
+
'protectedspec', 'sourcetext', 'chunktext', 'excerpt', 'embedding',
|
|
6
|
+
'vector', 'vectors', 'queryvector', 'clausevector', 'clausevectors',
|
|
7
|
+
'anchor', 'anchors', 'anchorid', 'anchorids', 'semantictext', 'semantictexts',
|
|
8
|
+
'semanticmodel', 'modelinternals',
|
|
9
|
+
'systemprompt', 'providerconfig', 'connectorconfig', 'runtimeadapterid',
|
|
10
|
+
'adaptersecret', 'sourcecode', 'skillfile', 'skillfiles', 'skillpath',
|
|
11
|
+
'sqlquery', 'formula', 'expression', 'when', 'left', 'right',
|
|
12
|
+
'forbiddeneffects', 'sourcedocumentid', 'sourcesha256',
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const SECRET_VALUE_PATTERNS = [
|
|
16
|
+
/-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----/i,
|
|
17
|
+
/\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/i,
|
|
18
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/,
|
|
19
|
+
/\bsk-(?:proj-|svcacct-|ant-)?[A-Za-z0-9_-]{20,}\b/i,
|
|
20
|
+
/\bgithub_pat_[A-Za-z0-9_]{40,}\b/,
|
|
21
|
+
/\bgh[pousr]_[A-Za-z0-9]{30,}\b/,
|
|
22
|
+
/\bxox[baprs]-[A-Za-z0-9-]{20,}\b/i,
|
|
23
|
+
/\b(?:sk|rk)_live_[A-Za-z0-9]{16,}\b/i,
|
|
24
|
+
/\bAIza[0-9A-Za-z_-]{30,}\b/,
|
|
25
|
+
/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/,
|
|
26
|
+
/\bEA[A-Za-z0-9]{35,}\b/,
|
|
27
|
+
/\b(?:api[_ -]?key|access[_ -]?token|refresh[_ -]?token|client[_ -]?secret|password)\s*[:=]\s*["']?[A-Za-z0-9_+/.=-]{16,}/i,
|
|
28
|
+
/(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?):\/\/[^:\s/]+:[^@\s/]{6,}@/i,
|
|
29
|
+
/[?&](?:access_token|refresh_token|api_key|token|signature|sig)=[A-Za-z0-9_+/.=-]{16,}/i,
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export class CoachBookOutputError extends Error {
|
|
33
|
+
constructor() {
|
|
34
|
+
super('The Fully Booked Coach response failed public-output verification.');
|
|
35
|
+
this.name = 'CoachBookOutputError';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizedKey(key) {
|
|
40
|
+
return key.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function record(value) {
|
|
44
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new CoachBookOutputError();
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function records(value) {
|
|
49
|
+
if (!Array.isArray(value)) throw new CoachBookOutputError();
|
|
50
|
+
return value.map(record);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function text(value) {
|
|
54
|
+
if (typeof value !== 'string') throw new CoachBookOutputError();
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function integer(value) {
|
|
59
|
+
if (!Number.isSafeInteger(value)) throw new CoachBookOutputError();
|
|
60
|
+
return Number(value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function number(value) {
|
|
64
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) throw new CoachBookOutputError();
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function boolean(value) {
|
|
69
|
+
if (typeof value !== 'boolean') throw new CoachBookOutputError();
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function nullableText(value) {
|
|
74
|
+
if (value === null || value === undefined || value === '') return null;
|
|
75
|
+
return text(value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function stringArray(value) {
|
|
79
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string')) {
|
|
80
|
+
throw new CoachBookOutputError();
|
|
81
|
+
}
|
|
82
|
+
return [...value];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function optionalNumber(target, source, key) {
|
|
86
|
+
if (source[key] !== undefined) target[key] = number(source[key]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function optionalText(target, source, key) {
|
|
90
|
+
if (source[key] !== undefined) target[key] = text(source[key]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function projectRanges(value) {
|
|
94
|
+
return records(value).map((range) => {
|
|
95
|
+
const currentLocator = range.start !== undefined || range.end !== undefined;
|
|
96
|
+
return currentLocator
|
|
97
|
+
? { start: integer(range.start), end: integer(range.end) }
|
|
98
|
+
: { start: integer(range.pdfPageStart), end: integer(range.pdfPageEnd) };
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function projectCitations(value) {
|
|
103
|
+
return records(value).map((citation) => ({
|
|
104
|
+
label: text(citation.label),
|
|
105
|
+
pdfPageStart: integer(citation.pdfPageStart),
|
|
106
|
+
pdfPageEnd: integer(citation.pdfPageEnd),
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function projectField(value) {
|
|
111
|
+
const source = record(value);
|
|
112
|
+
const projected = {
|
|
113
|
+
id: text(source.id),
|
|
114
|
+
type: text(source.type),
|
|
115
|
+
label: text(source.label),
|
|
116
|
+
required: boolean(source.required),
|
|
117
|
+
};
|
|
118
|
+
optionalText(projected, source, 'helpText');
|
|
119
|
+
for (const key of ['minimum', 'maximum', 'decimalPlaces', 'minimumItems', 'maximumItems']) {
|
|
120
|
+
optionalNumber(projected, source, key);
|
|
121
|
+
}
|
|
122
|
+
if (source.choices !== undefined) {
|
|
123
|
+
projected.choices = records(source.choices).map((choice) => ({
|
|
124
|
+
value: text(choice.value),
|
|
125
|
+
label: text(choice.label),
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
return projected;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function projectPublicSpec(value) {
|
|
132
|
+
const source = record(value);
|
|
133
|
+
const projected = {
|
|
134
|
+
schemaVersion: text(source.schemaVersion),
|
|
135
|
+
title: text(source.title),
|
|
136
|
+
summary: text(source.summary),
|
|
137
|
+
category: text(source.category),
|
|
138
|
+
estimatedMinutes: integer(source.estimatedMinutes),
|
|
139
|
+
executorKind: text(source.executorKind),
|
|
140
|
+
effectClass: text(source.effectClass),
|
|
141
|
+
steps: records(source.steps).map((step) => ({
|
|
142
|
+
id: text(step.id),
|
|
143
|
+
title: text(step.title),
|
|
144
|
+
description: text(step.description),
|
|
145
|
+
fields: records(step.fields).map(projectField),
|
|
146
|
+
})),
|
|
147
|
+
artifactSections: records(source.artifactSections).map((section) => ({
|
|
148
|
+
id: text(section.id),
|
|
149
|
+
title: text(section.title),
|
|
150
|
+
fieldIds: stringArray(section.fieldIds),
|
|
151
|
+
calculationIds: stringArray(section.calculationIds),
|
|
152
|
+
})),
|
|
153
|
+
sourceCitations: projectCitations(source.sourceCitations),
|
|
154
|
+
};
|
|
155
|
+
optionalText(projected, source, 'availabilityClass');
|
|
156
|
+
return projected;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function projectCatalogMethod(value) {
|
|
160
|
+
const source = record(value);
|
|
161
|
+
return {
|
|
162
|
+
tool_spec_id: text(source.tool_spec_id),
|
|
163
|
+
version: integer(source.version),
|
|
164
|
+
title: text(source.title),
|
|
165
|
+
sequence: integer(source.sequence),
|
|
166
|
+
summary: text(source.summary),
|
|
167
|
+
category: text(source.category),
|
|
168
|
+
estimated_minutes: integer(source.estimated_minutes),
|
|
169
|
+
implementation_status: text(source.implementation_status),
|
|
170
|
+
availability_class: text(source.availability_class),
|
|
171
|
+
executor_kind: text(source.executor_kind),
|
|
172
|
+
effect_class: text(source.effect_class),
|
|
173
|
+
runnable: boolean(source.runnable),
|
|
174
|
+
specialized: boolean(source.specialized),
|
|
175
|
+
source_page_ranges: projectRanges(source.source_page_ranges),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function projectDescribedMethod(value) {
|
|
180
|
+
const source = record(value);
|
|
181
|
+
return { ...projectCatalogMethod(source), public_spec: projectPublicSpec(source.public_spec) };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function projectSummary(value) {
|
|
185
|
+
const source = record(value);
|
|
186
|
+
return {
|
|
187
|
+
run_id: text(source.run_id),
|
|
188
|
+
status: text(source.status),
|
|
189
|
+
tool_spec_id: text(source.tool_spec_id),
|
|
190
|
+
tool_spec_version: integer(source.tool_spec_version),
|
|
191
|
+
artifact_count: integer(source.artifact_count),
|
|
192
|
+
decision_count: integer(source.decision_count),
|
|
193
|
+
reserved_charge_usd_micros: source.reserved_charge_usd_micros === undefined
|
|
194
|
+
? 0
|
|
195
|
+
: integer(source.reserved_charge_usd_micros),
|
|
196
|
+
retained_provider_cost_usd_micros: source.retained_provider_cost_usd_micros === undefined
|
|
197
|
+
? 0
|
|
198
|
+
: integer(source.retained_provider_cost_usd_micros),
|
|
199
|
+
charged_usd_micros: source.charged_usd_micros === undefined
|
|
200
|
+
? 0
|
|
201
|
+
: integer(source.charged_usd_micros),
|
|
202
|
+
released_usd_micros: source.released_usd_micros === undefined
|
|
203
|
+
? 0
|
|
204
|
+
: integer(source.released_usd_micros),
|
|
205
|
+
approved_at: nullableText(source.approved_at),
|
|
206
|
+
started_at: nullableText(source.started_at),
|
|
207
|
+
settled_at: nullableText(source.settled_at),
|
|
208
|
+
created_at: nullableText(source.created_at),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function projectArtifactValue(value) {
|
|
213
|
+
if (typeof value === 'string' || typeof value === 'boolean') return value;
|
|
214
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
215
|
+
return stringArray(value);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function projectArtifact(value) {
|
|
219
|
+
if (value === null || value === undefined) return null;
|
|
220
|
+
const source = record(value);
|
|
221
|
+
return {
|
|
222
|
+
title: text(source.title),
|
|
223
|
+
sections: records(source.sections).map((section) => ({
|
|
224
|
+
id: text(section.id),
|
|
225
|
+
title: text(section.title),
|
|
226
|
+
values: records(section.values).map((item) => ({
|
|
227
|
+
id: text(item.id),
|
|
228
|
+
label: text(item.label),
|
|
229
|
+
value: projectArtifactValue(item.value),
|
|
230
|
+
})),
|
|
231
|
+
})),
|
|
232
|
+
sourceCitations: projectCitations(source.sourceCitations),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function projectBoundaries(value) {
|
|
237
|
+
const source = record(value);
|
|
238
|
+
return {
|
|
239
|
+
providerUse: boolean(source.providerUse),
|
|
240
|
+
spend: boolean(source.spend),
|
|
241
|
+
contact: boolean(source.contact),
|
|
242
|
+
publication: boolean(source.publication),
|
|
243
|
+
durableCrmChange: boolean(source.durableCrmChange),
|
|
244
|
+
connectorDispatch: boolean(source.connectorDispatch),
|
|
245
|
+
outcomePromise: boolean(source.outcomePromise),
|
|
246
|
+
separateApprovalRequired: boolean(source.separateApprovalRequired),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function projectMethodResult(value) {
|
|
251
|
+
if (value === null || value === undefined) return null;
|
|
252
|
+
const source = record(value);
|
|
253
|
+
return {
|
|
254
|
+
schemaVersion: text(source.schemaVersion),
|
|
255
|
+
toolSpecId: text(source.toolSpecId),
|
|
256
|
+
toolSpecVersion: integer(source.toolSpecVersion),
|
|
257
|
+
status: text(source.status),
|
|
258
|
+
completedStepIds: stringArray(source.completedStepIds),
|
|
259
|
+
nextStepId: nullableText(source.nextStepId),
|
|
260
|
+
calculations: records(source.calculations).map((calculation) => ({
|
|
261
|
+
id: text(calculation.id),
|
|
262
|
+
label: text(calculation.label),
|
|
263
|
+
rawValue: number(calculation.rawValue),
|
|
264
|
+
formattedValue: text(calculation.formattedValue),
|
|
265
|
+
})),
|
|
266
|
+
decisions: records(source.decisions).map((decision) => ({
|
|
267
|
+
id: text(decision.id),
|
|
268
|
+
label: text(decision.label),
|
|
269
|
+
outcome: text(decision.outcome),
|
|
270
|
+
guidance: text(decision.guidance),
|
|
271
|
+
})),
|
|
272
|
+
artifact: projectArtifact(source.artifact),
|
|
273
|
+
boundaries: projectBoundaries(source.boundaries),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function projectReceipt(value) {
|
|
278
|
+
if (value === null || value === undefined) return null;
|
|
279
|
+
const source = record(value);
|
|
280
|
+
const projected = {
|
|
281
|
+
outcome: text(source.outcome),
|
|
282
|
+
retained_provider_cost_usd_micros: integer(source.retained_provider_cost_usd_micros),
|
|
283
|
+
charged_usd_micros: integer(source.charged_usd_micros),
|
|
284
|
+
};
|
|
285
|
+
for (const key of ['released_usd_micros', 'promo_released_usd_micros', 'paid_released_usd_micros', 'markup_multiplier']) {
|
|
286
|
+
if (source[key] !== undefined) projected[key] = integer(source[key]);
|
|
287
|
+
}
|
|
288
|
+
for (const key of ['provider_use', 'external_effect_performed']) {
|
|
289
|
+
if (source[key] !== undefined) projected[key] = boolean(source[key]);
|
|
290
|
+
}
|
|
291
|
+
if (source.created_at !== undefined) projected.created_at = nullableText(source.created_at);
|
|
292
|
+
return projected;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function projectGenericRun(value) {
|
|
296
|
+
const source = record(value);
|
|
297
|
+
const method = record(source.method);
|
|
298
|
+
const progress = record(source.progress);
|
|
299
|
+
return {
|
|
300
|
+
summary: projectSummary(source.summary),
|
|
301
|
+
method: {
|
|
302
|
+
tool_spec_id: text(method.tool_spec_id),
|
|
303
|
+
method_id: text(method.method_id),
|
|
304
|
+
sequence: integer(method.sequence),
|
|
305
|
+
version: integer(method.version),
|
|
306
|
+
title: text(method.title),
|
|
307
|
+
executor_kind: text(method.executor_kind),
|
|
308
|
+
effect_class: text(method.effect_class),
|
|
309
|
+
public_spec: projectPublicSpec(method.public_spec),
|
|
310
|
+
source_page_ranges: projectRanges(method.source_page_ranges),
|
|
311
|
+
},
|
|
312
|
+
progress: {
|
|
313
|
+
completed_step_ids: stringArray(progress.completed_step_ids),
|
|
314
|
+
next_step_id: nullableText(progress.next_step_id),
|
|
315
|
+
result: projectMethodResult(progress.result),
|
|
316
|
+
settlement_pending: boolean(progress.settlement_pending),
|
|
317
|
+
},
|
|
318
|
+
receipt: projectReceipt(source.receipt),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function projectNicheDimension(value) {
|
|
323
|
+
const source = record(value);
|
|
324
|
+
const threshold = record(source.threshold);
|
|
325
|
+
const evidenceGate = record(source.evidenceGate);
|
|
326
|
+
return {
|
|
327
|
+
dimension: text(source.dimension),
|
|
328
|
+
score: number(source.score),
|
|
329
|
+
threshold: {
|
|
330
|
+
operator: text(threshold.operator),
|
|
331
|
+
value: number(threshold.value),
|
|
332
|
+
sourcePage: integer(threshold.sourcePage),
|
|
333
|
+
},
|
|
334
|
+
thresholdPassed: boolean(source.thresholdPassed),
|
|
335
|
+
falsePositiveObserved: boolean(source.falsePositiveObserved),
|
|
336
|
+
passed: boolean(source.passed),
|
|
337
|
+
confidence: text(source.confidence),
|
|
338
|
+
evidenceGate: {
|
|
339
|
+
directSourcePresent: boolean(evidenceGate.directSourcePresent),
|
|
340
|
+
counterevidenceCompleted: boolean(evidenceGate.counterevidenceCompleted),
|
|
341
|
+
passed: boolean(evidenceGate.passed),
|
|
342
|
+
failures: stringArray(evidenceGate.failures),
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function projectNicheResult(value) {
|
|
348
|
+
const source = record(value);
|
|
349
|
+
const dimensions = record(source.dimensions);
|
|
350
|
+
const plan = record(source.validationPlan);
|
|
351
|
+
const pace = record(plan.paceObservationWeeks);
|
|
352
|
+
const stopRules = record(plan.falsePositiveStopRules);
|
|
353
|
+
const boundaries = record(source.boundaries);
|
|
354
|
+
return {
|
|
355
|
+
schemaVersion: text(source.schemaVersion),
|
|
356
|
+
dimensions: {
|
|
357
|
+
findability: projectNicheDimension(dimensions.findability),
|
|
358
|
+
bragability: projectNicheDimension(dimensions.bragability),
|
|
359
|
+
sellability: projectNicheDimension(dimensions.sellability),
|
|
360
|
+
},
|
|
361
|
+
overallStatus: text(source.overallStatus),
|
|
362
|
+
confidence: text(source.confidence),
|
|
363
|
+
recommendation: text(source.recommendation),
|
|
364
|
+
validationPlan: {
|
|
365
|
+
durationDays: integer(plan.durationDays),
|
|
366
|
+
paceObservationWeeks: { minimum: integer(pace.minimum), maximum: integer(pace.maximum) },
|
|
367
|
+
weeklyAcceptedCollaborationTarget: integer(plan.weeklyAcceptedCollaborationTarget),
|
|
368
|
+
approximateAnnualCollaborationPace: integer(plan.approximateAnnualCollaborationPace),
|
|
369
|
+
brandAssetsRequiredBeforeTest: boolean(plan.brandAssetsRequiredBeforeTest),
|
|
370
|
+
trackedSignals: stringArray(plan.trackedSignals),
|
|
371
|
+
falsePositiveStopRules: {
|
|
372
|
+
findability: text(stopRules.findability),
|
|
373
|
+
bragability: text(stopRules.bragability),
|
|
374
|
+
sellability: text(stopRules.sellability),
|
|
375
|
+
},
|
|
376
|
+
humanDecisionRequired: boolean(plan.humanDecisionRequired),
|
|
377
|
+
separateApprovalRequiredFor: stringArray(plan.separateApprovalRequiredFor),
|
|
378
|
+
performsExternalAction: boolean(plan.performsExternalAction),
|
|
379
|
+
promisesOutcome: boolean(plan.promisesOutcome),
|
|
380
|
+
sourcePages: Array.isArray(plan.sourcePages) ? plan.sourcePages.map(integer) : (() => { throw new CoachBookOutputError(); })(),
|
|
381
|
+
sourceNote: text(plan.sourceNote),
|
|
382
|
+
},
|
|
383
|
+
boundaries: {
|
|
384
|
+
modelScoreAllowed: boolean(boundaries.modelScoreAllowed),
|
|
385
|
+
providerUse: boolean(boundaries.providerUse),
|
|
386
|
+
spend: boolean(boundaries.spend),
|
|
387
|
+
contact: boolean(boundaries.contact),
|
|
388
|
+
publication: boolean(boundaries.publication),
|
|
389
|
+
outcomePromise: boolean(boundaries.outcomePromise),
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function projectNicheRun(value) {
|
|
395
|
+
const source = record(value);
|
|
396
|
+
const artifact = source.artifact === null || source.artifact === undefined ? null : record(source.artifact);
|
|
397
|
+
const decision = source.decision === null || source.decision === undefined ? null : record(source.decision);
|
|
398
|
+
return {
|
|
399
|
+
summary: projectSummary(source.summary),
|
|
400
|
+
artifact: artifact ? {
|
|
401
|
+
id: text(artifact.id),
|
|
402
|
+
artifact_type: text(artifact.artifact_type),
|
|
403
|
+
result: projectNicheResult(artifact.result),
|
|
404
|
+
source_citations: records(artifact.source_citations).map((citation) => ({
|
|
405
|
+
locator: text(citation.locator),
|
|
406
|
+
page_start: integer(citation.page_start),
|
|
407
|
+
page_end: integer(citation.page_end),
|
|
408
|
+
supports: text(citation.supports),
|
|
409
|
+
})),
|
|
410
|
+
resume_supported_surfaces: stringArray(artifact.resume_supported_surfaces),
|
|
411
|
+
created_at: nullableText(artifact.created_at),
|
|
412
|
+
} : null,
|
|
413
|
+
decision: decision ? {
|
|
414
|
+
id: text(decision.id),
|
|
415
|
+
decision_type: text(decision.decision_type),
|
|
416
|
+
decision_status: text(decision.decision_status),
|
|
417
|
+
decision_payload: {
|
|
418
|
+
confirmed_by_user: boolean(record(decision.decision_payload).confirmed_by_user),
|
|
419
|
+
tool_spec_id: text(record(decision.decision_payload).tool_spec_id),
|
|
420
|
+
result_status: text(record(decision.decision_payload).result_status),
|
|
421
|
+
performs_external_action: boolean(record(decision.decision_payload).performs_external_action),
|
|
422
|
+
},
|
|
423
|
+
created_at: nullableText(decision.created_at),
|
|
424
|
+
} : null,
|
|
425
|
+
receipt: projectReceipt(source.receipt),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function hasForbiddenOutput(value, allowConfirmationToken = false) {
|
|
430
|
+
if (typeof value === 'string') return SECRET_VALUE_PATTERNS.some((pattern) => pattern.test(value));
|
|
431
|
+
if (Array.isArray(value)) return value.some((entry) => hasForbiddenOutput(entry, false));
|
|
432
|
+
if (!value || typeof value !== 'object') return false;
|
|
433
|
+
return Object.entries(value).some(([key, child]) => {
|
|
434
|
+
const normalized = normalizedKey(key);
|
|
435
|
+
const allowedToken = allowConfirmationToken && normalized === 'confirmtoken';
|
|
436
|
+
if (!allowedToken && (
|
|
437
|
+
FORBIDDEN_OUTPUT_KEYS.has(normalized)
|
|
438
|
+
|| normalized.endsWith('token')
|
|
439
|
+
|| normalized.endsWith('secret')
|
|
440
|
+
|| normalized.endsWith('password')
|
|
441
|
+
|| normalized.endsWith('privatekey')
|
|
442
|
+
|| normalized.endsWith('digest')
|
|
443
|
+
)) return true;
|
|
444
|
+
return allowedToken ? false : hasForbiddenOutput(child, false);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function assertPublicOutput(value) {
|
|
449
|
+
if (hasForbiddenOutput(value)) throw new CoachBookOutputError();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const FBC_METHOD_ID_RE = /^fbc[.]method[.][0-9]{3}[.][a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
453
|
+
export const FBC_METHOD_RANKER_VERSION = 'fbc.method-ranker.semantic-hybrid-v2';
|
|
454
|
+
const FBC_RECOMMENDATION_OUTCOMES = new Set([
|
|
455
|
+
'recommended', 'clarification_required', 'no_runnable_match', 'no_match',
|
|
456
|
+
]);
|
|
457
|
+
const FBC_RECOMMENDATION_CONFIDENCE = new Set(['high', 'medium', 'low']);
|
|
458
|
+
const FBC_RECOMMENDATION_AVAILABILITY = new Set(['runnable_only', 'include_pending']);
|
|
459
|
+
const FBC_RECOMMENDATION_EXECUTORS = new Set(['deterministic_single_step', 'guided_multi_step']);
|
|
460
|
+
const FBC_RECOMMENDATION_EFFECTS = new Set(['none', 'action_preview']);
|
|
461
|
+
const FBC_RECOMMENDATION_STAGES = new Set([
|
|
462
|
+
'purpose_and_capacity', 'oath', 'niche', 'collaboration', 'authority_content',
|
|
463
|
+
'audience_growth', 'strategy_session', 'offer_and_package', 'follow_up',
|
|
464
|
+
'client_map', 'super_coaching_delivery', 'results_and_proof', 'renewal_and_referral',
|
|
465
|
+
]);
|
|
466
|
+
const FBC_RECOMMENDATION_REASONS = new Set(['goal_too_broad', 'close_scores', 'low_confidence']);
|
|
467
|
+
const FBC_RECOMMENDATION_STATUSES = new Set(['implemented', 'method_owner_accepted', 'live_proven']);
|
|
468
|
+
const SHA256_RE = /^[0-9a-f]{64}$/;
|
|
469
|
+
|
|
470
|
+
function boundedText(value, minimum, maximum) {
|
|
471
|
+
const result = text(value);
|
|
472
|
+
if (result.length < minimum || result.length > maximum) throw new CoachBookOutputError();
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function enumText(value, allowed) {
|
|
477
|
+
const result = text(value);
|
|
478
|
+
if (!allowed.has(result)) throw new CoachBookOutputError();
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function nullableEnumText(value, allowed) {
|
|
483
|
+
if (value === null || value === undefined) return null;
|
|
484
|
+
return enumText(value, allowed);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function methodId(value) {
|
|
488
|
+
const result = text(value);
|
|
489
|
+
if (!FBC_METHOD_ID_RE.test(result)) throw new CoachBookOutputError();
|
|
490
|
+
return result;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function projectRecommendationMatch(value, expectedRank, lane) {
|
|
494
|
+
const source = record(value);
|
|
495
|
+
const rank = integer(source.rank);
|
|
496
|
+
const estimatedMinutes = integer(source.estimated_minutes);
|
|
497
|
+
const score = number(source.score);
|
|
498
|
+
if (
|
|
499
|
+
rank !== expectedRank
|
|
500
|
+
|| estimatedMinutes < 1
|
|
501
|
+
|| estimatedMinutes > 240
|
|
502
|
+
|| score < 0
|
|
503
|
+
|| score > 1
|
|
504
|
+
) {
|
|
505
|
+
throw new CoachBookOutputError();
|
|
506
|
+
}
|
|
507
|
+
const matchedIntents = stringArray(source.matched_intents);
|
|
508
|
+
if (
|
|
509
|
+
matchedIntents.length > 6
|
|
510
|
+
|| matchedIntents.some((intent) => intent.length < 1 || intent.length > 80)
|
|
511
|
+
) {
|
|
512
|
+
throw new CoachBookOutputError();
|
|
513
|
+
}
|
|
514
|
+
const sourcePageRanges = projectRanges(source.source_page_ranges);
|
|
515
|
+
if (sourcePageRanges.length > 20) throw new CoachBookOutputError();
|
|
516
|
+
const implementationStatus = enumText(source.implementation_status, FBC_RECOMMENDATION_STATUSES);
|
|
517
|
+
const runnable = boolean(source.runnable);
|
|
518
|
+
const canStart = boolean(source.can_start);
|
|
519
|
+
const laneIsValid = lane === 'runnable'
|
|
520
|
+
? ['method_owner_accepted', 'live_proven'].includes(implementationStatus)
|
|
521
|
+
&& runnable === true
|
|
522
|
+
&& canStart === true
|
|
523
|
+
: implementationStatus === 'implemented'
|
|
524
|
+
&& runnable === false
|
|
525
|
+
&& canStart === false;
|
|
526
|
+
if (!laneIsValid) throw new CoachBookOutputError();
|
|
527
|
+
return {
|
|
528
|
+
rank,
|
|
529
|
+
tool_spec_id: methodId(source.tool_spec_id),
|
|
530
|
+
title: boundedText(source.title, 3, 200),
|
|
531
|
+
summary: boundedText(source.summary, 10, 1200),
|
|
532
|
+
category: boundedText(source.category, 2, 160),
|
|
533
|
+
estimated_minutes: estimatedMinutes,
|
|
534
|
+
executor_kind: enumText(source.executor_kind, FBC_RECOMMENDATION_EXECUTORS),
|
|
535
|
+
effect_class: enumText(source.effect_class, FBC_RECOMMENDATION_EFFECTS),
|
|
536
|
+
implementation_status: implementationStatus,
|
|
537
|
+
runnable,
|
|
538
|
+
can_start: canStart,
|
|
539
|
+
score,
|
|
540
|
+
matched_intents: matchedIntents,
|
|
541
|
+
use_when: boundedText(source.use_when, 4, 1000),
|
|
542
|
+
source_page_ranges: sourcePageRanges,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function projectRecommendationMatches(value, lane) {
|
|
547
|
+
const source = records(value);
|
|
548
|
+
if (source.length > 3) throw new CoachBookOutputError();
|
|
549
|
+
const projected = source.map((match, index) => projectRecommendationMatch(match, index + 1, lane));
|
|
550
|
+
const toolSpecIds = projected.map((match) => String(match.tool_spec_id));
|
|
551
|
+
const ranks = projected.map((match) => Number(match.rank));
|
|
552
|
+
if (new Set(toolSpecIds).size !== toolSpecIds.length || new Set(ranks).size !== ranks.length) {
|
|
553
|
+
throw new CoachBookOutputError();
|
|
554
|
+
}
|
|
555
|
+
return projected;
|
|
556
|
+
}
|
|
557
|
+
function projectRecommendationClarification(value) {
|
|
558
|
+
if (value === null || value === undefined) return null;
|
|
559
|
+
const source = record(value);
|
|
560
|
+
const choices = records(source.choices);
|
|
561
|
+
if (choices.length < 1 || choices.length > 3) throw new CoachBookOutputError();
|
|
562
|
+
const question = text(source.question);
|
|
563
|
+
const instruction = text(source.instruction);
|
|
564
|
+
if (
|
|
565
|
+
question !== 'Which result do you want right now?'
|
|
566
|
+
|| instruction !== 'Ask this question exactly and do not choose for the user.'
|
|
567
|
+
) {
|
|
568
|
+
throw new CoachBookOutputError();
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
reason: enumText(source.reason, FBC_RECOMMENDATION_REASONS),
|
|
572
|
+
question,
|
|
573
|
+
choices: choices.map((choice) => ({
|
|
574
|
+
choice_id: methodId(choice.choice_id),
|
|
575
|
+
label: boundedText(choice.label, 1, 200),
|
|
576
|
+
description: boundedText(choice.description, 4, 1000),
|
|
577
|
+
})),
|
|
578
|
+
instruction,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function projectRecommendationBoundaries(value) {
|
|
583
|
+
const source = record(value);
|
|
584
|
+
const projected = {
|
|
585
|
+
public_metadata_only: boolean(source.public_metadata_only),
|
|
586
|
+
source_retrieval: boolean(source.source_retrieval),
|
|
587
|
+
protected_spec_access: boolean(source.protected_spec_access),
|
|
588
|
+
provider_use: boolean(source.provider_use),
|
|
589
|
+
writes: boolean(source.writes),
|
|
590
|
+
ask_the_book: text(source.ask_the_book),
|
|
591
|
+
};
|
|
592
|
+
if (
|
|
593
|
+
projected.public_metadata_only !== true
|
|
594
|
+
|| projected.source_retrieval !== false
|
|
595
|
+
|| projected.protected_spec_access !== false
|
|
596
|
+
|| projected.provider_use !== false
|
|
597
|
+
|| projected.writes !== false
|
|
598
|
+
|| projected.ask_the_book !== 'closed_by_product_decision'
|
|
599
|
+
) {
|
|
600
|
+
throw new CoachBookOutputError();
|
|
601
|
+
}
|
|
602
|
+
return projected;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function assertRecommendationSourceSafe(source) {
|
|
606
|
+
const {
|
|
607
|
+
registry_digest: registryDigest,
|
|
608
|
+
candidate_set_digest: candidateSetDigest,
|
|
609
|
+
goal_digest: goalDigest,
|
|
610
|
+
...publicCandidate
|
|
611
|
+
} = source;
|
|
612
|
+
const digests = [registryDigest, candidateSetDigest, goalDigest];
|
|
613
|
+
const presentDigests = digests.filter((digest) => digest !== undefined);
|
|
614
|
+
if (presentDigests.length !== 0 && presentDigests.length !== digests.length) {
|
|
615
|
+
throw new CoachBookOutputError();
|
|
616
|
+
}
|
|
617
|
+
for (const digest of presentDigests) {
|
|
618
|
+
if (typeof digest !== 'string' || !SHA256_RE.test(digest)) throw new CoachBookOutputError();
|
|
619
|
+
}
|
|
620
|
+
assertPublicOutput(publicCandidate);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function projectRecommendation(value) {
|
|
624
|
+
assertRecommendationSourceSafe(value);
|
|
625
|
+
if (
|
|
626
|
+
value.schema_version !== 'fbc.method-recommendations.v1'
|
|
627
|
+
|| value.ranker_version !== FBC_METHOD_RANKER_VERSION
|
|
628
|
+
) {
|
|
629
|
+
throw new CoachBookOutputError();
|
|
630
|
+
}
|
|
631
|
+
const outcome = enumText(value.outcome, FBC_RECOMMENDATION_OUTCOMES);
|
|
632
|
+
const confidence = nullableEnumText(value.confidence, FBC_RECOMMENDATION_CONFIDENCE);
|
|
633
|
+
const context = record(value.context_used);
|
|
634
|
+
const methodStage = nullableEnumText(context.method_stage, FBC_RECOMMENDATION_STAGES);
|
|
635
|
+
const maxMinutes = context.max_minutes === null || context.max_minutes === undefined
|
|
636
|
+
? null
|
|
637
|
+
: integer(context.max_minutes);
|
|
638
|
+
if (maxMinutes !== null && (maxMinutes < 5 || maxMinutes > 240)) {
|
|
639
|
+
throw new CoachBookOutputError();
|
|
640
|
+
}
|
|
641
|
+
const matches = projectRecommendationMatches(value.matches, 'runnable');
|
|
642
|
+
const pendingMatches = projectRecommendationMatches(value.pending_matches, 'pending');
|
|
643
|
+
const matchIds = matches.map((match) => String(match.tool_spec_id));
|
|
644
|
+
const pendingIds = pendingMatches.map((match) => String(match.tool_spec_id));
|
|
645
|
+
if (pendingIds.some((toolSpecId) => matchIds.includes(toolSpecId))) {
|
|
646
|
+
throw new CoachBookOutputError();
|
|
647
|
+
}
|
|
648
|
+
const selectedToolSpecId = value.selected_tool_spec_id === null
|
|
649
|
+
? null
|
|
650
|
+
: methodId(value.selected_tool_spec_id);
|
|
651
|
+
const clarification = projectRecommendationClarification(value.clarification);
|
|
652
|
+
const clarificationChoices = clarification === null ? [] : records(clarification.choices);
|
|
653
|
+
const clarificationBindsMatches = clarification !== null
|
|
654
|
+
&& clarificationChoices.length === matches.length
|
|
655
|
+
&& clarificationChoices.every((choice, index) => (
|
|
656
|
+
choice.choice_id === matches[index]?.tool_spec_id
|
|
657
|
+
&& choice.description === matches[index]?.use_when
|
|
658
|
+
));
|
|
659
|
+
const firstMatchId = matches[0]?.tool_spec_id ?? null;
|
|
660
|
+
const outcomeIsValid = outcome === 'recommended'
|
|
661
|
+
? matches.length > 0
|
|
662
|
+
&& selectedToolSpecId === firstMatchId
|
|
663
|
+
&& (confidence === 'high' || confidence === 'medium')
|
|
664
|
+
&& value.clarification === null
|
|
665
|
+
: outcome === 'clarification_required'
|
|
666
|
+
? matches.length > 0
|
|
667
|
+
&& selectedToolSpecId === null
|
|
668
|
+
&& confidence === 'low'
|
|
669
|
+
&& clarificationBindsMatches
|
|
670
|
+
: outcome === 'no_runnable_match'
|
|
671
|
+
? matches.length === 0
|
|
672
|
+
&& pendingMatches.length > 0
|
|
673
|
+
&& selectedToolSpecId === null
|
|
674
|
+
&& value.confidence === null
|
|
675
|
+
&& value.clarification === null
|
|
676
|
+
: matches.length === 0
|
|
677
|
+
&& pendingMatches.length === 0
|
|
678
|
+
&& selectedToolSpecId === null
|
|
679
|
+
&& value.confidence === null
|
|
680
|
+
&& value.clarification === null;
|
|
681
|
+
if (!outcomeIsValid || (outcome === 'clarification_required') !== (clarification !== null)) {
|
|
682
|
+
throw new CoachBookOutputError();
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
ok: true,
|
|
686
|
+
schema_version: 'fbc.method-recommendations.v1',
|
|
687
|
+
ranker_version: FBC_METHOD_RANKER_VERSION,
|
|
688
|
+
outcome,
|
|
689
|
+
confidence,
|
|
690
|
+
context_used: {
|
|
691
|
+
method_stage: methodStage,
|
|
692
|
+
availability_scope: enumText(context.availability_scope, FBC_RECOMMENDATION_AVAILABILITY),
|
|
693
|
+
max_minutes: maxMinutes,
|
|
694
|
+
executor_kind: nullableEnumText(context.executor_kind, FBC_RECOMMENDATION_EXECUTORS),
|
|
695
|
+
effect_class: nullableEnumText(context.effect_class, FBC_RECOMMENDATION_EFFECTS),
|
|
696
|
+
},
|
|
697
|
+
matches,
|
|
698
|
+
selected_tool_spec_id: selectedToolSpecId,
|
|
699
|
+
clarification,
|
|
700
|
+
pending_matches: pendingMatches,
|
|
701
|
+
boundaries: projectRecommendationBoundaries(value.boundaries),
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
function projectError(source) {
|
|
705
|
+
const projected = { error: text(source.error) };
|
|
706
|
+
if (source.message !== undefined) projected.message = text(source.message);
|
|
707
|
+
if (source.billing_url !== undefined) {
|
|
708
|
+
const billingUrl = text(source.billing_url);
|
|
709
|
+
if (!billingUrl.startsWith('https://')) throw new CoachBookOutputError();
|
|
710
|
+
projected.billing_url = billingUrl;
|
|
711
|
+
}
|
|
712
|
+
return projected;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function projectConfirmation(source) {
|
|
716
|
+
if (source.error !== 'confirmation_required' || typeof source.confirm_token !== 'string') {
|
|
717
|
+
throw new CoachBookOutputError();
|
|
718
|
+
}
|
|
719
|
+
if (source.confirm_token.length < 1 || source.confirm_token.length > 4096 || !/^[A-Za-z0-9._-]+$/.test(source.confirm_token)) {
|
|
720
|
+
throw new CoachBookOutputError();
|
|
721
|
+
}
|
|
722
|
+
if (hasForbiddenOutput(source, true)) throw new CoachBookOutputError();
|
|
723
|
+
const projected = {
|
|
724
|
+
error: 'confirmation_required',
|
|
725
|
+
confirm_token: source.confirm_token,
|
|
726
|
+
};
|
|
727
|
+
for (const key of ['what_would_happen', 'message', 'note', 'why']) optionalText(projected, source, key);
|
|
728
|
+
if (source.expires_in_seconds !== undefined) projected.expires_in_seconds = integer(source.expires_in_seconds);
|
|
729
|
+
return projected;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
export function projectCoachBookConnectorResult(kind, value) {
|
|
733
|
+
if (!['catalog', 'describe', 'recommendation', 'run'].includes(kind)) throw new CoachBookOutputError();
|
|
734
|
+
const source = record(value);
|
|
735
|
+
const data = record(source.data);
|
|
736
|
+
const status = integer(source.status);
|
|
737
|
+
const connectorOk = boolean(source.ok);
|
|
738
|
+
let projectedData;
|
|
739
|
+
if (status === 409 && data.error === 'confirmation_required') {
|
|
740
|
+
projectedData = projectConfirmation(data);
|
|
741
|
+
} else {
|
|
742
|
+
if (!(connectorOk && kind === 'recommendation')) assertPublicOutput(data);
|
|
743
|
+
if (!connectorOk) {
|
|
744
|
+
projectedData = projectError(data);
|
|
745
|
+
} else if (data.ok !== true) {
|
|
746
|
+
throw new CoachBookOutputError();
|
|
747
|
+
} else if (kind === 'catalog') {
|
|
748
|
+
projectedData = { ok: true, methods: records(data.methods).map(projectCatalogMethod) };
|
|
749
|
+
} else if (kind === 'describe') {
|
|
750
|
+
projectedData = { ok: true, method: projectDescribedMethod(data.method) };
|
|
751
|
+
} else if (kind === 'recommendation') {
|
|
752
|
+
projectedData = projectRecommendation(data);
|
|
753
|
+
} else {
|
|
754
|
+
const run = record(data.run);
|
|
755
|
+
projectedData = { ok: true, run: run.method ? projectGenericRun(run) : projectNicheRun(run) };
|
|
756
|
+
}
|
|
757
|
+
assertPublicOutput(projectedData);
|
|
758
|
+
}
|
|
759
|
+
return { ok: connectorOk, status, data: projectedData };
|
|
760
|
+
}
|