@thinkhive/sdk 2.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +274 -0
- package/README.md +7 -22
- package/dist/api/calibration.d.ts +168 -0
- package/dist/api/calibration.js +176 -0
- package/dist/api/claims.d.ts +262 -0
- package/dist/api/claims.js +262 -0
- package/dist/api/runs.d.ts +200 -0
- package/dist/api/runs.js +262 -0
- package/dist/core/client.d.ts +29 -0
- package/dist/core/client.js +89 -0
- package/dist/core/config.d.ts +38 -0
- package/dist/core/config.js +76 -0
- package/dist/core/types.d.ts +354 -0
- package/dist/core/types.js +8 -0
- package/dist/index.d.ts +222 -512
- package/dist/index.js +169 -394
- package/dist/instrumentation/langchain.d.ts +194 -0
- package/dist/instrumentation/langchain.js +429 -0
- package/dist/instrumentation/openai.d.ts +141 -0
- package/dist/instrumentation/openai.js +279 -0
- package/dist/integrations/customer-context.d.ts +203 -0
- package/dist/integrations/customer-context.js +274 -0
- package/dist/integrations/ticket-linking.d.ts +217 -0
- package/dist/integrations/ticket-linking.js +259 -0
- package/package.json +61 -9
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ThinkHive SDK v3.0 - Customer Context
|
|
4
|
+
*
|
|
5
|
+
* Time-series customer metrics snapshots
|
|
6
|
+
* Captures ARR, health score, segment AS OF the run time (not current values)
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.customerContext = void 0;
|
|
10
|
+
exports.toContextSnapshot = toContextSnapshot;
|
|
11
|
+
exports.captureCustomerContext = captureCustomerContext;
|
|
12
|
+
exports.getContextAsOf = getContextAsOf;
|
|
13
|
+
exports.calculateArrChange = calculateArrChange;
|
|
14
|
+
exports.calculateHealthTrend = calculateHealthTrend;
|
|
15
|
+
const client_1 = require("../core/client");
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// CUSTOMER CONTEXT API
|
|
18
|
+
// ============================================================================
|
|
19
|
+
/**
|
|
20
|
+
* Customer context API client for time-series metrics
|
|
21
|
+
*/
|
|
22
|
+
exports.customerContext = {
|
|
23
|
+
/**
|
|
24
|
+
* Create a customer account
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const account = await customerContext.createAccount({
|
|
29
|
+
* name: 'Acme Corp',
|
|
30
|
+
* externalId: 'sf_001234',
|
|
31
|
+
* externalSource: 'salesforce',
|
|
32
|
+
* segment: 'enterprise',
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
async createAccount(input) {
|
|
37
|
+
return (0, client_1.apiRequestWithData)('/customers', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
body: input,
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
/**
|
|
43
|
+
* Get a customer account by ID
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const account = await customerContext.getAccount('cust_abc123');
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
async getAccount(customerId) {
|
|
51
|
+
return (0, client_1.apiRequestWithData)(`/customers/${customerId}`);
|
|
52
|
+
},
|
|
53
|
+
/**
|
|
54
|
+
* Get a customer account by external ID
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const account = await customerContext.getAccountByExternalId(
|
|
59
|
+
* 'sf_001234',
|
|
60
|
+
* 'salesforce'
|
|
61
|
+
* );
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
async getAccountByExternalId(externalId, source) {
|
|
65
|
+
try {
|
|
66
|
+
return await (0, client_1.apiRequestWithData)(`/customers/external/${source}/${externalId}`);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error?.statusCode === 404) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
/**
|
|
76
|
+
* Capture a metrics snapshot for a customer
|
|
77
|
+
* This creates a point-in-time record of the customer's metrics
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // Capture current metrics
|
|
82
|
+
* const snapshot = await customerContext.captureSnapshot('cust_abc123', {
|
|
83
|
+
* arr: 120000,
|
|
84
|
+
* healthScore: 85,
|
|
85
|
+
* nps: 45,
|
|
86
|
+
* segment: 'enterprise',
|
|
87
|
+
* });
|
|
88
|
+
*
|
|
89
|
+
* // Use this snapshot in a run
|
|
90
|
+
* const run = await runs.create({
|
|
91
|
+
* agentId: 'agent_123',
|
|
92
|
+
* customerContext: {
|
|
93
|
+
* customerId: 'cust_abc123',
|
|
94
|
+
* arr: snapshot.arr,
|
|
95
|
+
* healthScore: snapshot.healthScore,
|
|
96
|
+
* capturedAt: snapshot.capturedAt,
|
|
97
|
+
* },
|
|
98
|
+
* // ...
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
async captureSnapshot(customerId, metrics) {
|
|
103
|
+
return (0, client_1.apiRequestWithData)(`/customers/${customerId}/snapshots`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
body: {
|
|
106
|
+
...metrics,
|
|
107
|
+
capturedAt: new Date().toISOString(),
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Get metrics snapshots for a customer (time-series)
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* // Get last 30 days of snapshots
|
|
117
|
+
* const snapshots = await customerContext.getSnapshots('cust_abc123', {
|
|
118
|
+
* from: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(),
|
|
119
|
+
* });
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
async getSnapshots(customerId, options = {}) {
|
|
123
|
+
const params = new URLSearchParams();
|
|
124
|
+
if (options.from)
|
|
125
|
+
params.set('from', options.from);
|
|
126
|
+
if (options.to)
|
|
127
|
+
params.set('to', options.to);
|
|
128
|
+
if (options.limit)
|
|
129
|
+
params.set('limit', String(options.limit));
|
|
130
|
+
return (0, client_1.apiRequestWithData)(`/customers/${customerId}/snapshots?${params.toString()}`);
|
|
131
|
+
},
|
|
132
|
+
/**
|
|
133
|
+
* Get the most recent snapshot for a customer
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const latest = await customerContext.getLatestSnapshot('cust_abc123');
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
async getLatestSnapshot(customerId) {
|
|
141
|
+
try {
|
|
142
|
+
return await (0, client_1.apiRequestWithData)(`/customers/${customerId}/snapshots/latest`);
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (error?.statusCode === 404) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
/**
|
|
152
|
+
* Get snapshot closest to a specific timestamp
|
|
153
|
+
* Useful for retroactive analysis
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* // Get metrics as of a specific date
|
|
158
|
+
* const snapshot = await customerContext.getSnapshotAsOf(
|
|
159
|
+
* 'cust_abc123',
|
|
160
|
+
* '2024-01-15T10:00:00Z'
|
|
161
|
+
* );
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
async getSnapshotAsOf(customerId, timestamp) {
|
|
165
|
+
const ts = new Date(timestamp).toISOString();
|
|
166
|
+
try {
|
|
167
|
+
return await (0, client_1.apiRequestWithData)(`/customers/${customerId}/snapshots/as-of?timestamp=${encodeURIComponent(ts)}`);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
if (error?.statusCode === 404) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// HELPER FUNCTIONS
|
|
179
|
+
// ============================================================================
|
|
180
|
+
/**
|
|
181
|
+
* Create a CustomerContextSnapshot from a metrics snapshot
|
|
182
|
+
*/
|
|
183
|
+
function toContextSnapshot(snapshot) {
|
|
184
|
+
return {
|
|
185
|
+
customerId: snapshot.customerAccountId,
|
|
186
|
+
arr: snapshot.arr,
|
|
187
|
+
healthScore: snapshot.healthScore,
|
|
188
|
+
segment: snapshot.segment,
|
|
189
|
+
capturedAt: snapshot.capturedAt,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Capture metrics and create context snapshot in one call
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* const context = await captureCustomerContext('cust_abc123', {
|
|
198
|
+
* arr: 100000,
|
|
199
|
+
* healthScore: 90,
|
|
200
|
+
* segment: 'enterprise',
|
|
201
|
+
* });
|
|
202
|
+
*
|
|
203
|
+
* // Use in run
|
|
204
|
+
* const run = await runs.create({
|
|
205
|
+
* agentId: 'agent_123',
|
|
206
|
+
* customerContext: context,
|
|
207
|
+
* // ...
|
|
208
|
+
* });
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
async function captureCustomerContext(customerId, metrics) {
|
|
212
|
+
const snapshot = await exports.customerContext.captureSnapshot(customerId, metrics);
|
|
213
|
+
return toContextSnapshot(snapshot);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Get customer context as of a specific time
|
|
217
|
+
*/
|
|
218
|
+
async function getContextAsOf(customerId, timestamp) {
|
|
219
|
+
const snapshot = await exports.customerContext.getSnapshotAsOf(customerId, timestamp);
|
|
220
|
+
return snapshot ? toContextSnapshot(snapshot) : null;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Calculate ARR change between two snapshots
|
|
224
|
+
*/
|
|
225
|
+
function calculateArrChange(older, newer) {
|
|
226
|
+
const oldArr = older.arr || 0;
|
|
227
|
+
const newArr = newer.arr || 0;
|
|
228
|
+
const absolute = newArr - oldArr;
|
|
229
|
+
const percentage = oldArr > 0 ? (absolute / oldArr) * 100 : 0;
|
|
230
|
+
return {
|
|
231
|
+
absolute,
|
|
232
|
+
percentage,
|
|
233
|
+
direction: absolute > 0 ? 'increase' : absolute < 0 ? 'decrease' : 'stable',
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Calculate health score trend
|
|
238
|
+
*/
|
|
239
|
+
function calculateHealthTrend(snapshots) {
|
|
240
|
+
const scores = snapshots
|
|
241
|
+
.filter((s) => s.healthScore !== undefined && s.healthScore !== null)
|
|
242
|
+
.map((s) => s.healthScore);
|
|
243
|
+
if (scores.length === 0) {
|
|
244
|
+
return {
|
|
245
|
+
currentScore: null,
|
|
246
|
+
avgScore: null,
|
|
247
|
+
trend: 'stable',
|
|
248
|
+
volatility: 'low',
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
const currentScore = scores[0];
|
|
252
|
+
const avgScore = scores.reduce((a, b) => a + b, 0) / scores.length;
|
|
253
|
+
// Calculate trend (simple linear regression direction)
|
|
254
|
+
let trend = 'stable';
|
|
255
|
+
if (scores.length >= 2) {
|
|
256
|
+
const recentAvg = scores.slice(0, Math.ceil(scores.length / 2)).reduce((a, b) => a + b, 0) / Math.ceil(scores.length / 2);
|
|
257
|
+
const olderAvg = scores.slice(Math.ceil(scores.length / 2)).reduce((a, b) => a + b, 0) / Math.floor(scores.length / 2);
|
|
258
|
+
if (recentAvg - olderAvg > 5)
|
|
259
|
+
trend = 'improving';
|
|
260
|
+
else if (olderAvg - recentAvg > 5)
|
|
261
|
+
trend = 'declining';
|
|
262
|
+
}
|
|
263
|
+
// Calculate volatility (standard deviation)
|
|
264
|
+
const variance = scores.reduce((acc, s) => acc + Math.pow(s - avgScore, 2), 0) / scores.length;
|
|
265
|
+
const stdDev = Math.sqrt(variance);
|
|
266
|
+
const volatility = stdDev < 5 ? 'low' : stdDev < 15 ? 'medium' : 'high';
|
|
267
|
+
return {
|
|
268
|
+
currentScore,
|
|
269
|
+
avgScore,
|
|
270
|
+
trend,
|
|
271
|
+
volatility,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItY29udGV4dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9pbnRlZ3JhdGlvbnMvY3VzdG9tZXItY29udGV4dC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7O0dBS0c7OztBQTJQSCw4Q0FVQztBQXFCRCx3REFVQztBQUtELHdDQU1DO0FBS0QsZ0RBa0JDO0FBS0Qsb0RBNENDO0FBclhELDJDQUFnRTtBQWtDaEUsK0VBQStFO0FBQy9FLHVCQUF1QjtBQUN2QiwrRUFBK0U7QUFFL0U7O0dBRUc7QUFDVSxRQUFBLGVBQWUsR0FBRztJQUM3Qjs7Ozs7Ozs7Ozs7O09BWUc7SUFDSCxLQUFLLENBQUMsYUFBYSxDQUFDLEtBUW5CO1FBQ0MsT0FBTyxJQUFBLDJCQUFrQixFQUFrQixZQUFZLEVBQUU7WUFDdkQsTUFBTSxFQUFFLE1BQU07WUFDZCxJQUFJLEVBQUUsS0FBSztTQUNaLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsS0FBSyxDQUFDLFVBQVUsQ0FBQyxVQUFrQjtRQUNqQyxPQUFPLElBQUEsMkJBQWtCLEVBQWtCLGNBQWMsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUN6RSxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7T0FVRztJQUNILEtBQUssQ0FBQyxzQkFBc0IsQ0FDMUIsVUFBa0IsRUFDbEIsTUFBeUM7UUFFekMsSUFBSSxDQUFDO1lBQ0gsT0FBTyxNQUFNLElBQUEsMkJBQWtCLEVBQzdCLHVCQUF1QixNQUFNLElBQUksVUFBVSxFQUFFLENBQzlDLENBQUM7UUFDSixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUssS0FBYSxFQUFFLFVBQVUsS0FBSyxHQUFHLEVBQUUsQ0FBQztnQkFDdkMsT0FBTyxJQUFJLENBQUM7WUFDZCxDQUFDO1lBQ0QsTUFBTSxLQUFLLENBQUM7UUFDZCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7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RSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDdkgsSUFBSSxTQUFTLEdBQUcsUUFBUSxHQUFHLENBQUM7WUFBRSxLQUFLLEdBQUcsV0FBVyxDQUFDO2FBQzdDLElBQUksUUFBUSxHQUFHLFNBQVMsR0FBRyxDQUFDO1lBQUUsS0FBSyxHQUFHLFdBQVcsQ0FBQztJQUN6RCxDQUFDO0lBRUQsNENBQTRDO0lBQzVDLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsUUFBUSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDL0YsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNuQyxNQUFNLFVBQVUsR0FBRyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU0sR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBRXhFLE9BQU87UUFDTCxZQUFZO1FBQ1osUUFBUTtRQUNSLEtBQUs7UUFDTCxVQUFVO0tBQ1gsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFRoaW5rSGl2ZSBTREsgdjMuMCAtIEN1c3RvbWVyIENvbnRleHRcbiAqXG4gKiBUaW1lLXNlcmllcyBjdXN0b21lciBtZXRyaWNzIHNuYXBzaG90c1xuICogQ2FwdHVyZXMgQVJSLCBoZWFsdGggc2NvcmUsIHNlZ21lbnQgQVMgT0YgdGhlIHJ1biB0aW1lIChub3QgY3VycmVudCB2YWx1ZXMpXG4gKi9cblxuaW1wb3J0IHsgYXBpUmVxdWVzdCwgYXBpUmVxdWVzdFdpdGhEYXRhIH0gZnJvbSAnLi4vY29yZS9jbGllbnQnO1xuaW1wb3J0IHR5cGUgeyBDdXN0b21lckNvbnRleHRTbmFwc2hvdCB9IGZyb20gJy4uL2NvcmUvdHlwZXMnO1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBDVVNUT01FUiBBQ0NPVU5UIFRZUEVTXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ3VzdG9tZXJBY2NvdW50IHtcbiAgaWQ6IHN0cmluZztcbiAgY29tcGFueUlkOiBzdHJpbmc7XG4gIGV4dGVybmFsSWQ/OiBzdHJpbmc7XG4gIGV4dGVybmFsU291cmNlPzogJ3NhbGVzZm9yY2UnIHwgJ2h1YnNwb3QnIHwgJ3plbmRlc2snIHwgJ2ludGVyY29tJyB8ICdjdXN0b20nO1xuICBuYW1lOiBzdHJpbmc7XG4gIGRvbWFpbj86IHN0cmluZztcbiAgc2VnbWVudD86IHN0cmluZztcbiAgaW5kdXN0cnk/OiBzdHJpbmc7XG4gIGVtcGxveWVlQ291bnQ/OiBudW1iZXI7XG4gIGNyZWF0ZWRBdDogc3RyaW5nO1xuICB1cGRhdGVkQXQ6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDdXN0b21lck1ldHJpY3NTbmFwc2hvdCB7XG4gIGlkOiBzdHJpbmc7XG4gIGN1c3RvbWVyQWNjb3VudElkOiBzdHJpbmc7XG4gIGFycj86IG51bWJlcjtcbiAgaGVhbHRoU2NvcmU/OiBudW1iZXI7XG4gIG5wcz86IG51bWJlcjtcbiAgc2VnbWVudD86IHN0cmluZztcbiAgY2h1cm5SaXNrPzogJ2xvdycgfCAnbWVkaXVtJyB8ICdoaWdoJztcbiAgY2FwdHVyZWRBdDogc3RyaW5nO1xuICBzb3VyY2U/OiBzdHJpbmc7XG4gIGNyZWF0ZWRBdDogc3RyaW5nO1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBDVVNUT01FUiBDT05URVhUIEFQSVxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIEN1c3RvbWVyIGNvbnRleHQgQVBJIGNsaWVudCBmb3IgdGltZS1zZXJpZXMgbWV0cmljc1xuICovXG5leHBvcnQgY29uc3QgY3VzdG9tZXJDb250ZXh0ID0ge1xuICAvKipcbiAgICogQ3JlYXRlIGEgY3VzdG9tZXIgYWNjb3VudFxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IGFjY291bnQgPSBhd2FpdCBjdXN0b21lckNvbnRleHQuY3JlYXRlQWNjb3VudCh7XG4gICAqICAgbmFtZTogJ0FjbWUgQ29ycCcsXG4gICAqICAgZXh0ZXJuYWxJZDogJ3NmXzAwMTIzNCcsXG4gICAqICAgZXh0ZXJuYWxTb3VyY2U6ICdzYWxlc2ZvcmNlJyxcbiAgICogICBzZWdtZW50OiAnZW50ZXJwcmlzZScsXG4gICAqIH0pO1xuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIGNyZWF0ZUFjY291bnQoaW5wdXQ6IHtcbiAgICBuYW1lOiBzdHJpbmc7XG4gICAgZXh0ZXJuYWxJZD86IHN0cmluZztcbiAgICBleHRlcm5hbFNvdXJjZT86IEN1c3RvbWVyQWNjb3VudFsnZXh0ZXJuYWxTb3VyY2UnXTtcbiAgICBkb21haW4/OiBzdHJpbmc7XG4gICAgc2VnbWVudD86IHN0cmluZztcbiAgICBpbmR1c3RyeT86IHN0cmluZztcbiAgICBlbXBsb3llZUNvdW50PzogbnVtYmVyO1xuICB9KTogUHJvbWlzZTxDdXN0b21lckFjY291bnQ+IHtcbiAgICByZXR1cm4gYXBpUmVxdWVzdFdpdGhEYXRhPEN1c3RvbWVyQWNjb3VudD4oJy9jdXN0b21lcnMnLCB7XG4gICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgIGJvZHk6IGlucHV0LFxuICAgIH0pO1xuICB9LFxuXG4gIC8qKlxuICAgKiBHZXQgYSBjdXN0b21lciBhY2NvdW50IGJ5IElEXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY29uc3QgYWNjb3VudCA9IGF3YWl0IGN1c3RvbWVyQ29udGV4dC5nZXRBY2NvdW50KCdjdXN0X2FiYzEyMycpO1xuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIGdldEFjY291bnQoY3VzdG9tZXJJZDogc3RyaW5nKTogUHJvbWlzZTxDdXN0b21lckFjY291bnQ+IHtcbiAgICByZXR1cm4gYXBpUmVxdWVzdFdpdGhEYXRhPEN1c3RvbWVyQWNjb3VudD4oYC9jdXN0b21lcnMvJHtjdXN0b21lcklkfWApO1xuICB9LFxuXG4gIC8qKlxuICAgKiBHZXQgYSBjdXN0b21lciBhY2NvdW50IGJ5IGV4dGVybmFsIElEXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY29uc3QgYWNjb3VudCA9IGF3YWl0IGN1c3RvbWVyQ29udGV4dC5nZXRBY2NvdW50QnlFeHRlcm5hbElkKFxuICAgKiAgICdzZl8wMDEyMzQnLFxuICAgKiAgICdzYWxlc2ZvcmNlJ1xuICAgKiApO1xuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIGdldEFjY291bnRCeUV4dGVybmFsSWQoXG4gICAgZXh0ZXJuYWxJZDogc3RyaW5nLFxuICAgIHNvdXJjZTogQ3VzdG9tZXJBY2NvdW50WydleHRlcm5hbFNvdXJjZSddXG4gICk6IFByb21pc2U8Q3VzdG9tZXJBY2NvdW50IHwgbnVsbD4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgYXBpUmVxdWVzdFdpdGhEYXRhPEN1c3RvbWVyQWNjb3VudD4oXG4gICAgICAgIGAvY3VzdG9tZXJzL2V4dGVybmFsLyR7c291cmNlfS8ke2V4dGVybmFsSWR9YFxuICAgICAgKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKChlcnJvciBhcyBhbnkpPy5zdGF0dXNDb2RlID09PSA0MDQpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH0sXG5cbiAgLyoqXG4gICAqIENhcHR1cmUgYSBtZXRyaWNzIHNuYXBzaG90IGZvciBhIGN1c3RvbWVyXG4gICAqIFRoaXMgY3JlYXRlcyBhIHBvaW50LWluLXRpbWUgcmVjb3JkIG9mIHRoZSBjdXN0b21lcidzIG1ldHJpY3NcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiAvLyBDYXB0dXJlIGN1cnJlbnQgbWV0cmljc1xuICAgKiBjb25zdCBzbmFwc2hvdCA9IGF3YWl0IGN1c3RvbWVyQ29udGV4dC5jYXB0dXJlU25hcHNob3QoJ2N1c3RfYWJjMTIzJywge1xuICAgKiAgIGFycjogMTIwMDAwLFxuICAgKiAgIGhlYWx0aFNjb3JlOiA4NSxcbiAgICogICBucHM6IDQ1LFxuICAgKiAgIHNlZ21lbnQ6ICdlbnRlcnByaXNlJyxcbiAgICogfSk7XG4gICAqXG4gICAqIC8vIFVzZSB0aGlzIHNuYXBzaG90IGluIGEgcnVuXG4gICAqIGNvbnN0IHJ1biA9IGF3YWl0IHJ1bnMuY3JlYXRlKHtcbiAgICogICBhZ2VudElkOiAnYWdlbnRfMTIzJyxcbiAgICogICBjdXN0b21lckNvbnRleHQ6IHtcbiAgICogICAgIGN1c3RvbWVySWQ6ICdjdXN0X2FiYzEyMycsXG4gICAqICAgICBhcnI6IHNuYXBzaG90LmFycixcbiAgICogICAgIGhlYWx0aFNjb3JlOiBzbmFwc2hvdC5oZWFsdGhTY29yZSxcbiAgICogICAgIGNhcHR1cmVkQXQ6IHNuYXBzaG90LmNhcHR1cmVkQXQsXG4gICAqICAgfSxcbiAgICogICAvLyAuLi5cbiAgICogfSk7XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgY2FwdHVyZVNuYXBzaG90KFxuICAgIGN1c3RvbWVySWQ6IHN0cmluZyxcbiAgICBtZXRyaWNzOiB7XG4gICAgICBhcnI/OiBudW1iZXI7XG4gICAgICBoZWFsdGhTY29yZT86IG51bWJlcjtcbiAgICAgIG5wcz86IG51bWJlcjtcbiAgICAgIHNlZ21lbnQ/OiBzdHJpbmc7XG4gICAgICBjaHVyblJpc2s/OiAnbG93JyB8ICdtZWRpdW0nIHwgJ2hpZ2gnO1xuICAgICAgc291cmNlPzogc3RyaW5nO1xuICAgIH1cbiAgKTogUHJvbWlzZTxDdXN0b21lck1ldHJpY3NTbmFwc2hvdD4ge1xuICAgIHJldHVybiBhcGlSZXF1ZXN0V2l0aERhdGE8Q3VzdG9tZXJNZXRyaWNzU25hcHNob3Q+KFxuICAgICAgYC9jdXN0b21lcnMvJHtjdXN0b21lcklkfS9zbmFwc2hvdHNgLFxuICAgICAge1xuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgICAgYm9keToge1xuICAgICAgICAgIC4uLm1ldHJpY3MsXG4gICAgICAgICAgY2FwdHVyZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICB9LFxuICAgICAgfVxuICAgICk7XG4gIH0sXG5cbiAgLyoqXG4gICAqIEdldCBtZXRyaWNzIHNuYXBzaG90cyBmb3IgYSBjdXN0b21lciAodGltZS1zZXJpZXMpXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogLy8gR2V0IGxhc3QgMzAgZGF5cyBvZiBzbmFwc2hvdHNcbiAgICogY29uc3Qgc25hcHNob3RzID0gYXdhaXQgY3VzdG9tZXJDb250ZXh0LmdldFNuYXBzaG90cygnY3VzdF9hYmMxMjMnLCB7XG4gICAqICAgZnJvbTogbmV3IERhdGUoRGF0ZS5ub3coKSAtIDMwICogMjQgKiA2MCAqIDYwICogMTAwMCkudG9JU09TdHJpbmcoKSxcbiAgICogfSk7XG4gICAqIGBgYFxuICAgKi9cbiAgYXN5bmMgZ2V0U25hcHNob3RzKFxuICAgIGN1c3RvbWVySWQ6IHN0cmluZyxcbiAgICBvcHRpb25zOiB7XG4gICAgICBmcm9tPzogc3RyaW5nO1xuICAgICAgdG8/OiBzdHJpbmc7XG4gICAgICBsaW1pdD86IG51bWJlcjtcbiAgICB9ID0ge31cbiAgKTogUHJvbWlzZTxDdXN0b21lck1ldHJpY3NTbmFwc2hvdFtdPiB7XG4gICAgY29uc3QgcGFyYW1zID0gbmV3IFVSTFNlYXJjaFBhcmFtcygpO1xuICAgIGlmIChvcHRpb25zLmZyb20pIHBhcmFtcy5zZXQoJ2Zyb20nLCBvcHRpb25zLmZyb20pO1xuICAgIGlmIChvcHRpb25zLnRvKSBwYXJhbXMuc2V0KCd0bycsIG9wdGlvbnMudG8pO1xuICAgIGlmIChvcHRpb25zLmxpbWl0KSBwYXJhbXMuc2V0KCdsaW1pdCcsIFN0cmluZyhvcHRpb25zLmxpbWl0KSk7XG5cbiAgICByZXR1cm4gYXBpUmVxdWVzdFdpdGhEYXRhPEN1c3RvbWVyTWV0cmljc1NuYXBzaG90W10+KFxuICAgICAgYC9jdXN0b21lcnMvJHtjdXN0b21lcklkfS9zbmFwc2hvdHM/JHtwYXJhbXMudG9TdHJpbmcoKX1gXG4gICAgKTtcbiAgfSxcblxuICAvKipcbiAgICogR2V0IHRoZSBtb3N0IHJlY2VudCBzbmFwc2hvdCBmb3IgYSBjdXN0b21lclxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IGxhdGVzdCA9IGF3YWl0IGN1c3RvbWVyQ29udGV4dC5nZXRMYXRlc3RTbmFwc2hvdCgnY3VzdF9hYmMxMjMnKTtcbiAgICogYGBgXG4gICAqL1xuICBhc3luYyBnZXRMYXRlc3RTbmFwc2hvdChcbiAgICBjdXN0b21lcklkOiBzdHJpbmdcbiAgKTogUHJvbWlzZTxDdXN0b21lck1ldHJpY3NTbmFwc2hvdCB8IG51bGw+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IGFwaVJlcXVlc3RXaXRoRGF0YTxDdXN0b21lck1ldHJpY3NTbmFwc2hvdD4oXG4gICAgICAgIGAvY3VzdG9tZXJzLyR7Y3VzdG9tZXJJZH0vc25hcHNob3RzL2xhdGVzdGBcbiAgICAgICk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmICgoZXJyb3IgYXMgYW55KT8uc3RhdHVzQ29kZSA9PT0gNDA0KSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9LFxuXG4gIC8qKlxuICAgKiBHZXQgc25hcHNob3QgY2xvc2VzdCB0byBhIHNwZWNpZmljIHRpbWVzdGFtcFxuICAgKiBVc2VmdWwgZm9yIHJldHJvYWN0aXZlIGFuYWx5c2lzXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogLy8gR2V0IG1ldHJpY3MgYXMgb2YgYSBzcGVjaWZpYyBkYXRlXG4gICAqIGNvbnN0IHNuYXBzaG90ID0gYXdhaXQgY3VzdG9tZXJDb250ZXh0LmdldFNuYXBzaG90QXNPZihcbiAgICogICAnY3VzdF9hYmMxMjMnLFxuICAgKiAgICcyMDI0LTAxLTE1VDEwOjAwOjAwWidcbiAgICogKTtcbiAgICogYGBgXG4gICAqL1xuICBhc3luYyBnZXRTbmFwc2hvdEFzT2YoXG4gICAgY3VzdG9tZXJJZDogc3RyaW5nLFxuICAgIHRpbWVzdGFtcDogc3RyaW5nIHwgRGF0ZVxuICApOiBQcm9taXNlPEN1c3RvbWVyTWV0cmljc1NuYXBzaG90IHwgbnVsbD4ge1xuICAgIGNvbnN0IHRzID0gbmV3IERhdGUodGltZXN0YW1wKS50b0lTT1N0cmluZygpO1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgYXBpUmVxdWVzdFdpdGhEYXRhPEN1c3RvbWVyTWV0cmljc1NuYXBzaG90PihcbiAgICAgICAgYC9jdXN0b21lcnMvJHtjdXN0b21lcklkfS9zbmFwc2hvdHMvYXMtb2Y/dGltZXN0YW1wPSR7ZW5jb2RlVVJJQ29tcG9uZW50KHRzKX1gXG4gICAgICApO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoKGVycm9yIGFzIGFueSk/LnN0YXR1c0NvZGUgPT09IDQwNCkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfSxcbn07XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIEhFTFBFUiBGVU5DVElPTlNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBDcmVhdGUgYSBDdXN0b21lckNvbnRleHRTbmFwc2hvdCBmcm9tIGEgbWV0cmljcyBzbmFwc2hvdFxuICovXG5leHBvcnQgZnVuY3Rpb24gdG9Db250ZXh0U25hcHNob3QoXG4gIHNuYXBzaG90OiBDdXN0b21lck1ldHJpY3NTbmFwc2hvdFxuKTogQ3VzdG9tZXJDb250ZXh0U25hcHNob3Qge1xuICByZXR1cm4ge1xuICAgIGN1c3RvbWVySWQ6IHNuYXBzaG90LmN1c3RvbWVyQWNjb3VudElkLFxuICAgIGFycjogc25hcHNob3QuYXJyLFxuICAgIGhlYWx0aFNjb3JlOiBzbmFwc2hvdC5oZWFsdGhTY29yZSxcbiAgICBzZWdtZW50OiBzbmFwc2hvdC5zZWdtZW50LFxuICAgIGNhcHR1cmVkQXQ6IHNuYXBzaG90LmNhcHR1cmVkQXQsXG4gIH07XG59XG5cbi8qKlxuICogQ2FwdHVyZSBtZXRyaWNzIGFuZCBjcmVhdGUgY29udGV4dCBzbmFwc2hvdCBpbiBvbmUgY2FsbFxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBjb25zdCBjb250ZXh0ID0gYXdhaXQgY2FwdHVyZUN1c3RvbWVyQ29udGV4dCgnY3VzdF9hYmMxMjMnLCB7XG4gKiAgIGFycjogMTAwMDAwLFxuICogICBoZWFsdGhTY29yZTogOTAsXG4gKiAgIHNlZ21lbnQ6ICdlbnRlcnByaXNlJyxcbiAqIH0pO1xuICpcbiAqIC8vIFVzZSBpbiBydW5cbiAqIGNvbnN0IHJ1biA9IGF3YWl0IHJ1bnMuY3JlYXRlKHtcbiAqICAgYWdlbnRJZDogJ2FnZW50XzEyMycsXG4gKiAgIGN1c3RvbWVyQ29udGV4dDogY29udGV4dCxcbiAqICAgLy8gLi4uXG4gKiB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FwdHVyZUN1c3RvbWVyQ29udGV4dChcbiAgY3VzdG9tZXJJZDogc3RyaW5nLFxuICBtZXRyaWNzOiB7XG4gICAgYXJyPzogbnVtYmVyO1xuICAgIGhlYWx0aFNjb3JlPzogbnVtYmVyO1xuICAgIHNlZ21lbnQ/OiBzdHJpbmc7XG4gIH1cbik6IFByb21pc2U8Q3VzdG9tZXJDb250ZXh0U25hcHNob3Q+IHtcbiAgY29uc3Qgc25hcHNob3QgPSBhd2FpdCBjdXN0b21lckNvbnRleHQuY2FwdHVyZVNuYXBzaG90KGN1c3RvbWVySWQsIG1ldHJpY3MpO1xuICByZXR1cm4gdG9Db250ZXh0U25hcHNob3Qoc25hcHNob3QpO1xufVxuXG4vKipcbiAqIEdldCBjdXN0b21lciBjb250ZXh0IGFzIG9mIGEgc3BlY2lmaWMgdGltZVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0Q29udGV4dEFzT2YoXG4gIGN1c3RvbWVySWQ6IHN0cmluZyxcbiAgdGltZXN0YW1wOiBzdHJpbmcgfCBEYXRlXG4pOiBQcm9taXNlPEN1c3RvbWVyQ29udGV4dFNuYXBzaG90IHwgbnVsbD4ge1xuICBjb25zdCBzbmFwc2hvdCA9IGF3YWl0IGN1c3RvbWVyQ29udGV4dC5nZXRTbmFwc2hvdEFzT2YoY3VzdG9tZXJJZCwgdGltZXN0YW1wKTtcbiAgcmV0dXJuIHNuYXBzaG90ID8gdG9Db250ZXh0U25hcHNob3Qoc25hcHNob3QpIDogbnVsbDtcbn1cblxuLyoqXG4gKiBDYWxjdWxhdGUgQVJSIGNoYW5nZSBiZXR3ZWVuIHR3byBzbmFwc2hvdHNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZUFyckNoYW5nZShcbiAgb2xkZXI6IEN1c3RvbWVyTWV0cmljc1NuYXBzaG90LFxuICBuZXdlcjogQ3VzdG9tZXJNZXRyaWNzU25hcHNob3Rcbik6IHtcbiAgYWJzb2x1dGU6IG51bWJlcjtcbiAgcGVyY2VudGFnZTogbnVtYmVyO1xuICBkaXJlY3Rpb246ICdpbmNyZWFzZScgfCAnZGVjcmVhc2UnIHwgJ3N0YWJsZSc7XG59IHtcbiAgY29uc3Qgb2xkQXJyID0gb2xkZXIuYXJyIHx8IDA7XG4gIGNvbnN0IG5ld0FyciA9IG5ld2VyLmFyciB8fCAwO1xuICBjb25zdCBhYnNvbHV0ZSA9IG5ld0FyciAtIG9sZEFycjtcbiAgY29uc3QgcGVyY2VudGFnZSA9IG9sZEFyciA+IDAgPyAoYWJzb2x1dGUgLyBvbGRBcnIpICogMTAwIDogMDtcblxuICByZXR1cm4ge1xuICAgIGFic29sdXRlLFxuICAgIHBlcmNlbnRhZ2UsXG4gICAgZGlyZWN0aW9uOiBhYnNvbHV0ZSA+IDAgPyAnaW5jcmVhc2UnIDogYWJzb2x1dGUgPCAwID8gJ2RlY3JlYXNlJyA6ICdzdGFibGUnLFxuICB9O1xufVxuXG4vKipcbiAqIENhbGN1bGF0ZSBoZWFsdGggc2NvcmUgdHJlbmRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZUhlYWx0aFRyZW5kKFxuICBzbmFwc2hvdHM6IEN1c3RvbWVyTWV0cmljc1NuYXBzaG90W11cbik6IHtcbiAgY3VycmVudFNjb3JlOiBudW1iZXIgfCBudWxsO1xuICBhdmdTY29yZTogbnVtYmVyIHwgbnVsbDtcbiAgdHJlbmQ6ICdpbXByb3ZpbmcnIHwgJ2RlY2xpbmluZycgfCAnc3RhYmxlJztcbiAgdm9sYXRpbGl0eTogJ2xvdycgfCAnbWVkaXVtJyB8ICdoaWdoJztcbn0ge1xuICBjb25zdCBzY29yZXMgPSBzbmFwc2hvdHNcbiAgICAuZmlsdGVyKChzKSA9PiBzLmhlYWx0aFNjb3JlICE9PSB1bmRlZmluZWQgJiYgcy5oZWFsdGhTY29yZSAhPT0gbnVsbClcbiAgICAubWFwKChzKSA9PiBzLmhlYWx0aFNjb3JlISk7XG5cbiAgaWYgKHNjb3Jlcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4ge1xuICAgICAgY3VycmVudFNjb3JlOiBudWxsLFxuICAgICAgYXZnU2NvcmU6IG51bGwsXG4gICAgICB0cmVuZDogJ3N0YWJsZScsXG4gICAgICB2b2xhdGlsaXR5OiAnbG93JyxcbiAgICB9O1xuICB9XG5cbiAgY29uc3QgY3VycmVudFNjb3JlID0gc2NvcmVzWzBdO1xuICBjb25zdCBhdmdTY29yZSA9IHNjb3Jlcy5yZWR1Y2UoKGEsIGIpID0+IGEgKyBiLCAwKSAvIHNjb3Jlcy5sZW5ndGg7XG5cbiAgLy8gQ2FsY3VsYXRlIHRyZW5kIChzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gZGlyZWN0aW9uKVxuICBsZXQgdHJlbmQ6ICdpbXByb3ZpbmcnIHwgJ2RlY2xpbmluZycgfCAnc3RhYmxlJyA9ICdzdGFibGUnO1xuICBpZiAoc2NvcmVzLmxlbmd0aCA+PSAyKSB7XG4gICAgY29uc3QgcmVjZW50QXZnID0gc2NvcmVzLnNsaWNlKDAsIE1hdGguY2VpbChzY29yZXMubGVuZ3RoIC8gMikpLnJlZHVjZSgoYSwgYikgPT4gYSArIGIsIDApIC8gTWF0aC5jZWlsKHNjb3Jlcy5sZW5ndGggLyAyKTtcbiAgICBjb25zdCBvbGRlckF2ZyA9IHNjb3Jlcy5zbGljZShNYXRoLmNlaWwoc2NvcmVzLmxlbmd0aCAvIDIpKS5yZWR1Y2UoKGEsIGIpID0+IGEgKyBiLCAwKSAvIE1hdGguZmxvb3Ioc2NvcmVzLmxlbmd0aCAvIDIpO1xuICAgIGlmIChyZWNlbnRBdmcgLSBvbGRlckF2ZyA+IDUpIHRyZW5kID0gJ2ltcHJvdmluZyc7XG4gICAgZWxzZSBpZiAob2xkZXJBdmcgLSByZWNlbnRBdmcgPiA1KSB0cmVuZCA9ICdkZWNsaW5pbmcnO1xuICB9XG5cbiAgLy8gQ2FsY3VsYXRlIHZvbGF0aWxpdHkgKHN0YW5kYXJkIGRldmlhdGlvbilcbiAgY29uc3QgdmFyaWFuY2UgPSBzY29yZXMucmVkdWNlKChhY2MsIHMpID0+IGFjYyArIE1hdGgucG93KHMgLSBhdmdTY29yZSwgMiksIDApIC8gc2NvcmVzLmxlbmd0aDtcbiAgY29uc3Qgc3RkRGV2ID0gTWF0aC5zcXJ0KHZhcmlhbmNlKTtcbiAgY29uc3Qgdm9sYXRpbGl0eSA9IHN0ZERldiA8IDUgPyAnbG93JyA6IHN0ZERldiA8IDE1ID8gJ21lZGl1bScgOiAnaGlnaCc7XG5cbiAgcmV0dXJuIHtcbiAgICBjdXJyZW50U2NvcmUsXG4gICAgYXZnU2NvcmUsXG4gICAgdHJlbmQsXG4gICAgdm9sYXRpbGl0eSxcbiAgfTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThinkHive SDK v3.0 - Ticket Linking
|
|
3
|
+
*
|
|
4
|
+
* Deterministic linking between runs and support tickets
|
|
5
|
+
* 7 link methods with explicit confidence scores
|
|
6
|
+
*/
|
|
7
|
+
import type { LinkMethod } from '../core/types';
|
|
8
|
+
/**
|
|
9
|
+
* Generate a Zendesk marker to embed in agent responses
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const runId = 'run_abc123';
|
|
14
|
+
* const marker = generateZendeskMarker(runId);
|
|
15
|
+
* // Returns: '[THID:run_abc123]'
|
|
16
|
+
*
|
|
17
|
+
* // Append to your agent response:
|
|
18
|
+
* const response = `I've found your order. ${marker}`;
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateZendeskMarker(runId: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Parse a Zendesk marker from text
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const text = 'Thank you for contacting us. [THID:run_abc123]';
|
|
28
|
+
* const runId = parseZendeskMarker(text);
|
|
29
|
+
* // Returns: 'run_abc123'
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseZendeskMarker(text: string): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Check if text contains a Zendesk marker
|
|
35
|
+
*/
|
|
36
|
+
export declare function hasZendeskMarker(text: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Remove Zendesk marker from text (for clean display)
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const text = 'Thank you! [THID:run_abc123]';
|
|
43
|
+
* const clean = removeZendeskMarker(text);
|
|
44
|
+
* // Returns: 'Thank you!'
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function removeZendeskMarker(text: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Confidence scores for each link method
|
|
50
|
+
* Based on deterministic linking principles from v3 spec
|
|
51
|
+
*/
|
|
52
|
+
export declare const LINK_METHOD_CONFIDENCE: Record<LinkMethod, number>;
|
|
53
|
+
/**
|
|
54
|
+
* Link evidence structure
|
|
55
|
+
*/
|
|
56
|
+
export interface LinkEvidence {
|
|
57
|
+
method: LinkMethod;
|
|
58
|
+
confidence: number;
|
|
59
|
+
timestamp: string;
|
|
60
|
+
details: {
|
|
61
|
+
runId?: string;
|
|
62
|
+
ticketId?: string;
|
|
63
|
+
externalTicketId?: string;
|
|
64
|
+
platform?: string;
|
|
65
|
+
sessionId?: string;
|
|
66
|
+
email?: string;
|
|
67
|
+
timeWindowMinutes?: number;
|
|
68
|
+
customFieldName?: string;
|
|
69
|
+
customFieldValue?: string;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create link input
|
|
74
|
+
*/
|
|
75
|
+
export interface CreateLinkInput {
|
|
76
|
+
runId: string;
|
|
77
|
+
ticketId?: string;
|
|
78
|
+
externalTicketId?: string;
|
|
79
|
+
platform?: 'zendesk' | 'intercom' | 'salesforce' | 'freshdesk';
|
|
80
|
+
method: LinkMethod;
|
|
81
|
+
evidence?: Partial<LinkEvidence['details']>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Link response
|
|
85
|
+
*/
|
|
86
|
+
export interface LinkResponse {
|
|
87
|
+
id: string;
|
|
88
|
+
runId: string;
|
|
89
|
+
ticketId?: string;
|
|
90
|
+
externalTicketId?: string;
|
|
91
|
+
platform?: string;
|
|
92
|
+
method: LinkMethod;
|
|
93
|
+
confidence: number;
|
|
94
|
+
evidence: LinkEvidence;
|
|
95
|
+
createdAt: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Linking API client
|
|
99
|
+
*/
|
|
100
|
+
export declare const linking: {
|
|
101
|
+
/**
|
|
102
|
+
* Create a link between a run and a ticket
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* // SDK explicit linking (highest confidence)
|
|
107
|
+
* const link = await linking.create({
|
|
108
|
+
* runId: 'run_abc123',
|
|
109
|
+
* ticketId: 'ticket_xyz',
|
|
110
|
+
* method: 'sdk_explicit',
|
|
111
|
+
* });
|
|
112
|
+
*
|
|
113
|
+
* // Zendesk marker linking
|
|
114
|
+
* const link = await linking.create({
|
|
115
|
+
* runId: 'run_abc123',
|
|
116
|
+
* externalTicketId: '12345',
|
|
117
|
+
* platform: 'zendesk',
|
|
118
|
+
* method: 'zendesk_marker',
|
|
119
|
+
* });
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
create(input: CreateLinkInput): Promise<LinkResponse>;
|
|
123
|
+
/**
|
|
124
|
+
* Get links for a run
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const links = await linking.getForRun('run_abc123');
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
getForRun(runId: string): Promise<LinkResponse[]>;
|
|
132
|
+
/**
|
|
133
|
+
* Get links for a ticket
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const links = await linking.getForTicket('ticket_xyz');
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
getForTicket(ticketId: string): Promise<LinkResponse[]>;
|
|
141
|
+
/**
|
|
142
|
+
* Verify a link (confirm or reject)
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* await linking.verify('link_abc123', {
|
|
147
|
+
* verified: true,
|
|
148
|
+
* notes: 'Confirmed by support agent',
|
|
149
|
+
* });
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
verify(linkId: string, options: {
|
|
153
|
+
verified: boolean;
|
|
154
|
+
notes?: string;
|
|
155
|
+
}): Promise<{
|
|
156
|
+
linkId: string;
|
|
157
|
+
verified: boolean;
|
|
158
|
+
message: string;
|
|
159
|
+
}>;
|
|
160
|
+
/**
|
|
161
|
+
* Delete a link
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* await linking.delete('link_abc123');
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
delete(linkId: string): Promise<void>;
|
|
169
|
+
/**
|
|
170
|
+
* Auto-link runs to tickets based on available evidence
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* const results = await linking.autoLink('run_abc123');
|
|
175
|
+
* for (const link of results.created) {
|
|
176
|
+
* console.log(`Linked to ${link.ticketId} via ${link.method}`);
|
|
177
|
+
* }
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
autoLink(runId: string): Promise<{
|
|
181
|
+
runId: string;
|
|
182
|
+
created: LinkResponse[];
|
|
183
|
+
candidates: Array<{
|
|
184
|
+
ticketId: string;
|
|
185
|
+
method: LinkMethod;
|
|
186
|
+
confidence: number;
|
|
187
|
+
reason: string;
|
|
188
|
+
}>;
|
|
189
|
+
}>;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Create SDK explicit link (convenience function)
|
|
193
|
+
*/
|
|
194
|
+
export declare function linkRunToTicket(runId: string, ticketId: string): Promise<LinkResponse>;
|
|
195
|
+
/**
|
|
196
|
+
* Create Zendesk link via marker
|
|
197
|
+
*/
|
|
198
|
+
export declare function linkRunToZendeskTicket(runId: string, zendeskTicketId: string): Promise<LinkResponse>;
|
|
199
|
+
/**
|
|
200
|
+
* Get the best link method for a given scenario
|
|
201
|
+
*/
|
|
202
|
+
export declare function getBestLinkMethod(available: {
|
|
203
|
+
hasTicketId: boolean;
|
|
204
|
+
hasMarker: boolean;
|
|
205
|
+
hasCustomField: boolean;
|
|
206
|
+
hasMiddlewareStamp: boolean;
|
|
207
|
+
hasSessionId: boolean;
|
|
208
|
+
hasEmail: boolean;
|
|
209
|
+
}): LinkMethod | null;
|
|
210
|
+
/**
|
|
211
|
+
* Format link confidence for display
|
|
212
|
+
*/
|
|
213
|
+
export declare function formatLinkConfidence(confidence: number): string;
|
|
214
|
+
/**
|
|
215
|
+
* Get confidence level label
|
|
216
|
+
*/
|
|
217
|
+
export declare function getConfidenceLevel(confidence: number): 'definitive' | 'high' | 'medium' | 'low';
|