@tellegen/engine 0.2.0 → 0.4.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/README.md +71 -20
- package/dist/data/populated_places.json +1 -0
- package/dist/generated/{contracts.d.ts → api-types.d.ts} +26 -18
- package/dist/generated/api-types.d.ts.map +1 -0
- package/dist/generated/api-types.js +8 -0
- package/dist/generated/api-types.js.map +1 -0
- package/dist/generated/study-schema.d.ts +2 -0
- package/dist/generated/study-schema.d.ts.map +1 -0
- package/dist/generated/study-schema.js +3 -0
- package/dist/generated/study-schema.js.map +1 -0
- package/dist/generated/study-types.d.ts +426 -0
- package/dist/generated/study-types.d.ts.map +1 -0
- package/dist/generated/study-types.js +4 -0
- package/dist/generated/study-types.js.map +1 -0
- package/dist/generated/synth-schema.d.ts +2 -0
- package/dist/generated/synth-schema.d.ts.map +1 -0
- package/dist/generated/synth-schema.js +3 -0
- package/dist/generated/synth-schema.js.map +1 -0
- package/dist/generated/synth-types.d.ts +482 -0
- package/dist/generated/synth-types.d.ts.map +1 -0
- package/dist/generated/synth-types.js +4 -0
- package/dist/generated/synth-types.js.map +1 -0
- package/dist/host.d.ts +35 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +103 -17
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +549 -122
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +780 -109
- package/dist/index.js.map +1 -1
- package/dist/input-limit.d.ts +1 -1
- package/dist/input-limit.js +1 -1
- package/dist/module.d.ts +2 -0
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js.map +1 -1
- package/dist/protocol.d.ts +182 -31
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +153 -26
- package/dist/protocol.js.map +1 -1
- package/dist/study-document.d.ts +46 -0
- package/dist/study-document.d.ts.map +1 -0
- package/dist/study-document.js +183 -0
- package/dist/study-document.js.map +1 -0
- package/dist/study-document.test.d.ts +2 -0
- package/dist/study-document.test.d.ts.map +1 -0
- package/dist/study-document.test.js +119 -0
- package/dist/study-document.test.js.map +1 -0
- package/dist/wasm-pkg/LICENSE +17 -4
- package/dist/wasm-pkg/tellegen.d.ts +332 -85
- package/dist/wasm-pkg/tellegen.js +1324 -250
- package/dist/wasm-pkg/tellegen_bg.wasm +0 -0
- package/dist/wasm-pkg/tellegen_bg.wasm.d.ts +57 -9
- package/dist/worker.js +20 -5
- package/dist/worker.js.map +1 -1
- package/package.json +32 -11
- package/dist/generated/contracts.d.ts.map +0 -1
- package/dist/generated/contracts.js +0 -8
- package/dist/generated/contracts.js.map +0 -1
- package/dist/wasm-pkg/LICENSE-APACHE +0 -201
- package/dist/wasm-pkg/LICENSE-MIT +0 -19
package/dist/protocol.js
CHANGED
|
@@ -1,20 +1,59 @@
|
|
|
1
1
|
/** The engine call protocol: one request shape shared by the worker host
|
|
2
2
|
* (postMessage) and the main-thread host (direct dispatch), so a request built
|
|
3
3
|
* once runs on either side. Payloads are the wasm surface's own JSON strings
|
|
4
|
-
* (or raw display bytes
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* (or raw display bytes, or numeric arrays and synthetic previews that cross
|
|
5
|
+
* as transferable typed arrays); the typed translation stays in index.ts.
|
|
6
|
+
* Study and session handles are allocated by the caller, which keeps them
|
|
7
|
+
* valid when pending requests replay against the other host. */
|
|
8
|
+
/** The buffers an answer moves to the caller instead of copying. */
|
|
9
|
+
export function transferables(value) {
|
|
10
|
+
if (value instanceof Float64Array)
|
|
11
|
+
return [value.buffer];
|
|
12
|
+
if (value !== null && typeof value === "object" && value.kind === "synth_preview") {
|
|
13
|
+
return [
|
|
14
|
+
value.busPositions.buffer,
|
|
15
|
+
value.busKinds.buffer,
|
|
16
|
+
value.busKv.buffer,
|
|
17
|
+
value.branchPositions.buffer,
|
|
18
|
+
value.branchStartIndices.buffer,
|
|
19
|
+
value.branchKv.buffer,
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
/** An empty handle table for one side of the boundary. */
|
|
25
|
+
export function emptyHandles() {
|
|
26
|
+
return { studies: new Map(), mcPfSessions: new Map(), synthSessions: new Map() };
|
|
27
|
+
}
|
|
7
28
|
/** Run one request against a loaded wasm module. `studies` maps caller
|
|
8
29
|
* allocated handles to live wasm Study instances on this side of the
|
|
9
30
|
* boundary. */
|
|
10
|
-
export function runRequest(mod,
|
|
31
|
+
export function runRequest(mod, handles, req, cancelled = () => false) {
|
|
11
32
|
const study = (handle) => {
|
|
12
|
-
const s = studies.get(handle);
|
|
33
|
+
const s = handles.studies.get(handle);
|
|
13
34
|
if (!s)
|
|
14
35
|
throw new Error(`unknown study handle ${handle}`);
|
|
15
36
|
return s;
|
|
16
37
|
};
|
|
38
|
+
const mcPfSession = (handle) => {
|
|
39
|
+
const session = handles.mcPfSessions.get(handle);
|
|
40
|
+
if (!session)
|
|
41
|
+
throw new Error(`unknown multiconductor PF session handle ${handle}`);
|
|
42
|
+
return session;
|
|
43
|
+
};
|
|
44
|
+
const synthSession = (handle) => {
|
|
45
|
+
const session = handles.synthSessions.get(handle);
|
|
46
|
+
if (!session)
|
|
47
|
+
throw new Error(`unknown synthetic generation session handle ${handle}`);
|
|
48
|
+
return session;
|
|
49
|
+
};
|
|
17
50
|
switch (req.op) {
|
|
51
|
+
case "study_document_create":
|
|
52
|
+
return mod.study_document_create(req.request);
|
|
53
|
+
case "study_document_import":
|
|
54
|
+
return mod.study_document_import(req.bundle);
|
|
55
|
+
case "study_document_run":
|
|
56
|
+
return mod.study_document_run(req.bundle, req.request, cancelled);
|
|
18
57
|
case "preload":
|
|
19
58
|
return null; // loading the module was the work
|
|
20
59
|
case "classify_json":
|
|
@@ -23,10 +62,6 @@ export function runRequest(mod, studies, req) {
|
|
|
23
62
|
return mod.ingest_json_drop(req.bytes);
|
|
24
63
|
case "ingest_case":
|
|
25
64
|
return mod.ingest_case(req.bytes, req.format);
|
|
26
|
-
case "ingest_model_json":
|
|
27
|
-
return mod.ingest_model_json(req.network_json);
|
|
28
|
-
case "ingest_model_json_bytes":
|
|
29
|
-
return mod.ingest_model_json_bytes(req.bytes);
|
|
30
65
|
case "ingest_dist_case":
|
|
31
66
|
return mod.ingest_dist_case(req.text, req.format);
|
|
32
67
|
case "ingest_dist_case_bytes":
|
|
@@ -36,19 +71,69 @@ export function runRequest(mod, studies, req) {
|
|
|
36
71
|
case "parse_geo":
|
|
37
72
|
return mod.parse_geo(req.bytes, req.hint);
|
|
38
73
|
case "apply_geo":
|
|
39
|
-
return mod.apply_geo(req.
|
|
74
|
+
return mod.apply_geo(req.module_json, req.layer);
|
|
40
75
|
case "apply_layout":
|
|
41
|
-
return mod.apply_layout(req.
|
|
76
|
+
return mod.apply_layout(req.module_json, req.coords, req.kind);
|
|
42
77
|
case "extract_geo":
|
|
43
|
-
return mod.extract_geo(req.
|
|
78
|
+
return mod.extract_geo(req.module_json);
|
|
44
79
|
case "apply_display_geo":
|
|
45
|
-
return mod.apply_display_geo(req.
|
|
80
|
+
return mod.apply_display_geo(req.module_json, req.bytes);
|
|
46
81
|
case "capabilities":
|
|
47
82
|
return mod.capabilities_json();
|
|
48
|
-
case "
|
|
49
|
-
return mod.
|
|
83
|
+
case "solve_module":
|
|
84
|
+
return mod.solve_module(req.module_json, req.request);
|
|
85
|
+
case "solve_mc_bmopf":
|
|
86
|
+
return mod.solve_mc_bmopf(req.text, req.options);
|
|
87
|
+
case "solve_mc_module":
|
|
88
|
+
return mod.solve_mc_module(req.module_json, req.options);
|
|
89
|
+
case "solve_mc_study":
|
|
90
|
+
return mod.solve_mc_study(req.module_json, req.study_id, req.study_title, req.options);
|
|
91
|
+
case "replay_mc_study":
|
|
92
|
+
return mod.replay_mc_study(req.snapshot);
|
|
93
|
+
case "apply_mc_study_geo":
|
|
94
|
+
return mod.apply_mc_study_geo(req.snapshot, req.layer);
|
|
95
|
+
case "mc_pf_session_new": {
|
|
96
|
+
const session = new mod.McPfSession(req.module_json, req.options);
|
|
97
|
+
handles.mcPfSessions.set(req.session, session);
|
|
98
|
+
return session.summary();
|
|
99
|
+
}
|
|
100
|
+
case "mc_pf_session_result":
|
|
101
|
+
return mcPfSession(req.session).result();
|
|
102
|
+
case "mc_pf_session_summary":
|
|
103
|
+
return mcPfSession(req.session).summary();
|
|
104
|
+
case "mc_pf_session_detail":
|
|
105
|
+
return mcPfSession(req.session).detail(req.query);
|
|
106
|
+
case "mc_pf_session_terminal_ids":
|
|
107
|
+
return mcPfSession(req.session).terminal_ids();
|
|
108
|
+
case "mc_pf_session_terminal_voltages":
|
|
109
|
+
return mcPfSession(req.session).terminal_voltages();
|
|
110
|
+
case "mc_pf_session_terminal_currents":
|
|
111
|
+
return mcPfSession(req.session).terminal_currents();
|
|
112
|
+
case "mc_pf_session_profile":
|
|
113
|
+
return req.cold
|
|
114
|
+
? mcPfSession(req.session).cold_profile()
|
|
115
|
+
: mcPfSession(req.session).profile();
|
|
116
|
+
case "mc_pf_session_materialization_count":
|
|
117
|
+
return String(mcPfSession(req.session).materialization_count());
|
|
118
|
+
case "mc_pf_session_load_branches":
|
|
119
|
+
return mcPfSession(req.session).load_branches();
|
|
120
|
+
case "mc_pf_session_replace_load_powers":
|
|
121
|
+
return mcPfSession(req.session).replace_load_powers(req.edits);
|
|
122
|
+
case "mc_pf_session_input_module":
|
|
123
|
+
return mcPfSession(req.session).input_module();
|
|
124
|
+
case "mc_pf_session_snapshot":
|
|
125
|
+
return mcPfSession(req.session).snapshot(req.study_id, req.study_title);
|
|
126
|
+
case "mc_pf_session_free":
|
|
127
|
+
handles.mcPfSessions.get(req.session)?.free();
|
|
128
|
+
handles.mcPfSessions.delete(req.session);
|
|
129
|
+
return null;
|
|
130
|
+
case "memory_stats":
|
|
131
|
+
return mod.memory_stats();
|
|
132
|
+
case "reset_peak_memory":
|
|
133
|
+
mod.reset_peak_memory();
|
|
134
|
+
return null;
|
|
50
135
|
case "study_new":
|
|
51
|
-
studies.set(req.study, new mod.Study(req.
|
|
136
|
+
handles.studies.set(req.study, new mod.Study(req.module_json, req.formulation));
|
|
52
137
|
return null;
|
|
53
138
|
case "study_replace_edits":
|
|
54
139
|
return study(req.study).replace_edits(req.edits, req.sensitivities);
|
|
@@ -56,20 +141,62 @@ export function runRequest(mod, studies, req) {
|
|
|
56
141
|
return study(req.study).preview_replacement(req.edits, req.operands);
|
|
57
142
|
case "study_solution":
|
|
58
143
|
return study(req.study).solution();
|
|
59
|
-
case "
|
|
60
|
-
return study(req.study).
|
|
144
|
+
case "study_plan":
|
|
145
|
+
return study(req.study).plan(req.spec);
|
|
146
|
+
case "study_save_instance_module":
|
|
147
|
+
return study(req.study).save_instance_module();
|
|
148
|
+
case "study_save_module":
|
|
149
|
+
return study(req.study).save_module();
|
|
150
|
+
case "study_save_solution_module":
|
|
151
|
+
return study(req.study).save_solution_module();
|
|
152
|
+
case "study_export":
|
|
153
|
+
return study(req.study).export(req.format);
|
|
61
154
|
case "study_apply_geo":
|
|
62
155
|
return study(req.study).apply_geo(req.layer);
|
|
63
|
-
case "load_package":
|
|
64
|
-
return mod.load_package(req.text);
|
|
65
|
-
case "load_package_bytes":
|
|
66
|
-
return mod.load_package_bytes(req.bytes);
|
|
67
|
-
case "export_study":
|
|
68
|
-
return mod.export_study(req.package_json, req.commit, req.format);
|
|
69
156
|
case "study_free":
|
|
70
|
-
studies.get(req.study)?.free();
|
|
71
|
-
studies.delete(req.study);
|
|
157
|
+
handles.studies.get(req.study)?.free();
|
|
158
|
+
handles.studies.delete(req.study);
|
|
159
|
+
return null;
|
|
160
|
+
case "synth_load_places":
|
|
161
|
+
return mod.synth_load_places(req.bytes);
|
|
162
|
+
case "synth_overpass_queries":
|
|
163
|
+
return mod.synth_overpass_queries(req.request);
|
|
164
|
+
case "synth_session_new":
|
|
165
|
+
handles.synthSessions.set(req.session, new mod.SynthSession(req.request, req.osm ?? undefined));
|
|
166
|
+
return null;
|
|
167
|
+
case "synth_session_advance":
|
|
168
|
+
return synthSession(req.session).advance();
|
|
169
|
+
case "synth_session_preview": {
|
|
170
|
+
const buffers = synthSession(req.session).preview_buffers();
|
|
171
|
+
try {
|
|
172
|
+
return {
|
|
173
|
+
kind: "synth_preview",
|
|
174
|
+
busPositions: buffers.bus_positions(),
|
|
175
|
+
busKinds: buffers.bus_kinds(),
|
|
176
|
+
busKv: buffers.bus_kv(),
|
|
177
|
+
branchPositions: buffers.branch_positions(),
|
|
178
|
+
branchStartIndices: buffers.branch_start_indices(),
|
|
179
|
+
branchKv: buffers.branch_kv(),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
buffers.free();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
case "synth_session_report":
|
|
187
|
+
return synthSession(req.session).report();
|
|
188
|
+
case "synth_session_module":
|
|
189
|
+
return synthSession(req.session).module();
|
|
190
|
+
case "synth_session_ingest":
|
|
191
|
+
return synthSession(req.session).ingest();
|
|
192
|
+
case "synth_session_export_gridfm":
|
|
193
|
+
return synthSession(req.session).export_gridfm();
|
|
194
|
+
case "synth_session_free":
|
|
195
|
+
handles.synthSessions.get(req.session)?.free();
|
|
196
|
+
handles.synthSessions.delete(req.session);
|
|
72
197
|
return null;
|
|
198
|
+
case "synth_generate":
|
|
199
|
+
return mod.synth_generate(req.request, req.osm ?? undefined);
|
|
73
200
|
}
|
|
74
201
|
}
|
|
75
202
|
//# sourceMappingURL=protocol.js.map
|
package/dist/protocol.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { CreateStudy, StudyBundle, StudyRequest, StudyOperationResult } from "./generated/study-types.js";
|
|
2
|
+
export type { CreateStudy, StudyBundle, StudyDocument, StudyRequest, StudyOperation, StudyOperationResult, GoalRevision, DecisionSpace, StudyObjective, StateNode, Comparison } from "./generated/study-types.js";
|
|
3
|
+
export interface StudyBackend {
|
|
4
|
+
create(request: CreateStudy, signal?: AbortSignal): Promise<StudyBundle>;
|
|
5
|
+
validate(text: string): Promise<StudyBundle>;
|
|
6
|
+
execute(bundle: StudyBundle, request: StudyRequest, signal?: AbortSignal): Promise<{
|
|
7
|
+
bundle: StudyBundle;
|
|
8
|
+
result: StudyOperationResult;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
export declare const studyBackend: StudyBackend;
|
|
12
|
+
export interface StudyStore {
|
|
13
|
+
load(id: string): Promise<StudyBundle | null>;
|
|
14
|
+
create(bundle: StudyBundle): Promise<void>;
|
|
15
|
+
commit(expectedRevision: number, bundle: StudyBundle): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/** IndexedDB commits the complete document and its deduplicated artifact map together. */
|
|
18
|
+
export declare class IndexedDbStudyStore implements StudyStore {
|
|
19
|
+
#private;
|
|
20
|
+
constructor(name?: string);
|
|
21
|
+
load(id: string): Promise<StudyBundle | null>;
|
|
22
|
+
list(): Promise<Array<{
|
|
23
|
+
id: string;
|
|
24
|
+
title: string;
|
|
25
|
+
revision: number;
|
|
26
|
+
}>>;
|
|
27
|
+
create(bundle: StudyBundle): Promise<void>;
|
|
28
|
+
commit(expectedRevision: number, bundle: StudyBundle): Promise<void>;
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/** Browser controls and agent tools share serialized, durable Study mutations. */
|
|
32
|
+
export declare class StudyDocumentController {
|
|
33
|
+
#private;
|
|
34
|
+
private constructor();
|
|
35
|
+
static create(request: CreateStudy, store: StudyStore, backend?: StudyBackend, signal?: AbortSignal): Promise<StudyDocumentController>;
|
|
36
|
+
static open(id: string, store: StudyStore, backend?: StudyBackend): Promise<StudyDocumentController>;
|
|
37
|
+
static import(text: string, store: StudyStore, backend?: StudyBackend): Promise<StudyDocumentController>;
|
|
38
|
+
get bundle(): StudyBundle;
|
|
39
|
+
export(): string;
|
|
40
|
+
execute(request: StudyRequest, signal?: AbortSignal): Promise<StudyOperationResult>;
|
|
41
|
+
/** Call only from the user's approval action; tokens stay in this controller. */
|
|
42
|
+
recordUserApproval(proposal: string): string;
|
|
43
|
+
isApprovalCurrent(token: string): boolean;
|
|
44
|
+
applyApprovedProposal(token: string, signal?: AbortSignal): Promise<StudyOperationResult>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=study-document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"study-document.d.ts","sourceRoot":"","sources":["../src/study-document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { isolatedEngineHost } from "./host.js";
|
|
2
|
+
async function callIsolated(request, signal) {
|
|
3
|
+
signal?.throwIfAborted();
|
|
4
|
+
const host = isolatedEngineHost();
|
|
5
|
+
const cooperative = request.op === "study_document_run";
|
|
6
|
+
const cancel = () => cooperative ? host.requestStop?.() : host.cancel?.(new DOMException("Study operation cancelled", "AbortError"));
|
|
7
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
8
|
+
try {
|
|
9
|
+
const result = await host.call(request);
|
|
10
|
+
if (!cooperative)
|
|
11
|
+
signal?.throwIfAborted();
|
|
12
|
+
if (typeof result !== "string")
|
|
13
|
+
throw new Error("The engine returned no Study result");
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
signal?.removeEventListener("abort", cancel);
|
|
18
|
+
host.cancel?.(new Error("Study operation completed"));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export const studyBackend = {
|
|
22
|
+
async create(request, signal) { return JSON.parse(await callIsolated({ op: "study_document_create", request: JSON.stringify(request) }, signal)); },
|
|
23
|
+
async validate(text) { return JSON.parse(await callIsolated({ op: "study_document_import", bundle: text })); },
|
|
24
|
+
async execute(bundle, request, signal) {
|
|
25
|
+
return JSON.parse(await callIsolated({ op: "study_document_run", bundle: JSON.stringify(bundle), request: JSON.stringify(request) }, signal));
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
function storageError(error) {
|
|
29
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
30
|
+
return new Error(`Study was not saved: ${cause}. Free browser storage or export the Study and choose another destination before retrying.`);
|
|
31
|
+
}
|
|
32
|
+
/** IndexedDB commits the complete document and its deduplicated artifact map together. */
|
|
33
|
+
export class IndexedDbStudyStore {
|
|
34
|
+
#database;
|
|
35
|
+
constructor(name = "tellegen-studies") {
|
|
36
|
+
this.#database = new Promise((resolve, reject) => {
|
|
37
|
+
const request = indexedDB.open(name, 1);
|
|
38
|
+
request.onupgradeneeded = () => request.result.createObjectStore("studies", { keyPath: "document.id" });
|
|
39
|
+
request.onsuccess = () => { request.result.onversionchange = () => request.result.close(); resolve(request.result); };
|
|
40
|
+
request.onerror = () => reject(storageError(request.error));
|
|
41
|
+
request.onblocked = () => reject(new Error("Study storage is blocked by another tab; close its connection and reload"));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async load(id) {
|
|
45
|
+
const db = await this.#database;
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const tx = db.transaction("studies", "readonly");
|
|
48
|
+
const request = tx.objectStore("studies").get(id);
|
|
49
|
+
request.onsuccess = () => resolve(request.result ?? null);
|
|
50
|
+
request.onerror = () => reject(storageError(request.error));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async list() {
|
|
54
|
+
const db = await this.#database;
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const rows = [];
|
|
57
|
+
const tx = db.transaction("studies", "readonly");
|
|
58
|
+
const request = tx.objectStore("studies").openCursor();
|
|
59
|
+
request.onsuccess = () => {
|
|
60
|
+
const cursor = request.result;
|
|
61
|
+
if (!cursor)
|
|
62
|
+
return;
|
|
63
|
+
const { id, title, revision } = cursor.value.document;
|
|
64
|
+
rows.push({ id, title, revision });
|
|
65
|
+
cursor.continue();
|
|
66
|
+
};
|
|
67
|
+
tx.oncomplete = () => resolve(rows);
|
|
68
|
+
tx.onabort = tx.onerror = () => reject(storageError(tx.error));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async create(bundle) { return this.#write(null, bundle); }
|
|
72
|
+
async commit(expectedRevision, bundle) { return this.#write(expectedRevision, bundle); }
|
|
73
|
+
async #write(expected, bundle) {
|
|
74
|
+
const db = await this.#database;
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
const tx = db.transaction("studies", "readwrite");
|
|
77
|
+
const store = tx.objectStore("studies");
|
|
78
|
+
let conflict;
|
|
79
|
+
const request = store.get(bundle.document.id);
|
|
80
|
+
request.onsuccess = () => {
|
|
81
|
+
const current = request.result;
|
|
82
|
+
if (expected === null ? current !== undefined : current?.document.revision !== expected || bundle.document.revision !== expected + 1) {
|
|
83
|
+
conflict = new Error("Study storage revision changed; reload the saved Study before retrying");
|
|
84
|
+
tx.abort();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
store.put(bundle);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
conflict = storageError(error);
|
|
92
|
+
tx.abort();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
tx.oncomplete = () => resolve();
|
|
96
|
+
tx.onabort = () => reject(conflict ?? storageError(tx.error));
|
|
97
|
+
tx.onerror = () => reject(conflict ?? storageError(tx.error));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async close() { (await this.#database).close(); }
|
|
101
|
+
}
|
|
102
|
+
/** Browser controls and agent tools share serialized, durable Study mutations. */
|
|
103
|
+
export class StudyDocumentController {
|
|
104
|
+
#bundle;
|
|
105
|
+
#store;
|
|
106
|
+
#backend;
|
|
107
|
+
#queue = Promise.resolve();
|
|
108
|
+
#approvals = new Map();
|
|
109
|
+
constructor(bundle, store, backend) {
|
|
110
|
+
this.#bundle = bundle;
|
|
111
|
+
this.#store = store;
|
|
112
|
+
this.#backend = backend;
|
|
113
|
+
}
|
|
114
|
+
static async create(request, store, backend = studyBackend, signal) {
|
|
115
|
+
const bundle = await backend.create(request, signal);
|
|
116
|
+
signal?.throwIfAborted();
|
|
117
|
+
await store.create(bundle);
|
|
118
|
+
return new StudyDocumentController(bundle, store, backend);
|
|
119
|
+
}
|
|
120
|
+
static async open(id, store, backend = studyBackend) {
|
|
121
|
+
const saved = await store.load(id);
|
|
122
|
+
if (!saved)
|
|
123
|
+
throw new Error(`Study ${id} is unavailable`);
|
|
124
|
+
const bundle = await backend.validate(JSON.stringify(saved));
|
|
125
|
+
return new StudyDocumentController(bundle, store, backend);
|
|
126
|
+
}
|
|
127
|
+
static async import(text, store, backend = studyBackend) {
|
|
128
|
+
const bundle = await backend.validate(text);
|
|
129
|
+
await store.create(bundle);
|
|
130
|
+
return new StudyDocumentController(bundle, store, backend);
|
|
131
|
+
}
|
|
132
|
+
get bundle() { return structuredClone(this.#bundle); }
|
|
133
|
+
export() { return JSON.stringify(this.#bundle); }
|
|
134
|
+
execute(request, signal) {
|
|
135
|
+
if (request.operation.kind === "apply")
|
|
136
|
+
return Promise.reject(new Error("Apply requires explicit user approval of the current proposal"));
|
|
137
|
+
return this.#execute(request, signal);
|
|
138
|
+
}
|
|
139
|
+
#execute(request, signal) {
|
|
140
|
+
const queuedRequest = structuredClone(request);
|
|
141
|
+
const operation = this.#queue.then(async () => {
|
|
142
|
+
signal?.throwIfAborted();
|
|
143
|
+
if (queuedRequest.expected_revision !== this.#bundle.document.revision)
|
|
144
|
+
throw new Error("Stale Study revision; inspect the current Study and retry");
|
|
145
|
+
const completed = await this.#backend.execute(structuredClone(this.#bundle), queuedRequest, signal);
|
|
146
|
+
if (queuedRequest.operation.kind !== "propose")
|
|
147
|
+
signal?.throwIfAborted();
|
|
148
|
+
await this.#store.commit(queuedRequest.expected_revision, completed.bundle);
|
|
149
|
+
this.#bundle = completed.bundle;
|
|
150
|
+
if (["inspect", "compare", "record_evidence"].includes(queuedRequest.operation.kind)) {
|
|
151
|
+
for (const approval of this.#approvals.values())
|
|
152
|
+
approval.revision = completed.bundle.document.revision;
|
|
153
|
+
}
|
|
154
|
+
else
|
|
155
|
+
this.#approvals.clear();
|
|
156
|
+
return completed.result;
|
|
157
|
+
});
|
|
158
|
+
this.#queue = operation.catch(() => undefined);
|
|
159
|
+
return operation;
|
|
160
|
+
}
|
|
161
|
+
/** Call only from the user's approval action; tokens stay in this controller. */
|
|
162
|
+
recordUserApproval(proposal) {
|
|
163
|
+
const d = this.#bundle.document;
|
|
164
|
+
const experiment = d.experiments[proposal];
|
|
165
|
+
if (!experiment || !["planning", "counterfactual"].includes(experiment.kind) || !experiment.start_state || experiment.goal !== d.active_goal || !d.recommended_state || !experiment.result_states.includes(d.recommended_state))
|
|
166
|
+
throw new Error("The proposal no longer matches the active goal and recommendation");
|
|
167
|
+
const token = crypto.randomUUID();
|
|
168
|
+
this.#approvals.set(token, { proposal, state: d.recommended_state, goal: d.active_goal ?? null, base: experiment.start_state, revision: d.revision });
|
|
169
|
+
return token;
|
|
170
|
+
}
|
|
171
|
+
isApprovalCurrent(token) {
|
|
172
|
+
const approval = this.#approvals.get(token), d = this.#bundle.document;
|
|
173
|
+
return !!approval && d.revision === approval.revision && d.active_goal === approval.goal &&
|
|
174
|
+
d.recommended_state === approval.state && d.experiments[approval.proposal]?.start_state === approval.base;
|
|
175
|
+
}
|
|
176
|
+
applyApprovedProposal(token, signal) {
|
|
177
|
+
const approval = this.#approvals.get(token);
|
|
178
|
+
if (!approval || !this.isApprovalCurrent(token))
|
|
179
|
+
return Promise.reject(new Error("Proposal approval expired; review the current proposal again"));
|
|
180
|
+
return this.#execute({ expected_revision: approval.revision, operation: { kind: "apply", proposal: approval.proposal, state: approval.state, goal: approval.goal, base_state: approval.base } }, signal);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=study-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"study-document.js","sourceRoot":"","sources":["../src/study-document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"study-document.test.d.ts","sourceRoot":"","sources":["../src/study-document.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { StudyDocumentController } from "./study-document.js";
|
|
3
|
+
const input = { id: "s", title: "Study", input: "", formulation: "dcopf", request: "Lower prices", interpretation: "Weighted prices",
|
|
4
|
+
objective: { kind: "weighted_observable", operand: { Price: "Active" }, weights: [{ element: 2, weight: 1 }] },
|
|
5
|
+
decisions: { variables: [{ id: "line", element: 1, intervention: "branch_rating", lower: 0, upper: 10, increment: 1, budget_weight: 1 }], total_budget: 10, max_changed_elements: 1 } };
|
|
6
|
+
function fixture() {
|
|
7
|
+
return { document: { schema: "tellegen-study", version: 1, id: "s", title: "Study", revision: 0,
|
|
8
|
+
goals: {}, states: {}, experiments: { p: { kind: "planning", start_state: "base", goal: "g", rationale: "Test", evidence: [], trials: [], result_states: ["candidate"], solve_count: 1, termination: "completed" } },
|
|
9
|
+
experiment_order: ["p"], decisions: {}, active_goal: "g", inspected_state: "base", applied_state: "base", recommended_state: "candidate" }, artifacts: {} };
|
|
10
|
+
}
|
|
11
|
+
class Store {
|
|
12
|
+
saved = null;
|
|
13
|
+
fail = false;
|
|
14
|
+
async load() { return structuredClone(this.saved); }
|
|
15
|
+
async create(bundle) { this.saved = structuredClone(bundle); }
|
|
16
|
+
async commit(expected, bundle) {
|
|
17
|
+
if (this.fail)
|
|
18
|
+
throw new Error("QuotaExceededError: free storage");
|
|
19
|
+
if (this.saved?.document.revision !== expected)
|
|
20
|
+
throw new Error("stale stored revision");
|
|
21
|
+
this.saved = structuredClone(bundle);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function backend() {
|
|
25
|
+
return {
|
|
26
|
+
create: async () => fixture(), validate: async (text) => JSON.parse(text),
|
|
27
|
+
execute: vi.fn(async (bundle, request) => {
|
|
28
|
+
bundle.document.revision++;
|
|
29
|
+
if (request.operation.kind === "inspect")
|
|
30
|
+
bundle.document.inspected_state = request.operation.state;
|
|
31
|
+
if (request.operation.kind === "apply")
|
|
32
|
+
bundle.document.applied_state = request.operation.state;
|
|
33
|
+
const result = { summary: { id: "s", title: "Study", revision: bundle.document.revision, state_count: 0, experiment_count: 1, recent_experiments: [], unavailable_historical_states: 0 }, experiment: null, comparison: null, inspected_view: null };
|
|
34
|
+
return { bundle, result };
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
describe("durable Study controller", () => {
|
|
39
|
+
it("publishes completed mutations only after storage succeeds", async () => {
|
|
40
|
+
const store = new Store();
|
|
41
|
+
const engine = backend();
|
|
42
|
+
const controller = await StudyDocumentController.create(input, store, engine);
|
|
43
|
+
store.fail = true;
|
|
44
|
+
await expect(controller.execute({ expected_revision: 0, operation: { kind: "inspect", state: "candidate" } })).rejects.toThrow("QuotaExceededError");
|
|
45
|
+
expect(controller.bundle.document.revision).toBe(0);
|
|
46
|
+
expect(controller.bundle.document.inspected_state).toBe("base");
|
|
47
|
+
expect(store.saved?.document.revision).toBe(0);
|
|
48
|
+
store.fail = false;
|
|
49
|
+
await controller.execute({ expected_revision: 0, operation: { kind: "inspect", state: "candidate" } });
|
|
50
|
+
expect(controller.bundle.document.applied_state).toBe("base");
|
|
51
|
+
expect(store.saved?.document.inspected_state).toBe("candidate");
|
|
52
|
+
});
|
|
53
|
+
it("serializes mutations and rejects requests based on stale state", async () => {
|
|
54
|
+
const store = new Store();
|
|
55
|
+
const engine = backend();
|
|
56
|
+
const controller = await StudyDocumentController.create(input, store, engine);
|
|
57
|
+
const first = controller.execute({ expected_revision: 0, operation: { kind: "inspect", state: "candidate" } });
|
|
58
|
+
const second = controller.execute({ expected_revision: 0, operation: { kind: "inspect", state: "base" } });
|
|
59
|
+
await first;
|
|
60
|
+
await expect(second).rejects.toThrow("Stale Study revision");
|
|
61
|
+
expect(engine.execute).toHaveBeenCalledTimes(1);
|
|
62
|
+
});
|
|
63
|
+
it("binds explicit approval to a proposal and never restores approval on reload", async () => {
|
|
64
|
+
const store = new Store();
|
|
65
|
+
const engine = backend();
|
|
66
|
+
const controller = await StudyDocumentController.create(input, store, engine);
|
|
67
|
+
const token = controller.recordUserApproval("p");
|
|
68
|
+
const restored = await StudyDocumentController.open("s", store, engine);
|
|
69
|
+
await expect(restored.applyApprovedProposal(token)).rejects.toThrow("expired");
|
|
70
|
+
await expect(controller.execute({ expected_revision: 0, operation: { kind: "apply", proposal: "p", goal: "g", base_state: "base", state: "candidate" } })).rejects.toThrow("explicit user approval");
|
|
71
|
+
await controller.applyApprovedProposal(token);
|
|
72
|
+
expect(controller.bundle.document.applied_state).toBe("candidate");
|
|
73
|
+
expect(controller.export()).not.toContain(token);
|
|
74
|
+
await expect(controller.applyApprovedProposal(token)).rejects.toThrow("expired");
|
|
75
|
+
});
|
|
76
|
+
it("saves completed planning evidence when cancellation ends an active proposal", async () => {
|
|
77
|
+
const store = new Store();
|
|
78
|
+
const engine = backend();
|
|
79
|
+
const cancel = new AbortController();
|
|
80
|
+
const execute = engine.execute;
|
|
81
|
+
engine.execute = async (bundle, request, signal) => {
|
|
82
|
+
cancel.abort();
|
|
83
|
+
const completed = await execute(bundle, request, signal);
|
|
84
|
+
completed.bundle.document.experiments.p.termination = "cancelled";
|
|
85
|
+
return completed;
|
|
86
|
+
};
|
|
87
|
+
const controller = await StudyDocumentController.create(input, store, engine);
|
|
88
|
+
await controller.execute({ expected_revision: 0, operation: { kind: "propose", state: "base", goal: "g", rationale: "Explore", options: { max_solves: 5, beam_width: 2, max_iterations: 2, min_improvement: 1e-7 } } }, cancel.signal);
|
|
89
|
+
expect(controller.bundle.document.revision).toBe(1);
|
|
90
|
+
expect(store.saved?.document.experiments.p.termination).toBe("cancelled");
|
|
91
|
+
expect(store.saved?.document.applied_state).toBe("base");
|
|
92
|
+
});
|
|
93
|
+
it("invalidates approval when branching and rejects pre-cancelled work", async () => {
|
|
94
|
+
const store = new Store();
|
|
95
|
+
const engine = backend();
|
|
96
|
+
const controller = await StudyDocumentController.create(input, store, engine);
|
|
97
|
+
const token = controller.recordUserApproval("p");
|
|
98
|
+
await controller.execute({ expected_revision: 0, operation: { kind: "branch", state: "candidate", rationale: "Explore this branch" } });
|
|
99
|
+
await expect(controller.applyApprovedProposal(token)).rejects.toThrow("expired");
|
|
100
|
+
const signal = AbortSignal.abort();
|
|
101
|
+
await expect(controller.execute({ expected_revision: 1, operation: { kind: "inspect", state: "base" } }, signal)).rejects.toThrow();
|
|
102
|
+
expect(controller.bundle.document.revision).toBe(1);
|
|
103
|
+
});
|
|
104
|
+
it("keeps approval through inspection and retries a failed durable apply", async () => {
|
|
105
|
+
const store = new Store(), engine = backend();
|
|
106
|
+
const controller = await StudyDocumentController.create(input, store, engine);
|
|
107
|
+
const token = controller.recordUserApproval("p");
|
|
108
|
+
await controller.execute({ expected_revision: 0, operation: { kind: "inspect", state: "candidate" } });
|
|
109
|
+
expect(controller.isApprovalCurrent(token)).toBe(true);
|
|
110
|
+
store.fail = true;
|
|
111
|
+
await expect(controller.applyApprovedProposal(token)).rejects.toThrow("QuotaExceededError");
|
|
112
|
+
expect(controller.bundle.document.applied_state).toBe("base");
|
|
113
|
+
expect(controller.isApprovalCurrent(token)).toBe(true);
|
|
114
|
+
store.fail = false;
|
|
115
|
+
await controller.applyApprovedProposal(token);
|
|
116
|
+
expect(controller.bundle.document.applied_state).toBe("candidate");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=study-document.test.js.map
|