@buoy-gg/agent-core 7.0.33
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/LICENSE +58 -0
- package/README.md +42 -0
- package/lib/commonjs/blocks/receipts.js +702 -0
- package/lib/commonjs/blocks/receipts.js.map +1 -0
- package/lib/commonjs/blocks/types.js +119 -0
- package/lib/commonjs/blocks/types.js.map +1 -0
- package/lib/commonjs/blocks/uiTool.js +994 -0
- package/lib/commonjs/blocks/uiTool.js.map +1 -0
- package/lib/commonjs/catalog/catalog.g.js +4044 -0
- package/lib/commonjs/catalog/catalog.g.js.map +1 -0
- package/lib/commonjs/catalog/catalog.source.json +4956 -0
- package/lib/commonjs/catalog/snapshotReads.js +256 -0
- package/lib/commonjs/catalog/snapshotReads.js.map +1 -0
- package/lib/commonjs/catalog/toProviderTools.js +107 -0
- package/lib/commonjs/catalog/toProviderTools.js.map +1 -0
- package/lib/commonjs/catalog/validateParams.js +129 -0
- package/lib/commonjs/catalog/validateParams.js.map +1 -0
- package/lib/commonjs/context/buildContextPack.js +141 -0
- package/lib/commonjs/context/buildContextPack.js.map +1 -0
- package/lib/commonjs/effects/ledger.js +229 -0
- package/lib/commonjs/effects/ledger.js.map +1 -0
- package/lib/commonjs/engine/effectFor.js +95 -0
- package/lib/commonjs/engine/effectFor.js.map +1 -0
- package/lib/commonjs/engine/runAgentTurn.js +667 -0
- package/lib/commonjs/engine/runAgentTurn.js.map +1 -0
- package/lib/commonjs/engine/systemPrompt.js +86 -0
- package/lib/commonjs/engine/systemPrompt.js.map +1 -0
- package/lib/commonjs/index.js +288 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/policy/labels.js +303 -0
- package/lib/commonjs/policy/labels.js.map +1 -0
- package/lib/commonjs/policy/policy.js +143 -0
- package/lib/commonjs/policy/policy.js.map +1 -0
- package/lib/commonjs/policy/redact.js +135 -0
- package/lib/commonjs/policy/redact.js.map +1 -0
- package/lib/commonjs/providers/anthropic.js +272 -0
- package/lib/commonjs/providers/anthropic.js.map +1 -0
- package/lib/commonjs/providers/openai.js +193 -0
- package/lib/commonjs/providers/openai.js.map +1 -0
- package/lib/commonjs/providers/sse.js +104 -0
- package/lib/commonjs/providers/sse.js.map +1 -0
- package/lib/commonjs/providers/types.js +6 -0
- package/lib/commonjs/providers/types.js.map +1 -0
- package/lib/commonjs/session.js +173 -0
- package/lib/commonjs/session.js.map +1 -0
- package/lib/commonjs/types.js +2 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/blocks/receipts.js +696 -0
- package/lib/module/blocks/receipts.js.map +1 -0
- package/lib/module/blocks/types.js +113 -0
- package/lib/module/blocks/types.js.map +1 -0
- package/lib/module/blocks/uiTool.js +987 -0
- package/lib/module/blocks/uiTool.js.map +1 -0
- package/lib/module/catalog/catalog.g.js +4040 -0
- package/lib/module/catalog/catalog.g.js.map +1 -0
- package/lib/module/catalog/catalog.source.json +4956 -0
- package/lib/module/catalog/snapshotReads.js +252 -0
- package/lib/module/catalog/snapshotReads.js.map +1 -0
- package/lib/module/catalog/toProviderTools.js +101 -0
- package/lib/module/catalog/toProviderTools.js.map +1 -0
- package/lib/module/catalog/validateParams.js +124 -0
- package/lib/module/catalog/validateParams.js.map +1 -0
- package/lib/module/context/buildContextPack.js +138 -0
- package/lib/module/context/buildContextPack.js.map +1 -0
- package/lib/module/effects/ledger.js +223 -0
- package/lib/module/effects/ledger.js.map +1 -0
- package/lib/module/engine/effectFor.js +91 -0
- package/lib/module/engine/effectFor.js.map +1 -0
- package/lib/module/engine/runAgentTurn.js +663 -0
- package/lib/module/engine/runAgentTurn.js.map +1 -0
- package/lib/module/engine/systemPrompt.js +82 -0
- package/lib/module/engine/systemPrompt.js.map +1 -0
- package/lib/module/index.js +30 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/policy/labels.js +296 -0
- package/lib/module/policy/labels.js.map +1 -0
- package/lib/module/policy/policy.js +137 -0
- package/lib/module/policy/policy.js.map +1 -0
- package/lib/module/policy/redact.js +128 -0
- package/lib/module/policy/redact.js.map +1 -0
- package/lib/module/providers/anthropic.js +268 -0
- package/lib/module/providers/anthropic.js.map +1 -0
- package/lib/module/providers/openai.js +189 -0
- package/lib/module/providers/openai.js.map +1 -0
- package/lib/module/providers/sse.js +99 -0
- package/lib/module/providers/sse.js.map +1 -0
- package/lib/module/providers/types.js +4 -0
- package/lib/module/providers/types.js.map +1 -0
- package/lib/module/session.js +168 -0
- package/lib/module/session.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/blocks/receipts.d.ts +32 -0
- package/lib/typescript/blocks/receipts.d.ts.map +1 -0
- package/lib/typescript/blocks/types.d.ts +576 -0
- package/lib/typescript/blocks/types.d.ts.map +1 -0
- package/lib/typescript/blocks/uiTool.d.ts +15 -0
- package/lib/typescript/blocks/uiTool.d.ts.map +1 -0
- package/lib/typescript/catalog/catalog.g.d.ts +11 -0
- package/lib/typescript/catalog/catalog.g.d.ts.map +1 -0
- package/lib/typescript/catalog/snapshotReads.d.ts +37 -0
- package/lib/typescript/catalog/snapshotReads.d.ts.map +1 -0
- package/lib/typescript/catalog/toProviderTools.d.ts +52 -0
- package/lib/typescript/catalog/toProviderTools.d.ts.map +1 -0
- package/lib/typescript/catalog/validateParams.d.ts +33 -0
- package/lib/typescript/catalog/validateParams.d.ts.map +1 -0
- package/lib/typescript/context/buildContextPack.d.ts +32 -0
- package/lib/typescript/context/buildContextPack.d.ts.map +1 -0
- package/lib/typescript/effects/ledger.d.ts +119 -0
- package/lib/typescript/effects/ledger.d.ts.map +1 -0
- package/lib/typescript/engine/effectFor.d.ts +21 -0
- package/lib/typescript/engine/effectFor.d.ts.map +1 -0
- package/lib/typescript/engine/runAgentTurn.d.ts +140 -0
- package/lib/typescript/engine/runAgentTurn.d.ts.map +1 -0
- package/lib/typescript/engine/systemPrompt.d.ts +49 -0
- package/lib/typescript/engine/systemPrompt.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +29 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/policy/labels.d.ts +10 -0
- package/lib/typescript/policy/labels.d.ts.map +1 -0
- package/lib/typescript/policy/policy.d.ts +106 -0
- package/lib/typescript/policy/policy.d.ts.map +1 -0
- package/lib/typescript/policy/redact.d.ts +37 -0
- package/lib/typescript/policy/redact.d.ts.map +1 -0
- package/lib/typescript/providers/anthropic.d.ts +22 -0
- package/lib/typescript/providers/anthropic.d.ts.map +1 -0
- package/lib/typescript/providers/openai.d.ts +19 -0
- package/lib/typescript/providers/openai.d.ts.map +1 -0
- package/lib/typescript/providers/sse.d.ts +18 -0
- package/lib/typescript/providers/sse.d.ts.map +1 -0
- package/lib/typescript/providers/types.d.ts +129 -0
- package/lib/typescript/providers/types.d.ts.map +1 -0
- package/lib/typescript/session.d.ts +58 -0
- package/lib/typescript/session.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +119 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,987 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `buoy_ui` — the one tool the model uses to SHOW or ASK instead of writing
|
|
5
|
+
* prose. One tool with `action` as the block kind, like every other Buoy
|
|
6
|
+
* tool, so tool selection stays good; the params are the block itself.
|
|
7
|
+
*
|
|
8
|
+
* Validation is the same `validateParams` every other action gets, and an
|
|
9
|
+
* invalid block goes back as a schema error for self-correction. A valid
|
|
10
|
+
* block is stamped with an id and yielded as a `block` event; an Ask block
|
|
11
|
+
* also ends the turn (see runAgentTurn).
|
|
12
|
+
*/
|
|
13
|
+
import { validateParams } from "../catalog/validateParams";
|
|
14
|
+
import { CAPS, MODEL_KINDS } from "./types";
|
|
15
|
+
export const UI_TOOL_NAME = "buoy_ui";
|
|
16
|
+
const tone = {
|
|
17
|
+
type: "string",
|
|
18
|
+
enum: ["muted", "accent", "warning", "danger", "info"]
|
|
19
|
+
};
|
|
20
|
+
const action = {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
label: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Button text, 1–4 words."
|
|
26
|
+
},
|
|
27
|
+
primary: {
|
|
28
|
+
type: "boolean"
|
|
29
|
+
},
|
|
30
|
+
send: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Text sent as the user's next message when tapped."
|
|
33
|
+
},
|
|
34
|
+
dispatch: {
|
|
35
|
+
type: "object",
|
|
36
|
+
description: "A Buoy action to run when tapped — same policy and undo as if you called it.",
|
|
37
|
+
properties: {
|
|
38
|
+
toolId: {
|
|
39
|
+
type: "string"
|
|
40
|
+
},
|
|
41
|
+
action: {
|
|
42
|
+
type: "string"
|
|
43
|
+
},
|
|
44
|
+
params: {
|
|
45
|
+
type: "object"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
required: ["toolId", "action"]
|
|
49
|
+
},
|
|
50
|
+
openTool: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
toolId: {
|
|
54
|
+
type: "string"
|
|
55
|
+
},
|
|
56
|
+
select: {
|
|
57
|
+
type: "string"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
required: ["toolId"]
|
|
61
|
+
},
|
|
62
|
+
minimize: {
|
|
63
|
+
type: "boolean",
|
|
64
|
+
description: "Collapse the chat so the user can look at the app."
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
required: ["label"],
|
|
68
|
+
additionalProperties: false
|
|
69
|
+
};
|
|
70
|
+
const cell = {
|
|
71
|
+
anyOf: [{
|
|
72
|
+
type: "string"
|
|
73
|
+
}, {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
text: {
|
|
77
|
+
type: "string"
|
|
78
|
+
},
|
|
79
|
+
tone
|
|
80
|
+
},
|
|
81
|
+
required: ["text"]
|
|
82
|
+
}]
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/** Per-kind param schemas. Keys are block kinds; `kind` itself is the `action`. */
|
|
86
|
+
export const UI_PARAMS = {
|
|
87
|
+
choice: {
|
|
88
|
+
type: "object",
|
|
89
|
+
properties: {
|
|
90
|
+
question: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "`title` is accepted as a synonym."
|
|
93
|
+
},
|
|
94
|
+
title: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Synonym for `question`."
|
|
97
|
+
},
|
|
98
|
+
options: {
|
|
99
|
+
type: "array",
|
|
100
|
+
maxItems: CAPS.choiceOptions,
|
|
101
|
+
items: {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {
|
|
104
|
+
id: {
|
|
105
|
+
type: "string"
|
|
106
|
+
},
|
|
107
|
+
label: {
|
|
108
|
+
type: "string"
|
|
109
|
+
},
|
|
110
|
+
detail: {
|
|
111
|
+
type: "string"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
required: ["id", "label"]
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
allowFreeText: {
|
|
118
|
+
type: "boolean"
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
// `question` is satisfied by `title` too — see normaliseChoice. Same
|
|
122
|
+
// lesson the confirm block cost us: a schema that rejects the obvious
|
|
123
|
+
// synonym sends a model into a retry loop and the user is never asked.
|
|
124
|
+
required: ["options"],
|
|
125
|
+
additionalProperties: false
|
|
126
|
+
},
|
|
127
|
+
confirm: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: {
|
|
130
|
+
title: {
|
|
131
|
+
type: "string",
|
|
132
|
+
description: "Optional heading. Defaults to \"Confirm\"."
|
|
133
|
+
},
|
|
134
|
+
change: {
|
|
135
|
+
type: "string",
|
|
136
|
+
description: "What will change, in the user's words. `message` is accepted as a synonym."
|
|
137
|
+
},
|
|
138
|
+
message: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Synonym for `change`."
|
|
141
|
+
},
|
|
142
|
+
consequence: {
|
|
143
|
+
type: "string"
|
|
144
|
+
},
|
|
145
|
+
approveLabel: {
|
|
146
|
+
type: "string"
|
|
147
|
+
},
|
|
148
|
+
declineLabel: {
|
|
149
|
+
type: "string"
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
// ONLY `change` is required, and `message` satisfies it (see
|
|
153
|
+
// normaliseConfirm). Requiring both `title` AND `change` while rejecting
|
|
154
|
+
// `message` sent a live model into a four-attempt failure loop — it kept
|
|
155
|
+
// sending the universal {title, message} shape, and the validator
|
|
156
|
+
// answered "change: required" then "title: required", which reads as
|
|
157
|
+
// contradictory. It gave up and the user never got asked.
|
|
158
|
+
required: [],
|
|
159
|
+
additionalProperties: false
|
|
160
|
+
},
|
|
161
|
+
input: {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: {
|
|
164
|
+
label: {
|
|
165
|
+
type: "string"
|
|
166
|
+
},
|
|
167
|
+
type: {
|
|
168
|
+
type: "string",
|
|
169
|
+
enum: ["text", "number", "secret"]
|
|
170
|
+
},
|
|
171
|
+
placeholder: {
|
|
172
|
+
type: "string"
|
|
173
|
+
},
|
|
174
|
+
submitLabel: {
|
|
175
|
+
type: "string"
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
required: ["label"],
|
|
179
|
+
additionalProperties: false
|
|
180
|
+
},
|
|
181
|
+
table: {
|
|
182
|
+
type: "object",
|
|
183
|
+
properties: {
|
|
184
|
+
title: {
|
|
185
|
+
type: "string"
|
|
186
|
+
},
|
|
187
|
+
columns: {
|
|
188
|
+
type: "array",
|
|
189
|
+
maxItems: 4,
|
|
190
|
+
items: {
|
|
191
|
+
type: "string"
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
rows: {
|
|
195
|
+
type: "array",
|
|
196
|
+
items: {
|
|
197
|
+
type: "array",
|
|
198
|
+
items: cell
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
total: {
|
|
202
|
+
type: "number"
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
required: ["columns", "rows"],
|
|
206
|
+
additionalProperties: false
|
|
207
|
+
},
|
|
208
|
+
keyValue: {
|
|
209
|
+
type: "object",
|
|
210
|
+
properties: {
|
|
211
|
+
title: {
|
|
212
|
+
type: "string"
|
|
213
|
+
},
|
|
214
|
+
pairs: {
|
|
215
|
+
type: "array",
|
|
216
|
+
items: {
|
|
217
|
+
type: "object",
|
|
218
|
+
properties: {
|
|
219
|
+
key: {
|
|
220
|
+
type: "string"
|
|
221
|
+
},
|
|
222
|
+
value: {}
|
|
223
|
+
},
|
|
224
|
+
required: ["key"]
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
required: ["pairs"],
|
|
229
|
+
additionalProperties: false
|
|
230
|
+
},
|
|
231
|
+
list: {
|
|
232
|
+
type: "object",
|
|
233
|
+
properties: {
|
|
234
|
+
title: {
|
|
235
|
+
type: "string"
|
|
236
|
+
},
|
|
237
|
+
items: {
|
|
238
|
+
type: "array",
|
|
239
|
+
items: {
|
|
240
|
+
type: "object",
|
|
241
|
+
properties: {
|
|
242
|
+
id: {
|
|
243
|
+
type: "string"
|
|
244
|
+
},
|
|
245
|
+
title: {
|
|
246
|
+
type: "string"
|
|
247
|
+
},
|
|
248
|
+
subtitle: {
|
|
249
|
+
type: "string"
|
|
250
|
+
},
|
|
251
|
+
trailing: {
|
|
252
|
+
type: "string"
|
|
253
|
+
},
|
|
254
|
+
image: {
|
|
255
|
+
type: "string",
|
|
256
|
+
description: "Only a URL you read from the app this turn."
|
|
257
|
+
},
|
|
258
|
+
tone,
|
|
259
|
+
action
|
|
260
|
+
},
|
|
261
|
+
required: ["title"]
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
total: {
|
|
265
|
+
type: "number"
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
required: ["items"],
|
|
269
|
+
additionalProperties: false
|
|
270
|
+
},
|
|
271
|
+
imageGrid: {
|
|
272
|
+
type: "object",
|
|
273
|
+
properties: {
|
|
274
|
+
title: {
|
|
275
|
+
type: "string"
|
|
276
|
+
},
|
|
277
|
+
images: {
|
|
278
|
+
type: "array",
|
|
279
|
+
maxItems: CAPS.gridMax,
|
|
280
|
+
items: {
|
|
281
|
+
type: "object",
|
|
282
|
+
properties: {
|
|
283
|
+
url: {
|
|
284
|
+
type: "string",
|
|
285
|
+
description: "Only a URL you read from the app this turn."
|
|
286
|
+
},
|
|
287
|
+
alt: {
|
|
288
|
+
type: "string"
|
|
289
|
+
},
|
|
290
|
+
caption: {
|
|
291
|
+
type: "string"
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
required: ["url", "alt"]
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
total: {
|
|
298
|
+
type: "number"
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
required: ["images"],
|
|
302
|
+
additionalProperties: false
|
|
303
|
+
},
|
|
304
|
+
diff: {
|
|
305
|
+
type: "object",
|
|
306
|
+
properties: {
|
|
307
|
+
title: {
|
|
308
|
+
type: "string"
|
|
309
|
+
},
|
|
310
|
+
changes: {
|
|
311
|
+
type: "array",
|
|
312
|
+
items: {
|
|
313
|
+
type: "object",
|
|
314
|
+
properties: {
|
|
315
|
+
path: {
|
|
316
|
+
type: "string"
|
|
317
|
+
},
|
|
318
|
+
before: {},
|
|
319
|
+
after: {}
|
|
320
|
+
},
|
|
321
|
+
required: ["path"]
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
required: ["changes"],
|
|
326
|
+
additionalProperties: false
|
|
327
|
+
},
|
|
328
|
+
chart: {
|
|
329
|
+
type: "object",
|
|
330
|
+
properties: {
|
|
331
|
+
title: {
|
|
332
|
+
type: "string"
|
|
333
|
+
},
|
|
334
|
+
bars: {
|
|
335
|
+
type: "array",
|
|
336
|
+
maxItems: CAPS.chartBars,
|
|
337
|
+
items: {
|
|
338
|
+
type: "object",
|
|
339
|
+
properties: {
|
|
340
|
+
label: {
|
|
341
|
+
type: "string"
|
|
342
|
+
},
|
|
343
|
+
value: {
|
|
344
|
+
type: "number"
|
|
345
|
+
},
|
|
346
|
+
unit: {
|
|
347
|
+
type: "string"
|
|
348
|
+
},
|
|
349
|
+
tone
|
|
350
|
+
},
|
|
351
|
+
required: ["label", "value"]
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
max: {
|
|
355
|
+
type: "number"
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
required: ["bars"],
|
|
359
|
+
additionalProperties: false
|
|
360
|
+
},
|
|
361
|
+
steps: {
|
|
362
|
+
type: "object",
|
|
363
|
+
properties: {
|
|
364
|
+
title: {
|
|
365
|
+
type: "string"
|
|
366
|
+
},
|
|
367
|
+
steps: {
|
|
368
|
+
type: "array",
|
|
369
|
+
items: {
|
|
370
|
+
type: "object",
|
|
371
|
+
properties: {
|
|
372
|
+
text: {
|
|
373
|
+
type: "string"
|
|
374
|
+
},
|
|
375
|
+
state: {
|
|
376
|
+
type: "string",
|
|
377
|
+
enum: ["todo", "done", "failed"]
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
required: ["text"]
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
required: ["steps"],
|
|
385
|
+
additionalProperties: false
|
|
386
|
+
},
|
|
387
|
+
code: {
|
|
388
|
+
type: "object",
|
|
389
|
+
properties: {
|
|
390
|
+
title: {
|
|
391
|
+
type: "string"
|
|
392
|
+
},
|
|
393
|
+
language: {
|
|
394
|
+
type: "string",
|
|
395
|
+
enum: ["json", "text"]
|
|
396
|
+
},
|
|
397
|
+
text: {
|
|
398
|
+
type: "string"
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
required: ["text"],
|
|
402
|
+
additionalProperties: false
|
|
403
|
+
},
|
|
404
|
+
actions: {
|
|
405
|
+
type: "object",
|
|
406
|
+
properties: {
|
|
407
|
+
actions: {
|
|
408
|
+
type: "array",
|
|
409
|
+
maxItems: CAPS.actions,
|
|
410
|
+
items: action
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
required: ["actions"],
|
|
414
|
+
additionalProperties: false
|
|
415
|
+
},
|
|
416
|
+
suggestions: {
|
|
417
|
+
type: "object",
|
|
418
|
+
properties: {
|
|
419
|
+
chips: {
|
|
420
|
+
type: "array",
|
|
421
|
+
maxItems: 4,
|
|
422
|
+
items: {
|
|
423
|
+
type: "object",
|
|
424
|
+
properties: {
|
|
425
|
+
label: {
|
|
426
|
+
type: "string"
|
|
427
|
+
},
|
|
428
|
+
send: {
|
|
429
|
+
type: "string"
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
required: ["label"]
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
required: ["chips"],
|
|
437
|
+
additionalProperties: false
|
|
438
|
+
},
|
|
439
|
+
multiSelect: {
|
|
440
|
+
type: "object",
|
|
441
|
+
properties: {
|
|
442
|
+
question: {
|
|
443
|
+
type: "string"
|
|
444
|
+
},
|
|
445
|
+
options: {
|
|
446
|
+
type: "array",
|
|
447
|
+
maxItems: 12,
|
|
448
|
+
items: {
|
|
449
|
+
type: "object",
|
|
450
|
+
properties: {
|
|
451
|
+
id: {
|
|
452
|
+
type: "string"
|
|
453
|
+
},
|
|
454
|
+
label: {
|
|
455
|
+
type: "string"
|
|
456
|
+
},
|
|
457
|
+
detail: {
|
|
458
|
+
type: "string"
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
required: ["id", "label"]
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
min: {
|
|
465
|
+
type: "number"
|
|
466
|
+
},
|
|
467
|
+
max: {
|
|
468
|
+
type: "number"
|
|
469
|
+
},
|
|
470
|
+
submitLabel: {
|
|
471
|
+
type: "string"
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
required: ["question", "options"],
|
|
475
|
+
additionalProperties: false
|
|
476
|
+
},
|
|
477
|
+
select: {
|
|
478
|
+
type: "object",
|
|
479
|
+
properties: {
|
|
480
|
+
label: {
|
|
481
|
+
type: "string"
|
|
482
|
+
},
|
|
483
|
+
options: {
|
|
484
|
+
type: "array",
|
|
485
|
+
maxItems: 100,
|
|
486
|
+
items: {
|
|
487
|
+
type: "object",
|
|
488
|
+
properties: {
|
|
489
|
+
id: {
|
|
490
|
+
type: "string"
|
|
491
|
+
},
|
|
492
|
+
label: {
|
|
493
|
+
type: "string"
|
|
494
|
+
},
|
|
495
|
+
detail: {
|
|
496
|
+
type: "string"
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
required: ["id", "label"]
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
searchable: {
|
|
503
|
+
type: "boolean"
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
required: ["label", "options"],
|
|
507
|
+
additionalProperties: false
|
|
508
|
+
},
|
|
509
|
+
slider: {
|
|
510
|
+
type: "object",
|
|
511
|
+
properties: {
|
|
512
|
+
label: {
|
|
513
|
+
type: "string"
|
|
514
|
+
},
|
|
515
|
+
min: {
|
|
516
|
+
type: "number"
|
|
517
|
+
},
|
|
518
|
+
max: {
|
|
519
|
+
type: "number"
|
|
520
|
+
},
|
|
521
|
+
step: {
|
|
522
|
+
type: "number"
|
|
523
|
+
},
|
|
524
|
+
value: {
|
|
525
|
+
type: "number"
|
|
526
|
+
},
|
|
527
|
+
unit: {
|
|
528
|
+
type: "string"
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
required: ["label", "min", "max"],
|
|
532
|
+
additionalProperties: false
|
|
533
|
+
},
|
|
534
|
+
dateTime: {
|
|
535
|
+
type: "object",
|
|
536
|
+
properties: {
|
|
537
|
+
label: {
|
|
538
|
+
type: "string"
|
|
539
|
+
},
|
|
540
|
+
mode: {
|
|
541
|
+
type: "string",
|
|
542
|
+
enum: ["date", "time", "datetime"]
|
|
543
|
+
},
|
|
544
|
+
presets: {
|
|
545
|
+
type: "array",
|
|
546
|
+
items: {
|
|
547
|
+
type: "object",
|
|
548
|
+
properties: {
|
|
549
|
+
label: {
|
|
550
|
+
type: "string"
|
|
551
|
+
},
|
|
552
|
+
value: {
|
|
553
|
+
type: "string"
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
required: ["label", "value"]
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
required: ["label"],
|
|
561
|
+
additionalProperties: false
|
|
562
|
+
},
|
|
563
|
+
form: {
|
|
564
|
+
type: "object",
|
|
565
|
+
properties: {
|
|
566
|
+
title: {
|
|
567
|
+
type: "string"
|
|
568
|
+
},
|
|
569
|
+
fields: {
|
|
570
|
+
type: "array",
|
|
571
|
+
maxItems: 6,
|
|
572
|
+
items: {
|
|
573
|
+
type: "object"
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
submitLabel: {
|
|
577
|
+
type: "string"
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
required: ["fields"],
|
|
581
|
+
additionalProperties: false
|
|
582
|
+
},
|
|
583
|
+
proposal: {
|
|
584
|
+
type: "object",
|
|
585
|
+
properties: {
|
|
586
|
+
title: {
|
|
587
|
+
type: "string"
|
|
588
|
+
},
|
|
589
|
+
change: {
|
|
590
|
+
type: "string"
|
|
591
|
+
},
|
|
592
|
+
fields: {
|
|
593
|
+
type: "array",
|
|
594
|
+
maxItems: 6,
|
|
595
|
+
items: {
|
|
596
|
+
type: "object"
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
consequence: {
|
|
600
|
+
type: "string"
|
|
601
|
+
},
|
|
602
|
+
approveLabel: {
|
|
603
|
+
type: "string"
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
required: ["title", "change", "fields"],
|
|
607
|
+
additionalProperties: false
|
|
608
|
+
},
|
|
609
|
+
claim: {
|
|
610
|
+
type: "object",
|
|
611
|
+
properties: {
|
|
612
|
+
text: {
|
|
613
|
+
type: "string"
|
|
614
|
+
},
|
|
615
|
+
sources: {
|
|
616
|
+
type: "array",
|
|
617
|
+
maxItems: 4,
|
|
618
|
+
items: {
|
|
619
|
+
type: "object",
|
|
620
|
+
properties: {
|
|
621
|
+
label: {
|
|
622
|
+
type: "string"
|
|
623
|
+
},
|
|
624
|
+
detail: {
|
|
625
|
+
type: "string"
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
required: ["label"]
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
verify: action
|
|
632
|
+
},
|
|
633
|
+
required: ["text", "sources"],
|
|
634
|
+
additionalProperties: false
|
|
635
|
+
},
|
|
636
|
+
details: {
|
|
637
|
+
type: "object",
|
|
638
|
+
properties: {
|
|
639
|
+
summary: {
|
|
640
|
+
type: "string"
|
|
641
|
+
},
|
|
642
|
+
body: {
|
|
643
|
+
type: "string"
|
|
644
|
+
},
|
|
645
|
+
open: {
|
|
646
|
+
type: "boolean"
|
|
647
|
+
}
|
|
648
|
+
},
|
|
649
|
+
required: ["summary", "body"],
|
|
650
|
+
additionalProperties: false
|
|
651
|
+
},
|
|
652
|
+
compare: {
|
|
653
|
+
type: "object",
|
|
654
|
+
properties: {
|
|
655
|
+
title: {
|
|
656
|
+
type: "string"
|
|
657
|
+
},
|
|
658
|
+
left: {
|
|
659
|
+
type: "object",
|
|
660
|
+
properties: {
|
|
661
|
+
title: {
|
|
662
|
+
type: "string"
|
|
663
|
+
},
|
|
664
|
+
pairs: {
|
|
665
|
+
type: "array",
|
|
666
|
+
items: {
|
|
667
|
+
type: "object"
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
},
|
|
671
|
+
required: ["title", "pairs"]
|
|
672
|
+
},
|
|
673
|
+
right: {
|
|
674
|
+
type: "object",
|
|
675
|
+
properties: {
|
|
676
|
+
title: {
|
|
677
|
+
type: "string"
|
|
678
|
+
},
|
|
679
|
+
pairs: {
|
|
680
|
+
type: "array",
|
|
681
|
+
items: {
|
|
682
|
+
type: "object"
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
required: ["title", "pairs"]
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
required: ["left", "right"],
|
|
690
|
+
additionalProperties: false
|
|
691
|
+
},
|
|
692
|
+
card: {
|
|
693
|
+
type: "object",
|
|
694
|
+
properties: {
|
|
695
|
+
title: {
|
|
696
|
+
type: "string"
|
|
697
|
+
},
|
|
698
|
+
subtitle: {
|
|
699
|
+
type: "string"
|
|
700
|
+
},
|
|
701
|
+
image: {
|
|
702
|
+
type: "string"
|
|
703
|
+
},
|
|
704
|
+
facts: {
|
|
705
|
+
type: "array",
|
|
706
|
+
maxItems: 6,
|
|
707
|
+
items: {
|
|
708
|
+
type: "object",
|
|
709
|
+
properties: {
|
|
710
|
+
label: {
|
|
711
|
+
type: "string"
|
|
712
|
+
},
|
|
713
|
+
value: {
|
|
714
|
+
type: "string"
|
|
715
|
+
},
|
|
716
|
+
tone
|
|
717
|
+
},
|
|
718
|
+
required: ["label", "value"]
|
|
719
|
+
}
|
|
720
|
+
},
|
|
721
|
+
actions: {
|
|
722
|
+
type: "array",
|
|
723
|
+
maxItems: CAPS.actions,
|
|
724
|
+
items: action
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
required: ["title"],
|
|
728
|
+
additionalProperties: false
|
|
729
|
+
},
|
|
730
|
+
cardCarousel: {
|
|
731
|
+
type: "object",
|
|
732
|
+
properties: {
|
|
733
|
+
title: {
|
|
734
|
+
type: "string"
|
|
735
|
+
},
|
|
736
|
+
cards: {
|
|
737
|
+
type: "array",
|
|
738
|
+
maxItems: 8,
|
|
739
|
+
items: {
|
|
740
|
+
type: "object"
|
|
741
|
+
}
|
|
742
|
+
},
|
|
743
|
+
total: {
|
|
744
|
+
type: "number"
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
required: ["cards"],
|
|
748
|
+
additionalProperties: false
|
|
749
|
+
},
|
|
750
|
+
sparkline: {
|
|
751
|
+
type: "object",
|
|
752
|
+
properties: {
|
|
753
|
+
title: {
|
|
754
|
+
type: "string"
|
|
755
|
+
},
|
|
756
|
+
series: {
|
|
757
|
+
type: "array",
|
|
758
|
+
maxItems: 120,
|
|
759
|
+
items: {
|
|
760
|
+
type: "number"
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
unit: {
|
|
764
|
+
type: "string"
|
|
765
|
+
},
|
|
766
|
+
label: {
|
|
767
|
+
type: "string"
|
|
768
|
+
},
|
|
769
|
+
current: {
|
|
770
|
+
type: "number"
|
|
771
|
+
}
|
|
772
|
+
},
|
|
773
|
+
required: ["series"],
|
|
774
|
+
additionalProperties: false
|
|
775
|
+
},
|
|
776
|
+
plan: {
|
|
777
|
+
type: "object",
|
|
778
|
+
properties: {
|
|
779
|
+
title: {
|
|
780
|
+
type: "string"
|
|
781
|
+
},
|
|
782
|
+
steps: {
|
|
783
|
+
type: "array",
|
|
784
|
+
maxItems: 10,
|
|
785
|
+
items: {
|
|
786
|
+
type: "object",
|
|
787
|
+
properties: {
|
|
788
|
+
text: {
|
|
789
|
+
type: "string"
|
|
790
|
+
},
|
|
791
|
+
state: {
|
|
792
|
+
type: "string",
|
|
793
|
+
enum: ["todo", "running", "done", "failed"]
|
|
794
|
+
}
|
|
795
|
+
},
|
|
796
|
+
required: ["text"]
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
pending: {
|
|
800
|
+
type: "boolean"
|
|
801
|
+
},
|
|
802
|
+
actions: {
|
|
803
|
+
type: "array",
|
|
804
|
+
maxItems: CAPS.actions,
|
|
805
|
+
items: action
|
|
806
|
+
}
|
|
807
|
+
},
|
|
808
|
+
required: ["title", "steps"],
|
|
809
|
+
additionalProperties: false
|
|
810
|
+
},
|
|
811
|
+
finding: {
|
|
812
|
+
type: "object",
|
|
813
|
+
properties: {
|
|
814
|
+
title: {
|
|
815
|
+
type: "string"
|
|
816
|
+
},
|
|
817
|
+
evidence: {
|
|
818
|
+
type: "array",
|
|
819
|
+
items: {
|
|
820
|
+
type: "object",
|
|
821
|
+
properties: {
|
|
822
|
+
label: {
|
|
823
|
+
type: "string"
|
|
824
|
+
},
|
|
825
|
+
value: {
|
|
826
|
+
type: "string"
|
|
827
|
+
},
|
|
828
|
+
tone
|
|
829
|
+
},
|
|
830
|
+
required: ["label", "value"]
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
actions: {
|
|
834
|
+
type: "array",
|
|
835
|
+
maxItems: CAPS.actions,
|
|
836
|
+
items: action
|
|
837
|
+
}
|
|
838
|
+
},
|
|
839
|
+
required: ["title", "evidence"],
|
|
840
|
+
additionalProperties: false
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
const KIND_HELP = {
|
|
844
|
+
choice: "ASK the user to pick one of 2–5 options when a request could mean different things. Ends your turn; their pick comes back as their next message.",
|
|
845
|
+
confirm: "ASK before a change you are guessing at (a shape you haven't read, a user you invented, anything destructive). Ends your turn.",
|
|
846
|
+
input: "ASK for one value you need (an order id, a quantity). Ends your turn.",
|
|
847
|
+
table: "Show rows of comparable facts — prices before/after, requests, counts. ≤4 columns, ≤8 rows; put the rest in total.",
|
|
848
|
+
keyValue: "Show one record's fields — a profile, a store's state, a stored value.",
|
|
849
|
+
list: "Show several things with a title each — items, orders, users. Add image only from URLs you read. Each item may carry one action.",
|
|
850
|
+
imageGrid: "Show pictures — menu items, loaded images. Only URLs you read from the app this turn. ≤24; grid up to 6, carousel after.",
|
|
851
|
+
diff: "Show what changed: path, before, after. Use for compare questions and after any write.",
|
|
852
|
+
chart: "Show numbers side by side as bars — render counts, timings, sizes. ≤8 bars.",
|
|
853
|
+
steps: "Show a procedure or a plan as a checklist (to reproduce, to test).",
|
|
854
|
+
code: "Show a raw value only when the user asked for the raw thing.",
|
|
855
|
+
actions: "Offer up to two buttons for what to do next instead of writing 'say X and I'll…'. Use send for a chat reply, dispatch to run an action directly, minimize to let them look at the app.",
|
|
856
|
+
finding: "Report something worth filing: a title plus evidence rows (request → status, screen → what it showed). Add a button to reproduce or open the tool.",
|
|
857
|
+
suggestions: "Offer 2–4 follow-up questions the user might ask next. Put it last.",
|
|
858
|
+
multiSelect: "ASK the user to pick several (which stores, which keys). Ends your turn.",
|
|
859
|
+
select: "ASK the user to pick one from a long list (routes, stores) — searchable. Ends your turn.",
|
|
860
|
+
slider: "ASK for a number in a range (a delay, a quantity). Ends your turn.",
|
|
861
|
+
dateTime: "ASK for a date or time. Ends your turn.",
|
|
862
|
+
form: "ASK for several coupled values at once. Ends your turn.",
|
|
863
|
+
proposal: "ASK to approve a change whose values the user may edit first. Ends your turn.",
|
|
864
|
+
claim: "State one fact with the evidence chips it rests on and a Verify button that re-reads it.",
|
|
865
|
+
details: "A collapsed detail the user can open — long explanations, raw values.",
|
|
866
|
+
compare: "Two things side by side with differences highlighted.",
|
|
867
|
+
card: "One entity: title, image, facts, up to two actions.",
|
|
868
|
+
cardCarousel: "3–8 cards to scan and pick from (scenarios, snapshots, items).",
|
|
869
|
+
sparkline: "A series over time — FPS, memory, request timing.",
|
|
870
|
+
plan: "Before multi-step work: the steps you intend, with Go/Stop. Update states as you go."
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
/** The provider-facing tool definition. */
|
|
874
|
+
export function uiProviderTool() {
|
|
875
|
+
const kinds = MODEL_KINDS.join(" | ");
|
|
876
|
+
const help = MODEL_KINDS.map(k => `- ${k}: ${KIND_HELP[k]}`).join("\n");
|
|
877
|
+
return {
|
|
878
|
+
name: UI_TOOL_NAME,
|
|
879
|
+
description: `Show the user something richer than text, or ask them something. Blocks render inline in the chat, ABOVE your text — so your text should say what the block means, never repeat it. Kinds: ${kinds}.\n${help}\n\nRules: choice/confirm/input END your turn — send them last, alone, with no other tool calls, and the answer arrives as the user's next message. Never put URLs, ids or numbers in a block that you did not read from a tool result this turn. Prefer the smallest block that answers.`,
|
|
880
|
+
inputSchema: {
|
|
881
|
+
type: "object",
|
|
882
|
+
properties: {
|
|
883
|
+
action: {
|
|
884
|
+
type: "string",
|
|
885
|
+
enum: [...MODEL_KINDS],
|
|
886
|
+
description: "The block kind."
|
|
887
|
+
},
|
|
888
|
+
params: {
|
|
889
|
+
type: "object",
|
|
890
|
+
description: "The block's fields; see the kind's schema."
|
|
891
|
+
}
|
|
892
|
+
},
|
|
893
|
+
required: ["action", "params"],
|
|
894
|
+
additionalProperties: false
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
let seq = 0;
|
|
899
|
+
|
|
900
|
+
/** Validate a `buoy_ui` call and build the block. */
|
|
901
|
+
/**
|
|
902
|
+
* Accept the shape models actually send for a confirm. `message` is the
|
|
903
|
+
* near-universal field name, and `title` alone is a perfectly clear ask —
|
|
904
|
+
* so fold them into the block's own vocabulary rather than bouncing the
|
|
905
|
+
* call. Returns null only when there is genuinely nothing to agree to.
|
|
906
|
+
*/
|
|
907
|
+
function normaliseConfirm(params) {
|
|
908
|
+
const str = v => typeof v === "string" && v.trim() ? v.trim() : undefined;
|
|
909
|
+
const {
|
|
910
|
+
message,
|
|
911
|
+
...rest
|
|
912
|
+
} = params;
|
|
913
|
+
const change = str(rest.change) ?? str(message) ?? str(rest.title);
|
|
914
|
+
if (!change) return null;
|
|
915
|
+
// When both arrived, the one we didn't use as `change` still carries
|
|
916
|
+
// meaning — keep it as the consequence line rather than dropping it.
|
|
917
|
+
const spare = str(rest.change) ? str(message) : undefined;
|
|
918
|
+
return {
|
|
919
|
+
...rest,
|
|
920
|
+
title: str(rest.title) ?? "Confirm",
|
|
921
|
+
change,
|
|
922
|
+
consequence: str(rest.consequence) ?? spare
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/** Accept `title` where the schema says `question` — see normaliseConfirm. */
|
|
927
|
+
function normaliseChoice(params) {
|
|
928
|
+
const str = v => typeof v === "string" && v.trim() ? v.trim() : undefined;
|
|
929
|
+
const {
|
|
930
|
+
title,
|
|
931
|
+
...rest
|
|
932
|
+
} = params;
|
|
933
|
+
const question = str(rest.question) ?? str(title);
|
|
934
|
+
if (!question) return null;
|
|
935
|
+
return {
|
|
936
|
+
...rest,
|
|
937
|
+
question
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
export function buildUiBlock(input) {
|
|
941
|
+
const kind = input.action;
|
|
942
|
+
if (!kind || !MODEL_KINDS.includes(kind)) {
|
|
943
|
+
return {
|
|
944
|
+
ok: false,
|
|
945
|
+
errors: [`"action" must be one of: ${MODEL_KINDS.join(", ")}`]
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
const schema = UI_PARAMS[kind];
|
|
949
|
+
const check = validateParams(input.params, schema);
|
|
950
|
+
if (!check.ok) return {
|
|
951
|
+
ok: false,
|
|
952
|
+
errors: check.errors
|
|
953
|
+
};
|
|
954
|
+
let params = input.params ?? {};
|
|
955
|
+
if (kind === "choice") {
|
|
956
|
+
const fixed = normaliseChoice(params);
|
|
957
|
+
if (!fixed) {
|
|
958
|
+
return {
|
|
959
|
+
ok: false,
|
|
960
|
+
errors: ['choice needs the question being asked: pass `question` (or `title`), e.g. { "question": "Which checkout?", "options": [...] }']
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
params = fixed;
|
|
964
|
+
}
|
|
965
|
+
if (kind === "confirm") {
|
|
966
|
+
const fixed = normaliseConfirm(params);
|
|
967
|
+
if (!fixed) {
|
|
968
|
+
return {
|
|
969
|
+
ok: false,
|
|
970
|
+
errors: ['confirm needs what the user is agreeing to: pass `change` (or `message`), e.g. { "change": "Wipe all app storage", "consequence": "This cannot be undone." }']
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
params = fixed;
|
|
974
|
+
}
|
|
975
|
+
const block = {
|
|
976
|
+
...params,
|
|
977
|
+
kind,
|
|
978
|
+
id: `ui${++seq}`,
|
|
979
|
+
source: "model"
|
|
980
|
+
};
|
|
981
|
+
return {
|
|
982
|
+
ok: true,
|
|
983
|
+
errors: [],
|
|
984
|
+
block
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
//# sourceMappingURL=uiTool.js.map
|