@rsconcept/rstool 0.10.3 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -33
- package/dist/agent-workflow-Gk0Vfnv1.d.ts +64 -0
- package/dist/analysis-LLnPhmGa.d.ts +23 -0
- package/dist/{common-DxLg3eXX.d.ts → common-DHJalS-Q.d.ts} +6 -1
- package/dist/constituenta-DnGR6bnM.d.ts +54 -0
- package/dist/diagnostic-D9yl_mEL.d.ts +19 -0
- package/dist/evaluation-Cns8BFm4.d.ts +31 -0
- package/dist/index.d.ts +11 -11
- package/dist/index.js +1 -2
- package/dist/mappers/model-adapter.d.ts +3 -3
- package/dist/mappers/schema-adapter.d.ts +4 -4
- package/dist/mappers/types.d.ts +6 -2
- package/dist/mappers/types.js +2 -0
- package/dist/mappers/types.js.map +1 -1
- package/dist/{model-value-SFAVj0dw.d.ts → model-value-BbonPzMz.d.ts} +14 -3
- package/dist/models/agent-workflow.d.ts +2 -0
- package/dist/models/agent-workflow.js +1 -0
- package/dist/models/analysis.d.ts +1 -1
- package/dist/models/common.d.ts +1 -1
- package/dist/models/constituenta.d.ts +2 -2
- package/dist/models/diagnostic.d.ts +1 -1
- package/dist/models/evaluation.d.ts +2 -2
- package/dist/models/index.d.ts +11 -11
- package/dist/models/index.js +2 -2
- package/dist/models/model-value.d.ts +2 -2
- package/dist/models/rstool-agent.d.ts +1 -1
- package/dist/models/rstool-agent.js +1 -1
- package/dist/models/session.d.ts +1 -1
- package/dist/models/tool-contract.d.ts +2 -2
- package/dist/models/tool-contract.js +2 -1
- package/dist/models/tool-contract.js.map +1 -1
- package/dist/models-Bw6Uum8i.js +685 -0
- package/dist/models-Bw6Uum8i.js.map +1 -0
- package/dist/rstool-agent-D2cQze_b.d.ts +71 -0
- package/dist/session/session-store.d.ts +18 -5
- package/dist/session/session-store.js +1 -64
- package/dist/{session-BPgsE80c.d.ts → session-ChexW8i7.d.ts} +11 -8
- package/dist/tool-contract-0uRGhEfW.d.ts +164 -0
- package/dist/wrapper/client.d.ts +23 -0
- package/dist/wrapper/client.js +17 -0
- package/dist/wrapper/client.js.map +1 -1
- package/dist/wrapper/stdio-wrapper.js +75 -63
- package/dist/wrapper/stdio-wrapper.js.map +1 -1
- package/docs/CONSTITUENTA.md +2 -2
- package/docs/DIAGNOSTICS.md +6 -5
- package/docs/MODEL-TESTING.md +3 -3
- package/docs/PORTAL-API.md +24 -18
- package/examples/README.md +1 -1
- package/examples/agent-client.ts +11 -41
- package/examples/build-chocolate-nim-rsform.ts +21 -70
- package/examples/chocolate-nim/build-rsform.ts +23 -18
- package/examples/chocolate-nim/build-rsmodel.ts +10 -12
- package/examples/chocolate-nim/rsform-session.json +290 -290
- package/examples/chocolate-nim/rsmodel-session.json +291 -291
- package/examples/expression-bank/bank-constituents.ts +304 -53
- package/examples/expression-bank/build-rsform.ts +19 -16
- package/examples/expression-bank/rsform-session.json +1551 -1551
- package/examples/kinship/build-rsform.ts +23 -18
- package/examples/kinship/build-rsmodel.ts +16 -16
- package/examples/kinship/rsform-session.json +219 -219
- package/examples/kinship/rsmodel-session.json +221 -221
- package/examples/kinship/session.ts +19 -21
- package/examples/movd/build-rsform.ts +23 -18
- package/examples/movd/build-rsmodel.ts +18 -20
- package/examples/movd/rsform-session.json +262 -262
- package/examples/movd/rsmodel-session.json +264 -264
- package/examples/sample/build-rsform.ts +18 -51
- package/examples/sample/build-rsmodel.ts +25 -44
- package/examples/sample/rsform-session.json +10 -7
- package/examples/sample/rsmodel-session.json +36 -33
- package/examples/template-apply/build-rsform.ts +27 -24
- package/examples/template-apply/rsform-session.json +48 -48
- package/package.json +4 -2
- package/skills/rstool-helper/EXAMPLES.md +44 -116
- package/skills/rstool-helper/GUIDE.md +40 -25
- package/skills/rstool-helper/REFERENCE.md +40 -177
- package/src/index.ts +24 -17
- package/src/mappers/portal-adapter.ts +49 -0
- package/src/mappers/types.ts +4 -0
- package/src/models/agent-workflow.ts +66 -0
- package/src/models/analysis.ts +7 -0
- package/src/models/common.ts +7 -0
- package/src/models/constituenta.ts +24 -6
- package/src/models/diagnostic.ts +4 -0
- package/src/models/evaluation.ts +11 -0
- package/src/models/import-detect.test.ts +66 -0
- package/src/models/import-detect.ts +42 -0
- package/src/models/import-export.ts +24 -0
- package/src/models/index.ts +22 -14
- package/src/models/model-value.ts +12 -0
- package/src/models/portal-json.test.ts +38 -0
- package/src/models/portal-json.ts +54 -1
- package/src/models/rstool-agent.test.ts +698 -146
- package/src/models/rstool-agent.ts +392 -92
- package/src/models/session.ts +8 -5
- package/src/models/tool-contract.ts +81 -42
- package/src/session/batch-apply.test.ts +123 -0
- package/src/session/batch-apply.ts +82 -0
- package/src/session/persistence.test.ts +63 -0
- package/src/session/persistence.ts +69 -0
- package/src/session/session-store.ts +76 -6
- package/src/wrapper/client.test.ts +58 -0
- package/src/wrapper/client.ts +23 -0
- package/src/wrapper/stdio-handler.test.ts +101 -0
- package/src/wrapper/stdio-handler.ts +195 -0
- package/src/wrapper/stdio-wrapper.ts +4 -187
- package/dist/analysis-JiwOYDKx.d.ts +0 -16
- package/dist/constituenta-Dnd6iToB.d.ts +0 -36
- package/dist/diagnostic-BMYvciz8.d.ts +0 -15
- package/dist/evaluation-CCVYH0wA.d.ts +0 -21
- package/dist/index-uhkmwruf.d.ts +0 -46
- package/dist/rstool-agent-BZi5jO1y.js +0 -158
- package/dist/rstool-agent-BZi5jO1y.js.map +0 -1
- package/dist/rstool-agent-pRaPnZay.d.ts +0 -35
- package/dist/session/session-store.js.map +0 -1
- package/dist/tool-contract-n1ghUOrK.d.ts +0 -32
|
@@ -1,31 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
portalDetailsToDrafts,
|
|
3
|
+
portalDetailsToSessionSeed,
|
|
4
|
+
portalSchemaToDrafts,
|
|
5
|
+
portalSchemaToSessionSeed
|
|
6
|
+
} from '../mappers/portal-adapter';
|
|
1
7
|
import { ModelAdapter } from '../mappers/model-adapter';
|
|
2
8
|
import { SchemaAdapter } from '../mappers/schema-adapter';
|
|
9
|
+
import { orderDrafts, reorderSessionItemsByDrafts } from '../session/batch-apply';
|
|
3
10
|
import { SessionStore } from '../session/session-store';
|
|
11
|
+
import {
|
|
12
|
+
type AgentConstituentaPatch,
|
|
13
|
+
type ApplySchemaPatchInput,
|
|
14
|
+
type ApplySchemaPatchResult,
|
|
15
|
+
type SessionStateDetail,
|
|
16
|
+
type SessionStateResult,
|
|
17
|
+
type SessionSummary
|
|
18
|
+
} from './agent-workflow';
|
|
4
19
|
import { type AnalysisResult, type AnalyzeExpressionInput } from './analysis';
|
|
20
|
+
import { CstType } from './common';
|
|
5
21
|
import {
|
|
6
22
|
type AddOrUpdateConstituentaInput,
|
|
7
|
-
type AddOrUpdateConstituentaResult
|
|
23
|
+
type AddOrUpdateConstituentaResult,
|
|
24
|
+
type ApplyConstituentsInput,
|
|
25
|
+
type ApplyConstituentsResult,
|
|
26
|
+
type ConstituentaDraft,
|
|
27
|
+
type ConstituentaState
|
|
8
28
|
} from './constituenta';
|
|
9
|
-
import { type ListDiagnosticsFilters } from './diagnostic';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from './evaluation';
|
|
15
|
-
import {
|
|
16
|
-
type ClearConstituentaValuesInput,
|
|
17
|
-
type RecalculateModelResult,
|
|
18
|
-
type SessionModelState,
|
|
19
|
-
type SetConstituentaValueInput,
|
|
20
|
-
type SetConstituentaValuesInput
|
|
21
|
-
} from './model-value';
|
|
29
|
+
import { type DiagnosticRecord, type ListDiagnosticsFilters } from './diagnostic';
|
|
30
|
+
import { type EvaluateInput, type EvaluationResult } from './evaluation';
|
|
31
|
+
import { detectImportKind, parseImportPayload } from './import-detect';
|
|
32
|
+
import { type ExportPortalInput, type ExportPortalResult, type ImportDataKind } from './import-export';
|
|
33
|
+
import { type RecalculateModelResult, type SessionModelState, type SetModelValuesInput } from './model-value';
|
|
22
34
|
import {
|
|
23
35
|
PORTAL_JSON_CONTRACT_VERSION,
|
|
24
36
|
type PortalModelImportData,
|
|
37
|
+
type PortalRsformDetails,
|
|
25
38
|
type PortalSchemaImportData
|
|
26
39
|
} from './portal-json';
|
|
27
40
|
import { type SessionHandle, type SessionRevision, type SessionState } from './session';
|
|
28
|
-
import { CONTRACT_VERSION, type RSToolAgentContract } from './tool-contract';
|
|
41
|
+
import { CONTRACT_VERSION, type RSToolAgentContract, type RSToolAgentOptions } from './tool-contract';
|
|
29
42
|
|
|
30
43
|
function normalizeImportedState(state: SessionState): SessionState {
|
|
31
44
|
return {
|
|
@@ -42,9 +55,7 @@ function portalImportMetadata(
|
|
|
42
55
|
kind: 'schema' | 'model'
|
|
43
56
|
): Pick<PortalSchemaImportData, 'title' | 'alias' | 'description'> {
|
|
44
57
|
const defaults =
|
|
45
|
-
kind === 'schema'
|
|
46
|
-
? { title: 'Conceptual schema', alias: 'SCHEMA' }
|
|
47
|
-
: { title: 'Conceptual model', alias: 'MODEL' };
|
|
58
|
+
kind === 'schema' ? { title: 'Conceptual schema', alias: 'SCHEMA' } : { title: 'Conceptual model', alias: 'MODEL' };
|
|
48
59
|
const title = session.title.trim();
|
|
49
60
|
const alias = session.alias.trim();
|
|
50
61
|
return {
|
|
@@ -54,60 +65,149 @@ function portalImportMetadata(
|
|
|
54
65
|
};
|
|
55
66
|
}
|
|
56
67
|
|
|
68
|
+
function inferCstType(alias: string): CstType {
|
|
69
|
+
const prefix = alias.trim().charAt(0).toUpperCase();
|
|
70
|
+
switch (prefix) {
|
|
71
|
+
case 'X':
|
|
72
|
+
return CstType.BASE;
|
|
73
|
+
case 'C':
|
|
74
|
+
return CstType.CONSTANT;
|
|
75
|
+
case 'S':
|
|
76
|
+
return CstType.STRUCTURED;
|
|
77
|
+
case 'D':
|
|
78
|
+
return CstType.TERM;
|
|
79
|
+
case 'A':
|
|
80
|
+
return CstType.AXIOM;
|
|
81
|
+
case 'F':
|
|
82
|
+
return CstType.FUNCTION;
|
|
83
|
+
case 'P':
|
|
84
|
+
return CstType.PREDICATE;
|
|
85
|
+
case 'N':
|
|
86
|
+
return CstType.NOMINAL;
|
|
87
|
+
case 'T':
|
|
88
|
+
return CstType.STATEMENT;
|
|
89
|
+
default:
|
|
90
|
+
throw new Error(`Cannot infer cstType from alias "${alias}"; pass cstType explicitly`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Agent-facing entry point for incremental RSForm editing, analysis, diagnostics,
|
|
96
|
+
* modeling, and evaluation.
|
|
97
|
+
*
|
|
98
|
+
* Holds in-memory (optionally persisted) sessions and delegates language work
|
|
99
|
+
* to internal schema and model adapters.
|
|
100
|
+
*/
|
|
57
101
|
export class RSToolAgent implements RSToolAgentContract {
|
|
58
102
|
public readonly contractVersion = CONTRACT_VERSION;
|
|
59
|
-
private readonly sessions
|
|
103
|
+
private readonly sessions: SessionStore;
|
|
60
104
|
private readonly adapter = new SchemaAdapter();
|
|
61
105
|
private readonly evaluation = new ModelAdapter();
|
|
106
|
+
private currentSessionId: string | null;
|
|
107
|
+
|
|
108
|
+
/** @param options - Optional persistence directory for session storage. */
|
|
109
|
+
public constructor(options: RSToolAgentOptions = {}) {
|
|
110
|
+
this.sessions = new SessionStore({ persistenceDir: options.persistenceDir });
|
|
111
|
+
this.currentSessionId = this.sessions.loadCurrentSessionId();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** @inheritdoc */
|
|
115
|
+
public ensureSession(initial?: Partial<SessionState>): SessionHandle {
|
|
116
|
+
const current = this.getCurrentSession();
|
|
117
|
+
return current ?? this.createSession(initial);
|
|
118
|
+
}
|
|
62
119
|
|
|
120
|
+
/** @inheritdoc */
|
|
63
121
|
public createSession(initial?: Partial<SessionState>): SessionHandle {
|
|
64
|
-
return this.sessions.create(initial, this.contractVersion);
|
|
122
|
+
return this.trackSession(this.sessions.create(initial, this.contractVersion));
|
|
65
123
|
}
|
|
66
124
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
125
|
+
/** @inheritdoc */
|
|
126
|
+
public getCurrentSession(): SessionHandle | null {
|
|
127
|
+
if (!this.currentSessionId) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
if (!this.sessions.has(this.currentSessionId)) {
|
|
131
|
+
this.currentSessionId = null;
|
|
132
|
+
this.sessions.saveCurrentSessionId(null);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
return { sessionId: this.currentSessionId, contractVersion: this.contractVersion };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** @inheritdoc */
|
|
139
|
+
public setCurrentSession(sessionId: string): SessionHandle {
|
|
140
|
+
if (!this.sessions.has(sessionId)) {
|
|
141
|
+
throw new Error(`Unknown session: ${sessionId}`);
|
|
142
|
+
}
|
|
143
|
+
return this.trackSession({ sessionId, contractVersion: this.contractVersion });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @inheritdoc */
|
|
147
|
+
public applySchemaPatch(input: ApplySchemaPatchInput, sessionId?: string): ApplySchemaPatchResult {
|
|
148
|
+
const session = sessionId
|
|
149
|
+
? { sessionId: this.resolveSessionId(sessionId), contractVersion: this.contractVersion }
|
|
150
|
+
: this.ensureSession(input.initial);
|
|
151
|
+
const drafts = this.resolveAgentPatches(session.sessionId, input.items);
|
|
152
|
+
const result = this.applyConstituents(
|
|
153
|
+
{
|
|
154
|
+
drafts,
|
|
155
|
+
mode: input.mode
|
|
156
|
+
},
|
|
157
|
+
session.sessionId
|
|
158
|
+
);
|
|
159
|
+
const revision =
|
|
160
|
+
result.success && input.commitMessage ? this.commitStep(input.commitMessage, session.sessionId) : undefined;
|
|
75
161
|
return {
|
|
76
|
-
|
|
77
|
-
|
|
162
|
+
...result,
|
|
163
|
+
session,
|
|
164
|
+
summary: this.buildSessionSummary(session.sessionId),
|
|
165
|
+
revision
|
|
78
166
|
};
|
|
79
167
|
}
|
|
80
168
|
|
|
81
|
-
|
|
82
|
-
|
|
169
|
+
/** @inheritdoc */
|
|
170
|
+
public getSessionState(detail: SessionStateDetail = 'summary', sessionId?: string): SessionStateResult {
|
|
171
|
+
if (detail === 'full') {
|
|
172
|
+
const envelope = this.sessions.get(this.resolveSessionId(sessionId));
|
|
173
|
+
return structuredClone(envelope.state);
|
|
174
|
+
}
|
|
175
|
+
return this.buildSessionSummary(sessionId);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** @inheritdoc */
|
|
179
|
+
public listDiagnostics(filters?: ListDiagnosticsFilters, sessionId?: string) {
|
|
180
|
+
return this.sessions.listDiagnostics(this.resolveSessionId(sessionId), filters);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** @inheritdoc */
|
|
184
|
+
public analyzeExpression(input: AnalyzeExpressionInput, sessionId?: string): AnalysisResult {
|
|
185
|
+
const id = this.resolveSessionId(sessionId);
|
|
186
|
+
const envelope = this.sessions.get(id);
|
|
83
187
|
const { result, diagnostics } = this.adapter.analyzeAgainstSession(envelope.state, {
|
|
84
188
|
id: -1,
|
|
85
189
|
alias: '_analysis',
|
|
86
190
|
cstType: input.cstType,
|
|
87
191
|
definitionFormal: input.expression
|
|
88
192
|
});
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
193
|
+
if (input.recordDiagnostics) {
|
|
194
|
+
this.sessions.replaceDiagnosticsForConstituent(
|
|
195
|
+
id,
|
|
196
|
+
undefined,
|
|
197
|
+
diagnostics.map(item => ({ ...item, constituentId: undefined }))
|
|
198
|
+
);
|
|
199
|
+
}
|
|
93
200
|
return result;
|
|
94
201
|
}
|
|
95
202
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return
|
|
203
|
+
/** @inheritdoc */
|
|
204
|
+
public commitStep(message?: string, sessionId?: string): SessionRevision {
|
|
205
|
+
return this.sessions.addRevision(this.resolveSessionId(sessionId), message);
|
|
99
206
|
}
|
|
100
207
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
public commitStep(sessionId: string, message?: string): SessionRevision {
|
|
106
|
-
return this.sessions.addRevision(sessionId, message);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
public exportSession(sessionId: string): string {
|
|
110
|
-
const envelope = this.sessions.get(sessionId);
|
|
208
|
+
/** @inheritdoc */
|
|
209
|
+
public exportSession(sessionId?: string): string {
|
|
210
|
+
const envelope = this.sessions.get(this.resolveSessionId(sessionId));
|
|
111
211
|
return JSON.stringify(
|
|
112
212
|
{
|
|
113
213
|
contractVersion: this.contractVersion,
|
|
@@ -119,9 +219,164 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
119
219
|
);
|
|
120
220
|
}
|
|
121
221
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
222
|
+
/** @inheritdoc */
|
|
223
|
+
public exportPortal(input: ExportPortalInput, sessionId?: string): ExportPortalResult {
|
|
224
|
+
const format = input.format ?? 'json';
|
|
225
|
+
const object =
|
|
226
|
+
input.kind === 'schema' ? this.buildPortalSchemaObject(sessionId) : this.buildPortalModelObject(sessionId);
|
|
227
|
+
return format === 'object' ? object : JSON.stringify(object, null, 2);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** @inheritdoc */
|
|
231
|
+
public importData(payload: string | object, kind: ImportDataKind = 'auto'): SessionHandle {
|
|
232
|
+
const parsed = parseImportPayload(payload);
|
|
233
|
+
const resolvedKind = kind === 'auto' ? detectImportKind(parsed) : kind;
|
|
234
|
+
|
|
235
|
+
switch (resolvedKind) {
|
|
236
|
+
case 'session':
|
|
237
|
+
return this.importSessionExport(parsed);
|
|
238
|
+
case 'portal-schema':
|
|
239
|
+
return this.importPortalSchemaData(parsed as PortalSchemaImportData);
|
|
240
|
+
case 'portal-details':
|
|
241
|
+
return this.importPortalDetailsData(parsed as PortalRsformDetails);
|
|
242
|
+
default:
|
|
243
|
+
throw new Error(`Unsupported import kind: ${resolvedKind as string}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** @inheritdoc */
|
|
248
|
+
public async setModelValues(input: SetModelValuesInput, sessionId?: string): Promise<SessionModelState> {
|
|
249
|
+
const id = this.resolveSessionId(sessionId);
|
|
250
|
+
const snapshot = this.sessions.snapshot(id);
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
let state = this.sessions.get(id).state;
|
|
254
|
+
|
|
255
|
+
if (input.clear?.length) {
|
|
256
|
+
const model = await this.evaluation.clearConstituentaValues(state, input.clear);
|
|
257
|
+
state = { ...state, model };
|
|
258
|
+
this.sessions.replaceState(id, state);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (input.set?.length) {
|
|
262
|
+
state = this.sessions.get(id).state;
|
|
263
|
+
const model = await this.evaluation.setConstituentaValues(state, { items: input.set });
|
|
264
|
+
state = { ...state, model };
|
|
265
|
+
this.sessions.replaceState(id, state);
|
|
266
|
+
return model;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return structuredClone(this.sessions.get(id).state.model);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
this.sessions.restore(id, snapshot);
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** @inheritdoc */
|
|
277
|
+
public getModelState(sessionId?: string): SessionModelState {
|
|
278
|
+
const envelope = this.sessions.get(this.resolveSessionId(sessionId));
|
|
279
|
+
return structuredClone(envelope.state.model);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** @inheritdoc */
|
|
283
|
+
public evaluate(input: EvaluateInput, sessionId?: string): EvaluationResult {
|
|
284
|
+
const envelope = this.sessions.get(this.resolveSessionId(sessionId));
|
|
285
|
+
|
|
286
|
+
if (input.constituentId !== undefined) {
|
|
287
|
+
return this.evaluation.evaluateConstituenta(envelope.state, input.constituentId);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (input.expression !== undefined && input.cstType !== undefined) {
|
|
291
|
+
return this.evaluation.evaluateExpression(envelope.state, input.expression, input.cstType);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
throw new Error('evaluate requires constituentId or expression with cstType');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** @inheritdoc */
|
|
298
|
+
public recalculateModel(sessionId?: string): RecalculateModelResult {
|
|
299
|
+
const envelope = this.sessions.get(this.resolveSessionId(sessionId));
|
|
300
|
+
return this.evaluation.recalculateModel(envelope.state);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private addOrUpdateConstituenta(
|
|
304
|
+
input: AddOrUpdateConstituentaInput,
|
|
305
|
+
sessionId?: string
|
|
306
|
+
): AddOrUpdateConstituentaResult {
|
|
307
|
+
const id = this.resolveSessionId(sessionId);
|
|
308
|
+
const envelope = this.sessions.get(id);
|
|
309
|
+
const { result, diagnostics } = this.adapter.analyzeAgainstSession(envelope.state, input.draft);
|
|
310
|
+
const state = this.adapter.mergeStateWithDraft(envelope.state, input.draft, result);
|
|
311
|
+
this.sessions.replaceDiagnosticsForConstituent(id, input.draft.id, diagnostics);
|
|
312
|
+
return { state, diagnostics };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private applyConstituents(input: ApplyConstituentsInput, sessionId?: string): ApplyConstituentsResult {
|
|
316
|
+
const id = this.resolveSessionId(sessionId);
|
|
317
|
+
const mode = input.mode ?? 'atomic';
|
|
318
|
+
const ordered = orderDrafts(this.sessions.get(id).state.items, input.drafts);
|
|
319
|
+
const snapshot = this.sessions.snapshot(id);
|
|
320
|
+
const applied: ConstituentaState[] = [];
|
|
321
|
+
const failed: ApplyConstituentsResult['failed'] = [];
|
|
322
|
+
|
|
323
|
+
for (const draft of ordered) {
|
|
324
|
+
const result = this.addOrUpdateConstituenta({ draft }, id);
|
|
325
|
+
if (result.state.analysis.success) {
|
|
326
|
+
applied.push(result.state);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
failed.push({ draft, diagnostics: result.diagnostics });
|
|
330
|
+
if (mode === 'atomic') {
|
|
331
|
+
this.sessions.restore(id, snapshot);
|
|
332
|
+
return {
|
|
333
|
+
success: false,
|
|
334
|
+
applied: [],
|
|
335
|
+
failed,
|
|
336
|
+
diagnostics: this.sessions.listDiagnostics(id)
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const envelope = this.sessions.get(id);
|
|
342
|
+
reorderSessionItemsByDrafts(envelope.state.items, input.drafts);
|
|
343
|
+
this.sessions.replaceState(id, envelope.state);
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
success: failed.length === 0,
|
|
347
|
+
applied,
|
|
348
|
+
failed,
|
|
349
|
+
diagnostics: this.sessions.listDiagnostics(id)
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private buildSessionSummary(sessionId?: string): SessionSummary {
|
|
354
|
+
const id = this.resolveSessionId(sessionId);
|
|
355
|
+
const envelope = this.sessions.get(id);
|
|
356
|
+
const diagnostics = this.sessions.listDiagnostics(id);
|
|
357
|
+
return {
|
|
358
|
+
sessionId: id,
|
|
359
|
+
contractVersion: this.contractVersion,
|
|
360
|
+
alias: envelope.state.alias,
|
|
361
|
+
title: envelope.state.title,
|
|
362
|
+
comment: envelope.state.comment,
|
|
363
|
+
itemCount: envelope.state.items.length,
|
|
364
|
+
modelItemCount: envelope.state.model.items.length,
|
|
365
|
+
diagnosticsCount: diagnostics.length,
|
|
366
|
+
items: envelope.state.items.map(item => ({
|
|
367
|
+
id: item.id,
|
|
368
|
+
alias: item.alias,
|
|
369
|
+
cstType: item.cstType,
|
|
370
|
+
analysisSuccess: item.analysis.success
|
|
371
|
+
})),
|
|
372
|
+
diagnostics,
|
|
373
|
+
lastRevision: envelope.state.revisions.at(-1)
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private buildPortalSchemaObject(sessionId?: string): PortalSchemaImportData {
|
|
378
|
+
const envelope = this.sessions.get(this.resolveSessionId(sessionId));
|
|
379
|
+
return {
|
|
125
380
|
contract_version: PORTAL_JSON_CONTRACT_VERSION,
|
|
126
381
|
...portalImportMetadata(envelope.state, 'schema'),
|
|
127
382
|
items: envelope.state.items.map(item => ({
|
|
@@ -141,12 +396,11 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
141
396
|
})),
|
|
142
397
|
attribution: []
|
|
143
398
|
};
|
|
144
|
-
return JSON.stringify(payload, null, 2);
|
|
145
399
|
}
|
|
146
400
|
|
|
147
|
-
|
|
148
|
-
const envelope = this.sessions.get(sessionId);
|
|
149
|
-
|
|
401
|
+
private buildPortalModelObject(sessionId?: string): PortalModelImportData {
|
|
402
|
+
const envelope = this.sessions.get(this.resolveSessionId(sessionId));
|
|
403
|
+
return {
|
|
150
404
|
contract_version: PORTAL_JSON_CONTRACT_VERSION,
|
|
151
405
|
...portalImportMetadata(envelope.state, 'model'),
|
|
152
406
|
items: envelope.state.model.items.map(item => ({
|
|
@@ -155,57 +409,103 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
155
409
|
value: item.value
|
|
156
410
|
}))
|
|
157
411
|
};
|
|
158
|
-
return JSON.stringify(payload, null, 2);
|
|
159
412
|
}
|
|
160
413
|
|
|
161
|
-
|
|
162
|
-
|
|
414
|
+
private importSessionExport(parsed: unknown): SessionHandle {
|
|
415
|
+
if (!parsed || typeof parsed !== 'object' || !('state' in parsed)) {
|
|
416
|
+
throw new Error('Invalid session export payload');
|
|
417
|
+
}
|
|
418
|
+
const data = parsed as {
|
|
163
419
|
state: SessionState;
|
|
420
|
+
diagnostics?: DiagnosticRecord[];
|
|
164
421
|
};
|
|
165
|
-
|
|
422
|
+
const handle = this.sessions.create(normalizeImportedState(data.state), this.contractVersion);
|
|
423
|
+
if (data.diagnostics?.length) {
|
|
424
|
+
this.sessions.setDiagnostics(handle.sessionId, data.diagnostics);
|
|
425
|
+
}
|
|
426
|
+
return this.trackSession(handle);
|
|
166
427
|
}
|
|
167
428
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const envelope = this.sessions.get(sessionId);
|
|
173
|
-
return this.evaluation.setConstituentaValue(envelope.state, input);
|
|
429
|
+
private importPortalSchemaData(data: PortalSchemaImportData): SessionHandle {
|
|
430
|
+
const handle = this.createSession(portalSchemaToSessionSeed(data));
|
|
431
|
+
this.applyConstituents({ drafts: portalSchemaToDrafts(data), mode: 'best_effort' }, handle.sessionId);
|
|
432
|
+
return handle;
|
|
174
433
|
}
|
|
175
434
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const envelope = this.sessions.get(sessionId);
|
|
181
|
-
return this.evaluation.setConstituentaValues(envelope.state, input);
|
|
435
|
+
private importPortalDetailsData(data: PortalRsformDetails): SessionHandle {
|
|
436
|
+
const handle = this.createSession(portalDetailsToSessionSeed(data));
|
|
437
|
+
this.applyConstituents({ drafts: portalDetailsToDrafts(data), mode: 'best_effort' }, handle.sessionId);
|
|
438
|
+
return handle;
|
|
182
439
|
}
|
|
183
440
|
|
|
184
|
-
|
|
185
|
-
sessionId
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return this.evaluation.clearConstituentaValues(envelope.state, input.items);
|
|
190
|
-
}
|
|
441
|
+
private resolveAgentPatches(sessionId: string, patches: AgentConstituentaPatch[]): ConstituentaDraft[] {
|
|
442
|
+
const items = this.sessions.get(sessionId).state.items;
|
|
443
|
+
const existingByAlias = new Map(items.map(item => [item.alias, item]));
|
|
444
|
+
const usedIds = new Set(items.map(item => item.id));
|
|
445
|
+
let nextId = items.reduce((max, item) => Math.max(max, item.id), 0) + 1;
|
|
191
446
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
447
|
+
const reserveId = (id: number): void => {
|
|
448
|
+
usedIds.add(id);
|
|
449
|
+
if (id >= nextId) {
|
|
450
|
+
nextId = id + 1;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const allocateId = (): number => {
|
|
455
|
+
while (usedIds.has(nextId)) {
|
|
456
|
+
nextId += 1;
|
|
457
|
+
}
|
|
458
|
+
const id = nextId;
|
|
459
|
+
nextId += 1;
|
|
460
|
+
usedIds.add(id);
|
|
461
|
+
return id;
|
|
462
|
+
};
|
|
196
463
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
464
|
+
return patches.map(patch => {
|
|
465
|
+
const existing = existingByAlias.get(patch.alias);
|
|
466
|
+
let id: number;
|
|
467
|
+
if (patch.id !== undefined) {
|
|
468
|
+
id = patch.id;
|
|
469
|
+
reserveId(id);
|
|
470
|
+
} else if (existing !== undefined) {
|
|
471
|
+
id = existing.id;
|
|
472
|
+
} else {
|
|
473
|
+
id = allocateId();
|
|
474
|
+
}
|
|
475
|
+
const draft = {
|
|
476
|
+
id,
|
|
477
|
+
alias: patch.alias,
|
|
478
|
+
cstType: patch.cstType ?? existing?.cstType ?? inferCstType(patch.alias),
|
|
479
|
+
definitionFormal: patch.definitionFormal ?? existing?.definitionFormal ?? '',
|
|
480
|
+
term: patch.term ?? existing?.term ?? '',
|
|
481
|
+
definitionText: patch.definitionText ?? existing?.definitionText ?? '',
|
|
482
|
+
convention: patch.convention ?? existing?.convention ?? ''
|
|
483
|
+
};
|
|
484
|
+
existingByAlias.set(patch.alias, {
|
|
485
|
+
...draft,
|
|
486
|
+
analysis: existing?.analysis ?? { success: true, type: null, valueClass: 'value', diagnostics: [] }
|
|
487
|
+
});
|
|
488
|
+
return draft;
|
|
489
|
+
});
|
|
200
490
|
}
|
|
201
491
|
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
492
|
+
private resolveSessionId(sessionId?: string): string {
|
|
493
|
+
const id = sessionId ?? this.currentSessionId;
|
|
494
|
+
if (!id) {
|
|
495
|
+
return this.createSession().sessionId;
|
|
496
|
+
}
|
|
497
|
+
if (!this.sessions.has(id)) {
|
|
498
|
+
if (sessionId) {
|
|
499
|
+
throw new Error(`Unknown session: ${sessionId}`);
|
|
500
|
+
}
|
|
501
|
+
return this.createSession().sessionId;
|
|
502
|
+
}
|
|
503
|
+
return id;
|
|
205
504
|
}
|
|
206
505
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
506
|
+
private trackSession(handle: SessionHandle): SessionHandle {
|
|
507
|
+
this.currentSessionId = handle.sessionId;
|
|
508
|
+
this.sessions.saveCurrentSessionId(handle.sessionId);
|
|
509
|
+
return handle;
|
|
210
510
|
}
|
|
211
511
|
}
|
package/src/models/session.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { type ConstituentaState } from './constituenta';
|
|
2
2
|
import { type SessionModelState } from './model-value';
|
|
3
3
|
|
|
4
|
+
/** Opaque session reference returned by create/import operations. */
|
|
4
5
|
export interface SessionHandle {
|
|
5
6
|
sessionId: string;
|
|
6
7
|
contractVersion: string;
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
/** Recorded checkpoint in session revision history. */
|
|
9
11
|
export interface SessionRevision {
|
|
10
12
|
revisionId: string;
|
|
11
13
|
at: string;
|
|
12
14
|
message?: string;
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
/** Full in-memory session state. */
|
|
15
18
|
export interface SessionState {
|
|
16
19
|
sessionId: string;
|
|
17
20
|
/** Library item alias for the conceptual schema or model. */
|
|
@@ -20,14 +23,14 @@ export interface SessionState {
|
|
|
20
23
|
title: string;
|
|
21
24
|
/** Developer comment (Portal JSON `description` on export). */
|
|
22
25
|
comment: string;
|
|
23
|
-
/**
|
|
26
|
+
/** ISO timestamp of session creation. */
|
|
24
27
|
createdAt: string;
|
|
25
|
-
/**
|
|
28
|
+
/** ISO timestamp of last mutation. */
|
|
26
29
|
updatedAt: string;
|
|
27
|
-
/**
|
|
30
|
+
/** Revision checkpoints recorded via {@link RSToolAgent.commitStep}. */
|
|
28
31
|
revisions: SessionRevision[];
|
|
29
|
-
/**
|
|
32
|
+
/** Analyzed constituents in the conceptual schema. */
|
|
30
33
|
items: ConstituentaState[];
|
|
31
|
-
/**
|
|
34
|
+
/** Evaluated model values. */
|
|
32
35
|
model: SessionModelState;
|
|
33
36
|
}
|