@ixo/oracle-runtime-workers 0.31.0 → 0.33.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/package.json +4 -4
- package/src/core/middlewares/tool-repetition-guard.ts +6 -3
- package/src/core/middlewares/turn-boundary.ts +6 -1
- package/src/core/tool-not-run.ts +30 -0
- package/src/core/wrap-plugin-tool.ts +9 -1
- package/src/do/user-oracle-do.ts +78 -17
- package/src/hub-actions/chain-tx-refusals.ts +307 -0
- package/src/hub-actions/client.ts +128 -30
- package/src/hub-actions/request.ts +32 -25
- package/src/hub-actions/transfer.ts +187 -33
- package/src/plugin-api/types.ts +103 -25
- package/src/plugins/hub-actions/chain-tx-catalog.ts +1451 -0
- package/src/plugins/hub-actions/hub-actions.plugin.ts +558 -222
- package/src/plugins/ixo-chain/ixo-chain.plugin.ts +1 -1
- package/src/plugins/ixo-chain/kinds/chain/index.ts +2 -0
- package/src/plugins/ixo-chain/kinds/chain/validators.ts +151 -0
- package/src/plugins/ixo-chain/kinds/indexed/dao-proposals.ts +37 -2
- package/src/plugins/ixo-chain/kinds/indexed/daos.ts +145 -9
- package/src/plugins/yoma/engine.ts +107 -55
- package/src/plugins/yoma/submission.ts +3 -6
- package/src/plugins/yoma/yoma.plugin.ts +12 -3
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import type * as SurveyCore from 'survey-core';
|
|
2
|
+
import type {
|
|
3
|
+
ItemValue,
|
|
3
4
|
Model,
|
|
4
5
|
Question,
|
|
5
|
-
QuestionBooleanModel,
|
|
6
|
-
QuestionCheckboxModel,
|
|
7
|
-
QuestionFileModel,
|
|
8
6
|
QuestionPanelDynamicModel,
|
|
9
7
|
QuestionRatingModel,
|
|
10
8
|
QuestionSelectBase,
|
|
11
|
-
QuestionTextModel,
|
|
12
|
-
Serializer,
|
|
13
|
-
type ItemValue,
|
|
14
9
|
} from 'survey-core';
|
|
15
10
|
import { MAX_SECTION_ITEMS } from './drafts';
|
|
16
11
|
import type { ChoiceQuestion } from './judge';
|
|
@@ -37,45 +32,93 @@ const DEFAULT_GRID_PRECISION = 7;
|
|
|
37
32
|
/** Choices shown per question; a longer list is cut (Yoma forms have at most a few dozen). */
|
|
38
33
|
const MAX_OPTIONS = 60;
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Jambo's map question as a survey-core question type, registered with the
|
|
37
|
+
* loaded module's `Serializer` and `ElementFactory` so forms that use it
|
|
38
|
+
* open. Defined here rather than at module scope: a class needs its base
|
|
39
|
+
* class when it is defined.
|
|
40
|
+
*/
|
|
41
|
+
function defineMapGridQuestion(core: typeof SurveyCore) {
|
|
42
|
+
class QuestionMapGridSelectorModel extends core.Question {
|
|
43
|
+
override getType() {
|
|
44
|
+
return MAP_GRID_TYPE;
|
|
45
|
+
}
|
|
46
|
+
override isEmpty(): boolean {
|
|
47
|
+
const value: unknown = this.value;
|
|
48
|
+
if (value === null || value === undefined) return true;
|
|
49
|
+
return Array.isArray(value) ? value.length === 0 : false;
|
|
50
|
+
}
|
|
51
|
+
get gridPrecision(): number {
|
|
52
|
+
return (
|
|
53
|
+
Number(this.getPropertyValue('gridPrecision')) || DEFAULT_GRID_PRECISION
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
set gridPrecision(value: number) {
|
|
57
|
+
this.setPropertyValue('gridPrecision', value);
|
|
58
|
+
}
|
|
59
|
+
get allowMultiple(): boolean {
|
|
60
|
+
return this.getPropertyValue('allowMultiple') === true;
|
|
61
|
+
}
|
|
62
|
+
set allowMultiple(value: boolean) {
|
|
63
|
+
this.setPropertyValue('allowMultiple', value);
|
|
64
|
+
}
|
|
48
65
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
66
|
+
|
|
67
|
+
if (!core.Serializer.findClass(MAP_GRID_TYPE)) {
|
|
68
|
+
core.Serializer.addClass(
|
|
69
|
+
MAP_GRID_TYPE,
|
|
70
|
+
[
|
|
71
|
+
{ name: 'gridPrecision:number', default: DEFAULT_GRID_PRECISION },
|
|
72
|
+
{ name: 'allowMultiple:boolean', default: false },
|
|
73
|
+
],
|
|
74
|
+
() => new QuestionMapGridSelectorModel(''),
|
|
75
|
+
'question',
|
|
76
|
+
);
|
|
77
|
+
core.ElementFactory.Instance.registerElement(
|
|
78
|
+
MAP_GRID_TYPE,
|
|
79
|
+
(name) => new QuestionMapGridSelectorModel(name),
|
|
52
80
|
);
|
|
53
81
|
}
|
|
54
|
-
|
|
55
|
-
this.setPropertyValue('gridPrecision', value);
|
|
56
|
-
}
|
|
57
|
-
get allowMultiple(): boolean {
|
|
58
|
-
return this.getPropertyValue('allowMultiple') === true;
|
|
59
|
-
}
|
|
60
|
-
set allowMultiple(value: boolean) {
|
|
61
|
-
this.setPropertyValue('allowMultiple', value);
|
|
62
|
-
}
|
|
82
|
+
return QuestionMapGridSelectorModel;
|
|
63
83
|
}
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
/** survey-core once loaded, with the map question registered on it. */
|
|
86
|
+
export interface LoadedSurveyCore {
|
|
87
|
+
core: typeof SurveyCore;
|
|
88
|
+
MapGridQuestion: ReturnType<typeof defineMapGridQuestion>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let loaded: LoadedSurveyCore | undefined;
|
|
92
|
+
let loading: Promise<void> | undefined;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Loads `survey-core` on first use rather than when the Worker starts:
|
|
96
|
+
* evaluating it (2.5 MB, every question class registered as its module
|
|
97
|
+
* runs) at startup pushed an oracle with this plugin to the Workers
|
|
98
|
+
* startup-time limit. Every function of this module that opens or reads a
|
|
99
|
+
* form model needs it, so callers await this first; it loads once per
|
|
100
|
+
* isolate, and a failed load is tried again on the next call.
|
|
101
|
+
*/
|
|
102
|
+
export function loadSurveyCore(): Promise<void> {
|
|
103
|
+
loading ??= import('survey-core').then(
|
|
104
|
+
(core) => {
|
|
105
|
+
loaded = { core, MapGridQuestion: defineMapGridQuestion(core) };
|
|
106
|
+
},
|
|
107
|
+
(error: unknown) => {
|
|
108
|
+
loading = undefined;
|
|
109
|
+
throw error;
|
|
110
|
+
},
|
|
78
111
|
);
|
|
112
|
+
return loading;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** The loaded survey-core; throws when {@link loadSurveyCore} has not finished. */
|
|
116
|
+
export function surveyCore(): LoadedSurveyCore {
|
|
117
|
+
if (!loaded)
|
|
118
|
+
throw new Error(
|
|
119
|
+
'survey-core is not loaded: await loadSurveyCore() before using the form engine',
|
|
120
|
+
);
|
|
121
|
+
return loaded;
|
|
79
122
|
}
|
|
80
123
|
|
|
81
124
|
/** How a question is answered in chat. */
|
|
@@ -188,7 +231,8 @@ export function openModel(
|
|
|
188
231
|
template: unknown,
|
|
189
232
|
data?: Record<string, unknown>,
|
|
190
233
|
): Model {
|
|
191
|
-
const
|
|
234
|
+
const { core } = surveyCore();
|
|
235
|
+
const model = new core.Model(surveyJson(template));
|
|
192
236
|
if (data) model.data = structuredClone(data);
|
|
193
237
|
return model;
|
|
194
238
|
}
|
|
@@ -225,10 +269,11 @@ function numericRating(question: QuestionRatingModel): number[] | undefined {
|
|
|
225
269
|
}
|
|
226
270
|
|
|
227
271
|
function inputFor(question: Question): ChatInput | undefined {
|
|
228
|
-
|
|
272
|
+
const { core, MapGridQuestion } = surveyCore();
|
|
273
|
+
if (question instanceof core.QuestionTextModel)
|
|
229
274
|
return INPUT_BY_TEXT_TYPE[question.inputType] ?? 'text';
|
|
230
|
-
if (question instanceof
|
|
231
|
-
if (question instanceof QuestionRatingModel)
|
|
275
|
+
if (question instanceof MapGridQuestion) return 'location';
|
|
276
|
+
if (question instanceof core.QuestionRatingModel)
|
|
232
277
|
return numericRating(question) ? 'rating' : 'pick-one';
|
|
233
278
|
switch (question.getType()) {
|
|
234
279
|
case 'comment':
|
|
@@ -270,7 +315,7 @@ function specialItems(question: QuestionSelectBase): {
|
|
|
270
315
|
question.dontKnowItem,
|
|
271
316
|
];
|
|
272
317
|
const skipped = new Set<ItemValue>([...extras, question.otherItem]);
|
|
273
|
-
if (question instanceof QuestionCheckboxModel)
|
|
318
|
+
if (question instanceof surveyCore().core.QuestionCheckboxModel)
|
|
274
319
|
skipped.add(question.selectAllItem);
|
|
275
320
|
return { extras, other: question.otherItem, skipped };
|
|
276
321
|
}
|
|
@@ -287,6 +332,7 @@ function describe(
|
|
|
287
332
|
): FormQuestion | undefined {
|
|
288
333
|
const input = inputFor(question);
|
|
289
334
|
if (!input) return undefined;
|
|
335
|
+
const { core, MapGridQuestion } = surveyCore();
|
|
290
336
|
const view: FormQuestion = {
|
|
291
337
|
path,
|
|
292
338
|
name: question.name,
|
|
@@ -303,7 +349,7 @@ function describe(
|
|
|
303
349
|
required: question.isRequired,
|
|
304
350
|
...(item ? { item } : {}),
|
|
305
351
|
};
|
|
306
|
-
if (question instanceof QuestionSelectBase) {
|
|
352
|
+
if (question instanceof core.QuestionSelectBase) {
|
|
307
353
|
const { extras, other, skipped } = specialItems(question);
|
|
308
354
|
const shown = new Set<ItemValue>(question.visibleChoices);
|
|
309
355
|
const regular = question.visibleChoices.filter((c) => !skipped.has(c));
|
|
@@ -323,7 +369,7 @@ function describe(
|
|
|
323
369
|
value: other.value,
|
|
324
370
|
};
|
|
325
371
|
}
|
|
326
|
-
if (question instanceof QuestionBooleanModel)
|
|
372
|
+
if (question instanceof core.QuestionBooleanModel)
|
|
327
373
|
view.options = [
|
|
328
374
|
{
|
|
329
375
|
value: question.getValueTrue(),
|
|
@@ -334,7 +380,7 @@ function describe(
|
|
|
334
380
|
text: clip(question.labelFalse || 'No', 200),
|
|
335
381
|
},
|
|
336
382
|
];
|
|
337
|
-
if (question instanceof QuestionRatingModel) {
|
|
383
|
+
if (question instanceof core.QuestionRatingModel) {
|
|
338
384
|
const values = numericRating(question);
|
|
339
385
|
if (values) {
|
|
340
386
|
view.rateValues = values;
|
|
@@ -342,11 +388,11 @@ function describe(
|
|
|
342
388
|
view.rateMax = values[values.length - 1];
|
|
343
389
|
} else view.options = question.visibleRateValues.map(optionOf);
|
|
344
390
|
}
|
|
345
|
-
if (question instanceof QuestionFileModel) {
|
|
391
|
+
if (question instanceof core.QuestionFileModel) {
|
|
346
392
|
if (question.acceptedTypes) view.acceptedTypes = question.acceptedTypes;
|
|
347
393
|
view.allowMultiple = question.allowMultiple;
|
|
348
394
|
}
|
|
349
|
-
if (question instanceof
|
|
395
|
+
if (question instanceof MapGridQuestion)
|
|
350
396
|
view.allowMultiple = question.allowMultiple;
|
|
351
397
|
return view;
|
|
352
398
|
}
|
|
@@ -372,9 +418,10 @@ type Walked =
|
|
|
372
418
|
* where "add another?" is asked), never a question the form hides.
|
|
373
419
|
*/
|
|
374
420
|
function* walk(model: Model): Generator<Walked> {
|
|
421
|
+
const { core } = surveyCore();
|
|
375
422
|
for (const question of model.getAllQuestions(false)) {
|
|
376
423
|
if (!shown(question)) continue;
|
|
377
|
-
if (question instanceof QuestionPanelDynamicModel) {
|
|
424
|
+
if (question instanceof core.QuestionPanelDynamicModel) {
|
|
378
425
|
const title = sectionTitle(question);
|
|
379
426
|
for (const [index, panel] of question.panels.entries()) {
|
|
380
427
|
if (!panel.isVisible) continue;
|
|
@@ -433,7 +480,9 @@ function dynamicSection(
|
|
|
433
480
|
name: string,
|
|
434
481
|
): QuestionPanelDynamicModel | undefined {
|
|
435
482
|
const question = model.getQuestionByName(name);
|
|
436
|
-
return question instanceof QuestionPanelDynamicModel
|
|
483
|
+
return question instanceof surveyCore().core.QuestionPanelDynamicModel
|
|
484
|
+
? question
|
|
485
|
+
: undefined;
|
|
437
486
|
}
|
|
438
487
|
|
|
439
488
|
/** The live survey-core question at a path, or undefined when the form has none. */
|
|
@@ -572,7 +621,10 @@ export function setAnswer(
|
|
|
572
621
|
};
|
|
573
622
|
const before = structuredClone(dataOf(model));
|
|
574
623
|
question.value = value;
|
|
575
|
-
if (
|
|
624
|
+
if (
|
|
625
|
+
otherText !== undefined &&
|
|
626
|
+
question instanceof surveyCore().core.QuestionSelectBase
|
|
627
|
+
)
|
|
576
628
|
question.otherValue = otherText;
|
|
577
629
|
const errors = errorsFor(question);
|
|
578
630
|
if (errors.length) {
|
|
@@ -887,7 +939,7 @@ export function gridCell(
|
|
|
887
939
|
): Record<string, unknown> {
|
|
888
940
|
const question = questionAt(model, path);
|
|
889
941
|
const asked =
|
|
890
|
-
question instanceof
|
|
942
|
+
question instanceof surveyCore().MapGridQuestion
|
|
891
943
|
? question.gridPrecision
|
|
892
944
|
: DEFAULT_GRID_PRECISION;
|
|
893
945
|
const precision = Math.min(
|
|
@@ -5,17 +5,14 @@ import {
|
|
|
5
5
|
type ClaimSubmitOperation,
|
|
6
6
|
type SubmissionSummaryRow,
|
|
7
7
|
} from '@ixo/action-protocol';
|
|
8
|
-
import {
|
|
9
|
-
QuestionPanelDynamicModel,
|
|
10
|
-
type Model,
|
|
11
|
-
type Question,
|
|
12
|
-
} from 'survey-core';
|
|
8
|
+
import type { Model, Question } from 'survey-core';
|
|
13
9
|
import type { ChainCall } from '../ixo-chain/http';
|
|
14
10
|
import type { Draft, Evidence } from './drafts';
|
|
15
11
|
import {
|
|
16
12
|
clearHiddenAnswers,
|
|
17
13
|
questionAt,
|
|
18
14
|
submittableData,
|
|
15
|
+
surveyCore,
|
|
19
16
|
visibleQuestions,
|
|
20
17
|
type FormQuestion,
|
|
21
18
|
} from './engine';
|
|
@@ -187,7 +184,7 @@ function labelFor(
|
|
|
187
184
|
? `${titleOf(question)}${own && question.getValueName() === own ? ' (own answer)' : ''}`
|
|
188
185
|
: head;
|
|
189
186
|
if (
|
|
190
|
-
question instanceof QuestionPanelDynamicModel &&
|
|
187
|
+
question instanceof surveyCore().core.QuestionPanelDynamicModel &&
|
|
191
188
|
rest.length > 1 &&
|
|
192
189
|
/^\d+$/.test(rest[0]!)
|
|
193
190
|
) {
|
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
formErrors,
|
|
54
54
|
formTitle,
|
|
55
55
|
gridCell,
|
|
56
|
+
loadSurveyCore,
|
|
56
57
|
nextStep,
|
|
57
58
|
openModel,
|
|
58
59
|
parsePin,
|
|
@@ -748,7 +749,9 @@ type Applied =
|
|
|
748
749
|
* of logged work. Two tools; reads go through ixo-chain's bounded HTTP
|
|
749
750
|
* policy and never throw. Not bundled: a host adds `new YomaPlugin()` from
|
|
750
751
|
* `@ixo/oracle-runtime-workers/plugins/yoma`, so `survey-core` lands only
|
|
751
|
-
* in oracles that use it
|
|
752
|
+
* in oracles that use it, and even there it is loaded on the first Yoma
|
|
753
|
+
* tool call, not at Worker startup (both tools await `loadSurveyCore`
|
|
754
|
+
* before any form model is opened).
|
|
752
755
|
*/
|
|
753
756
|
export class YomaPlugin extends OraclePlugin {
|
|
754
757
|
readonly name = 'yoma';
|
|
@@ -771,7 +774,10 @@ export class YomaPlugin extends OraclePlugin {
|
|
|
771
774
|
].join(' '),
|
|
772
775
|
schema: lookupSchema,
|
|
773
776
|
effect: 'read',
|
|
774
|
-
handler: (input, ctx) =>
|
|
777
|
+
handler: async (input, ctx) => {
|
|
778
|
+
await loadSurveyCore();
|
|
779
|
+
return this.lookup(lookupSchema.parse(input), ctx);
|
|
780
|
+
},
|
|
775
781
|
},
|
|
776
782
|
{
|
|
777
783
|
name: 'yoma_form',
|
|
@@ -781,7 +787,10 @@ export class YomaPlugin extends OraclePlugin {
|
|
|
781
787
|
].join(' '),
|
|
782
788
|
schema: formSchema,
|
|
783
789
|
effect: 'write',
|
|
784
|
-
handler: (input, ctx) =>
|
|
790
|
+
handler: async (input, ctx) => {
|
|
791
|
+
await loadSurveyCore();
|
|
792
|
+
return this.form(formSchema.parse(input), ctx);
|
|
793
|
+
},
|
|
785
794
|
},
|
|
786
795
|
];
|
|
787
796
|
}
|