@rsconcept/rstool 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +585 -0
- package/dist/index.js.map +1 -0
- package/dist/mappers/model-adapter.d.ts +27 -0
- package/dist/mappers/model-adapter.js +248 -0
- package/dist/mappers/model-adapter.js.map +1 -0
- package/dist/mappers/schema-adapter.d.ts +22 -0
- package/dist/mappers/schema-adapter.js +89 -0
- package/dist/mappers/schema-adapter.js.map +1 -0
- package/dist/mappers/types.d.ts +23 -0
- package/dist/mappers/types.js +22 -0
- package/dist/mappers/types.js.map +1 -0
- package/dist/models/analysis.d.ts +18 -0
- package/dist/models/analysis.js +1 -0
- package/dist/models/analysis.js.map +1 -0
- package/dist/models/common.d.ts +15 -0
- package/dist/models/common.js +12 -0
- package/dist/models/common.js.map +1 -0
- package/dist/models/constituenta.d.ts +38 -0
- package/dist/models/constituenta.js +1 -0
- package/dist/models/constituenta.js.map +1 -0
- package/dist/models/diagnostic.d.ts +17 -0
- package/dist/models/diagnostic.js +1 -0
- package/dist/models/diagnostic.js.map +1 -0
- package/dist/models/evaluation.d.ts +23 -0
- package/dist/models/evaluation.js +1 -0
- package/dist/models/evaluation.js.map +1 -0
- package/dist/models/index.d.ts +13 -0
- package/dist/models/index.js +491 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/model-value.d.ts +37 -0
- package/dist/models/model-value.js +1 -0
- package/dist/models/model-value.js.map +1 -0
- package/dist/models/rstool-agent.d.ts +36 -0
- package/dist/models/rstool-agent.js +480 -0
- package/dist/models/rstool-agent.js.map +1 -0
- package/dist/models/session.d.ts +29 -0
- package/dist/models/session.js +1 -0
- package/dist/models/session.js.map +1 -0
- package/dist/models/tool-contract.d.ts +33 -0
- package/dist/models/tool-contract.js +6 -0
- package/dist/models/tool-contract.js.map +1 -0
- package/dist/session/session-store.d.ts +26 -0
- package/dist/session/session-store.js +66 -0
- package/dist/session/session-store.js.map +1 -0
- package/dist/wrapper/client.d.ts +30 -0
- package/dist/wrapper/client.js +96 -0
- package/dist/wrapper/client.js.map +1 -0
- package/dist/wrapper/stdio-wrapper.d.ts +1 -0
- package/dist/wrapper/stdio-wrapper.js +679 -0
- package/dist/wrapper/stdio-wrapper.js.map +1 -0
- package/docs/CONSTITUENTA.md +55 -0
- package/docs/DIAGNOSTICS.md +125 -0
- package/docs/DOMAIN.md +89 -0
- package/docs/GRAMMAR-REF.md +98 -0
- package/docs/PORTAL-API.md +48 -0
- package/docs/README.md +15 -0
- package/docs/SYNTAX.md +139 -0
- package/docs/TYPIFICATION.md +79 -0
- package/package.json +76 -0
- package/skills/README.md +15 -0
- package/skills/rstool-helper/EXAMPLES.md +154 -0
- package/skills/rstool-helper/REFERENCE.md +169 -0
- package/skills/rstool-helper/SKILL.md +148 -0
- package/src/index.ts +43 -0
- package/src/mappers/model-adapter.ts +276 -0
- package/src/mappers/schema-adapter.ts +87 -0
- package/src/mappers/types.ts +35 -0
- package/src/models/analysis.ts +13 -0
- package/src/models/common.ts +17 -0
- package/src/models/constituenta.ts +35 -0
- package/src/models/diagnostic.ts +12 -0
- package/src/models/evaluation.ts +25 -0
- package/src/models/index.ts +33 -0
- package/src/models/model-value.ts +31 -0
- package/src/models/rstool-agent.test.ts +300 -0
- package/src/models/rstool-agent.ts +143 -0
- package/src/models/session.ts +22 -0
- package/src/models/tool-contract.ts +47 -0
- package/src/session/session-store.ts +81 -0
- package/src/wrapper/client.ts +116 -0
- package/src/wrapper/stdio-wrapper.ts +225 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export { type AnalysisResult, type AnalyzeExpressionInput } from './analysis';
|
|
2
|
+
export {
|
|
3
|
+
CstType,
|
|
4
|
+
EvalStatus,
|
|
5
|
+
RSErrorCode,
|
|
6
|
+
ValueClass,
|
|
7
|
+
type BasicBinding,
|
|
8
|
+
type RSToolErrorDescription,
|
|
9
|
+
type RSToolValue
|
|
10
|
+
} from './common';
|
|
11
|
+
export {
|
|
12
|
+
type AddOrUpdateConstituentaInput,
|
|
13
|
+
type AddOrUpdateConstituentaResult,
|
|
14
|
+
type ConstituentaDraft,
|
|
15
|
+
type ConstituentaState
|
|
16
|
+
} from './constituenta';
|
|
17
|
+
export { type DiagnosticRecord, type ListDiagnosticsFilters } from './diagnostic';
|
|
18
|
+
export {
|
|
19
|
+
type EvaluateConstituentaInput,
|
|
20
|
+
type EvaluateExpressionInput,
|
|
21
|
+
type EvaluationResult
|
|
22
|
+
} from './evaluation';
|
|
23
|
+
export {
|
|
24
|
+
type ClearConstituentaValuesInput,
|
|
25
|
+
type ModelValueState,
|
|
26
|
+
type RecalculateModelResult,
|
|
27
|
+
type SessionModelState,
|
|
28
|
+
type SetConstituentaValueInput,
|
|
29
|
+
type SetConstituentaValuesInput
|
|
30
|
+
} from './model-value';
|
|
31
|
+
export { RSToolAgent } from './rstool-agent';
|
|
32
|
+
export { type SessionHandle, type SessionRevision, type SessionState } from './session';
|
|
33
|
+
export { CONTRACT_VERSION, type RSToolAgentContract } from './tool-contract';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type BasicBinding, type EvalStatus, type RSToolValue } from './common';
|
|
2
|
+
|
|
3
|
+
export interface ModelValueState {
|
|
4
|
+
id: number;
|
|
5
|
+
/** Frontend type string: `basic` or normalized effective typification. */
|
|
6
|
+
type: string;
|
|
7
|
+
value: RSToolValue | BasicBinding;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SessionModelState {
|
|
11
|
+
items: ModelValueState[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SetConstituentaValueInput {
|
|
15
|
+
target: number;
|
|
16
|
+
/** Optional type override; inferred from schema when omitted. */
|
|
17
|
+
type?: string;
|
|
18
|
+
value: RSToolValue | BasicBinding;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SetConstituentaValuesInput {
|
|
22
|
+
items: SetConstituentaValueInput[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ClearConstituentaValuesInput {
|
|
26
|
+
items: number[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface RecalculateModelResult {
|
|
30
|
+
items: Array<{ id: number; alias: string; value: RSToolValue | null; status: EvalStatus }>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { CstType, EvalStatus, RSErrorCode, RSToolAgent } from './index';
|
|
4
|
+
|
|
5
|
+
function buildSampleForm(tool: RSToolAgent, sessionId: string) {
|
|
6
|
+
tool.addOrUpdateConstituenta(sessionId, {
|
|
7
|
+
draft: { id: 1, alias: 'X1', cstType: CstType.BASE, definitionFormal: '' }
|
|
8
|
+
});
|
|
9
|
+
tool.addOrUpdateConstituenta(sessionId, {
|
|
10
|
+
draft: { id: 2, alias: 'D1', cstType: CstType.TERM, definitionFormal: '1+2' }
|
|
11
|
+
});
|
|
12
|
+
tool.addOrUpdateConstituenta(sessionId, {
|
|
13
|
+
draft: { id: 3, alias: 'A1', cstType: CstType.AXIOM, definitionFormal: '1=1' }
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('RSToolAgent', () => {
|
|
18
|
+
it('creates and exports a session', () => {
|
|
19
|
+
const tool = new RSToolAgent();
|
|
20
|
+
const session = tool.createSession();
|
|
21
|
+
const exported = tool.exportSession(session.sessionId);
|
|
22
|
+
expect(exported).toContain(session.sessionId);
|
|
23
|
+
expect(exported).toContain('contractVersion');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('analyzes a valid expression', () => {
|
|
27
|
+
const tool = new RSToolAgent();
|
|
28
|
+
const session = tool.createSession();
|
|
29
|
+
const analysis = tool.analyzeExpression(session.sessionId, {
|
|
30
|
+
expression: '1+2',
|
|
31
|
+
cstType: CstType.TERM
|
|
32
|
+
});
|
|
33
|
+
expect(analysis.success).toBe(true);
|
|
34
|
+
expect(analysis.diagnostics.length).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns syntax diagnostics for invalid expression', () => {
|
|
38
|
+
const tool = new RSToolAgent();
|
|
39
|
+
const session = tool.createSession();
|
|
40
|
+
const analysis = tool.analyzeExpression(session.sessionId, {
|
|
41
|
+
expression: '(',
|
|
42
|
+
cstType: CstType.TERM
|
|
43
|
+
});
|
|
44
|
+
expect(analysis.success).toBe(false);
|
|
45
|
+
expect(analysis.diagnostics.length).toBeGreaterThan(0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('rejects formal definition for constants', () => {
|
|
49
|
+
const tool = new RSToolAgent();
|
|
50
|
+
const session = tool.createSession();
|
|
51
|
+
const result = tool.addOrUpdateConstituenta(session.sessionId, {
|
|
52
|
+
draft: {
|
|
53
|
+
id: 11,
|
|
54
|
+
alias: 'C1',
|
|
55
|
+
cstType: CstType.CONSTANT,
|
|
56
|
+
definitionFormal: 'X1'
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
expect(result.state.analysis.success).toBe(false);
|
|
60
|
+
expect(result.diagnostics[0]?.error.code).toBe(RSErrorCode.definitionNotAllowed);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('rejects formal definition for basic sets', () => {
|
|
64
|
+
const tool = new RSToolAgent();
|
|
65
|
+
const session = tool.createSession();
|
|
66
|
+
const result = tool.addOrUpdateConstituenta(session.sessionId, {
|
|
67
|
+
draft: {
|
|
68
|
+
id: 12,
|
|
69
|
+
alias: 'X1',
|
|
70
|
+
cstType: CstType.BASE,
|
|
71
|
+
definitionFormal: 'Z'
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
expect(result.state.analysis.success).toBe(false);
|
|
75
|
+
expect(result.diagnostics[0]?.error.code).toBe(RSErrorCode.definitionNotAllowed);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('returns known analysis for empty base definition', () => {
|
|
79
|
+
const tool = new RSToolAgent();
|
|
80
|
+
const session = tool.createSession();
|
|
81
|
+
const result = tool.addOrUpdateConstituenta(session.sessionId, {
|
|
82
|
+
draft: {
|
|
83
|
+
id: 13,
|
|
84
|
+
alias: 'X1',
|
|
85
|
+
cstType: CstType.BASE,
|
|
86
|
+
definitionFormal: ''
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
expect(result.state.analysis.success).toBe(true);
|
|
90
|
+
expect(result.state.analysis.type).not.toBeNull();
|
|
91
|
+
expect(result.state.analysis.valueClass).toBe('value');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('persists term, definitionText, and convention in session state', () => {
|
|
95
|
+
const tool = new RSToolAgent();
|
|
96
|
+
const session = tool.createSession();
|
|
97
|
+
const result = tool.addOrUpdateConstituenta(session.sessionId, {
|
|
98
|
+
draft: {
|
|
99
|
+
id: 15,
|
|
100
|
+
alias: 'D2',
|
|
101
|
+
cstType: CstType.TERM,
|
|
102
|
+
definitionFormal: '1',
|
|
103
|
+
term: 'natural number',
|
|
104
|
+
definitionText: 'A positive integer',
|
|
105
|
+
convention: 'Standard arithmetic'
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
expect(result.state.term).toBe('natural number');
|
|
109
|
+
expect(result.state.definitionText).toBe('A positive integer');
|
|
110
|
+
expect(result.state.convention).toBe('Standard arithmetic');
|
|
111
|
+
|
|
112
|
+
const form = tool.getFormState(session.sessionId);
|
|
113
|
+
expect(form.items[0]).toMatchObject({
|
|
114
|
+
term: 'natural number',
|
|
115
|
+
definitionText: 'A positive integer',
|
|
116
|
+
convention: 'Standard arithmetic'
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const exported = tool.exportSession(session.sessionId);
|
|
120
|
+
const imported = tool.importSession(exported);
|
|
121
|
+
const restored = tool.getFormState(imported.sessionId);
|
|
122
|
+
expect(restored.items[0]).toMatchObject({
|
|
123
|
+
term: 'natural number',
|
|
124
|
+
definitionText: 'A positive integer',
|
|
125
|
+
convention: 'Standard arithmetic'
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('defaults missing text fields to empty strings', () => {
|
|
130
|
+
const tool = new RSToolAgent();
|
|
131
|
+
const session = tool.createSession();
|
|
132
|
+
const result = tool.addOrUpdateConstituenta(session.sessionId, {
|
|
133
|
+
draft: {
|
|
134
|
+
id: 16,
|
|
135
|
+
alias: 'D3',
|
|
136
|
+
cstType: CstType.TERM,
|
|
137
|
+
definitionFormal: '2'
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
expect(result.state.term).toBe('');
|
|
141
|
+
expect(result.state.definitionText).toBe('');
|
|
142
|
+
expect(result.state.convention).toBe('');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('returns known analysis for empty constant definition', () => {
|
|
146
|
+
const tool = new RSToolAgent();
|
|
147
|
+
const session = tool.createSession();
|
|
148
|
+
const result = tool.addOrUpdateConstituenta(session.sessionId, {
|
|
149
|
+
draft: {
|
|
150
|
+
id: 14,
|
|
151
|
+
alias: 'C1',
|
|
152
|
+
cstType: CstType.CONSTANT,
|
|
153
|
+
definitionFormal: ''
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
expect(result.state.analysis.success).toBe(true);
|
|
157
|
+
expect(result.state.analysis.type).not.toBeNull();
|
|
158
|
+
expect(result.state.analysis.valueClass).toBe('value');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('RSToolAgent modeling and evaluation', () => {
|
|
163
|
+
it('returns empty model on new session', () => {
|
|
164
|
+
const tool = new RSToolAgent();
|
|
165
|
+
const session = tool.createSession();
|
|
166
|
+
const model = tool.getModelState(session.sessionId);
|
|
167
|
+
expect(model.items).toEqual([]);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('sets base constituenta binding', async () => {
|
|
171
|
+
const tool = new RSToolAgent();
|
|
172
|
+
const session = tool.createSession();
|
|
173
|
+
buildSampleForm(tool, session.sessionId);
|
|
174
|
+
|
|
175
|
+
const model = await tool.setConstituentaValue(session.sessionId, {
|
|
176
|
+
target: 1,
|
|
177
|
+
value: { 0: 'zero', 1: 'one' }
|
|
178
|
+
});
|
|
179
|
+
expect(model.items).toHaveLength(1);
|
|
180
|
+
expect(model.items[0]).toMatchObject({
|
|
181
|
+
id: 1,
|
|
182
|
+
type: 'basic',
|
|
183
|
+
value: { 0: 'zero', 1: 'one' }
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('rejects setting inferrable term directly', async () => {
|
|
188
|
+
const tool = new RSToolAgent();
|
|
189
|
+
const session = tool.createSession();
|
|
190
|
+
buildSampleForm(tool, session.sessionId);
|
|
191
|
+
|
|
192
|
+
await expect(
|
|
193
|
+
tool.setConstituentaValue(session.sessionId, {
|
|
194
|
+
target: 2,
|
|
195
|
+
value: 3
|
|
196
|
+
})
|
|
197
|
+
).rejects.toThrow(/inferrable/);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('evaluates expression against session context', () => {
|
|
201
|
+
const tool = new RSToolAgent();
|
|
202
|
+
const session = tool.createSession();
|
|
203
|
+
buildSampleForm(tool, session.sessionId);
|
|
204
|
+
|
|
205
|
+
const result = tool.evaluateExpression(session.sessionId, {
|
|
206
|
+
expression: '1+2',
|
|
207
|
+
cstType: CstType.TERM
|
|
208
|
+
});
|
|
209
|
+
expect(result.success).toBe(true);
|
|
210
|
+
expect(result.value).toBe(3);
|
|
211
|
+
expect(result.status).toBe(EvalStatus.HAS_DATA);
|
|
212
|
+
expect(result.diagnostics).toHaveLength(0);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('evaluates a stored constituenta', () => {
|
|
216
|
+
const tool = new RSToolAgent();
|
|
217
|
+
const session = tool.createSession();
|
|
218
|
+
buildSampleForm(tool, session.sessionId);
|
|
219
|
+
|
|
220
|
+
const result = tool.evaluateConstituenta(session.sessionId, { constituentId: 2 });
|
|
221
|
+
expect(result.success).toBe(true);
|
|
222
|
+
expect(result.value).toBe(3);
|
|
223
|
+
expect(result.status).toBe(EvalStatus.HAS_DATA);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('evaluates axiom constituenta', () => {
|
|
227
|
+
const tool = new RSToolAgent();
|
|
228
|
+
const session = tool.createSession();
|
|
229
|
+
buildSampleForm(tool, session.sessionId);
|
|
230
|
+
|
|
231
|
+
const result = tool.evaluateConstituenta(session.sessionId, { constituentId: 3 });
|
|
232
|
+
expect(result.success).toBe(true);
|
|
233
|
+
expect(result.value).toBe(1);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('recalculates inferrable model values', () => {
|
|
237
|
+
const tool = new RSToolAgent();
|
|
238
|
+
const session = tool.createSession();
|
|
239
|
+
buildSampleForm(tool, session.sessionId);
|
|
240
|
+
|
|
241
|
+
const result = tool.recalculateModel(session.sessionId);
|
|
242
|
+
const d1 = result.items.find(item => item.alias === 'D1');
|
|
243
|
+
const a1 = result.items.find(item => item.alias === 'A1');
|
|
244
|
+
expect(d1?.value).toBe(3);
|
|
245
|
+
expect(d1?.status).toBe(EvalStatus.HAS_DATA);
|
|
246
|
+
expect(a1?.value).toBe(1);
|
|
247
|
+
expect(a1?.status).toBe(EvalStatus.HAS_DATA);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('clears model values', async () => {
|
|
251
|
+
const tool = new RSToolAgent();
|
|
252
|
+
const session = tool.createSession();
|
|
253
|
+
buildSampleForm(tool, session.sessionId);
|
|
254
|
+
await tool.setConstituentaValue(session.sessionId, {
|
|
255
|
+
target: 1,
|
|
256
|
+
value: { 0: 'a' }
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const model = await tool.clearConstituentaValues(session.sessionId, { items: [1] });
|
|
260
|
+
expect(model.items).toHaveLength(0);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('batch sets model values', async () => {
|
|
264
|
+
const tool = new RSToolAgent();
|
|
265
|
+
const session = tool.createSession();
|
|
266
|
+
tool.addOrUpdateConstituenta(session.sessionId, {
|
|
267
|
+
draft: { id: 1, alias: 'X1', cstType: CstType.BASE, definitionFormal: '' }
|
|
268
|
+
});
|
|
269
|
+
tool.addOrUpdateConstituenta(session.sessionId, {
|
|
270
|
+
draft: { id: 2, alias: 'C1', cstType: CstType.CONSTANT, definitionFormal: '' }
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const model = await tool.setConstituentaValues(session.sessionId, {
|
|
274
|
+
items: [
|
|
275
|
+
{ target: 1, value: { 0: 'a', 1: 'b' } },
|
|
276
|
+
{ target: 2, value: { 0: 'c' } }
|
|
277
|
+
]
|
|
278
|
+
});
|
|
279
|
+
expect(model.items).toHaveLength(2);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('exports and imports model state', async () => {
|
|
283
|
+
const tool = new RSToolAgent();
|
|
284
|
+
const session = tool.createSession();
|
|
285
|
+
buildSampleForm(tool, session.sessionId);
|
|
286
|
+
await tool.setConstituentaValue(session.sessionId, {
|
|
287
|
+
target: 1,
|
|
288
|
+
value: { 0: 'zero' }
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const exported = tool.exportSession(session.sessionId);
|
|
292
|
+
expect(exported).toContain('"model"');
|
|
293
|
+
|
|
294
|
+
const newTool = new RSToolAgent();
|
|
295
|
+
const imported = newTool.importSession(exported);
|
|
296
|
+
const model = newTool.getModelState(imported.sessionId);
|
|
297
|
+
expect(model.items).toHaveLength(1);
|
|
298
|
+
expect(model.items[0]?.value).toEqual({ 0: 'zero' });
|
|
299
|
+
});
|
|
300
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { ModelAdapter } from '../mappers/model-adapter';
|
|
2
|
+
import { SchemaAdapter } from '../mappers/schema-adapter';
|
|
3
|
+
import { SessionStore } from '../session/session-store';
|
|
4
|
+
import { type AnalysisResult, type AnalyzeExpressionInput } from './analysis';
|
|
5
|
+
import {
|
|
6
|
+
type AddOrUpdateConstituentaInput,
|
|
7
|
+
type AddOrUpdateConstituentaResult
|
|
8
|
+
} from './constituenta';
|
|
9
|
+
import { type ListDiagnosticsFilters } from './diagnostic';
|
|
10
|
+
import {
|
|
11
|
+
type EvaluateConstituentaInput,
|
|
12
|
+
type EvaluateExpressionInput,
|
|
13
|
+
type EvaluationResult
|
|
14
|
+
} from './evaluation';
|
|
15
|
+
import {
|
|
16
|
+
type ClearConstituentaValuesInput,
|
|
17
|
+
type RecalculateModelResult,
|
|
18
|
+
type SessionModelState,
|
|
19
|
+
type SetConstituentaValueInput,
|
|
20
|
+
type SetConstituentaValuesInput
|
|
21
|
+
} from './model-value';
|
|
22
|
+
import { type SessionHandle, type SessionRevision, type SessionState } from './session';
|
|
23
|
+
import { CONTRACT_VERSION, type RSToolAgentContract } from './tool-contract';
|
|
24
|
+
|
|
25
|
+
export class RSToolAgent implements RSToolAgentContract {
|
|
26
|
+
public readonly contractVersion = CONTRACT_VERSION;
|
|
27
|
+
private readonly sessions = new SessionStore();
|
|
28
|
+
private readonly adapter = new SchemaAdapter();
|
|
29
|
+
private readonly evaluation = new ModelAdapter();
|
|
30
|
+
|
|
31
|
+
public createSession(initial?: Partial<SessionState>): SessionHandle {
|
|
32
|
+
return this.sessions.create(initial, this.contractVersion);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public addOrUpdateConstituenta(
|
|
36
|
+
sessionId: string,
|
|
37
|
+
input: AddOrUpdateConstituentaInput
|
|
38
|
+
): AddOrUpdateConstituentaResult {
|
|
39
|
+
const envelope = this.sessions.get(sessionId);
|
|
40
|
+
const { result, diagnostics } = this.adapter.analyzeAgainstSession(envelope.state, input.draft);
|
|
41
|
+
const state = this.adapter.mergeStateWithDraft(envelope.state, input.draft, result);
|
|
42
|
+
this.sessions.appendDiagnostics(sessionId, diagnostics);
|
|
43
|
+
return {
|
|
44
|
+
state,
|
|
45
|
+
diagnostics
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public analyzeExpression(sessionId: string, input: AnalyzeExpressionInput): AnalysisResult {
|
|
50
|
+
const envelope = this.sessions.get(sessionId);
|
|
51
|
+
const { result, diagnostics } = this.adapter.analyzeAgainstSession(envelope.state, {
|
|
52
|
+
id: -1,
|
|
53
|
+
alias: '_analysis',
|
|
54
|
+
cstType: input.cstType,
|
|
55
|
+
definitionFormal: input.expression
|
|
56
|
+
});
|
|
57
|
+
this.sessions.appendDiagnostics(
|
|
58
|
+
sessionId,
|
|
59
|
+
diagnostics.map(item => ({ ...item, constituentId: undefined }))
|
|
60
|
+
);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public getFormState(sessionId: string): SessionState {
|
|
65
|
+
const envelope = this.sessions.get(sessionId);
|
|
66
|
+
return structuredClone(envelope.state);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public listDiagnostics(sessionId: string, filters?: ListDiagnosticsFilters) {
|
|
70
|
+
return this.sessions.listDiagnostics(sessionId, filters);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public commitStep(sessionId: string, message?: string): SessionRevision {
|
|
74
|
+
return this.sessions.addRevision(sessionId, message);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public exportSession(sessionId: string): string {
|
|
78
|
+
const envelope = this.sessions.get(sessionId);
|
|
79
|
+
return JSON.stringify(
|
|
80
|
+
{
|
|
81
|
+
contractVersion: this.contractVersion,
|
|
82
|
+
state: envelope.state,
|
|
83
|
+
diagnostics: envelope.diagnostics
|
|
84
|
+
},
|
|
85
|
+
null,
|
|
86
|
+
2
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public importSession(payload: string): SessionHandle {
|
|
91
|
+
const parsed = JSON.parse(payload) as {
|
|
92
|
+
state: SessionState;
|
|
93
|
+
};
|
|
94
|
+
if (!parsed.state.model) {
|
|
95
|
+
parsed.state.model = { items: [] };
|
|
96
|
+
}
|
|
97
|
+
return this.sessions.create(parsed.state, this.contractVersion);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public async setConstituentaValue(
|
|
101
|
+
sessionId: string,
|
|
102
|
+
input: SetConstituentaValueInput
|
|
103
|
+
): Promise<SessionModelState> {
|
|
104
|
+
const envelope = this.sessions.get(sessionId);
|
|
105
|
+
return this.evaluation.setConstituentaValue(envelope.state, input);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public async setConstituentaValues(
|
|
109
|
+
sessionId: string,
|
|
110
|
+
input: SetConstituentaValuesInput
|
|
111
|
+
): Promise<SessionModelState> {
|
|
112
|
+
const envelope = this.sessions.get(sessionId);
|
|
113
|
+
return this.evaluation.setConstituentaValues(envelope.state, input);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public async clearConstituentaValues(
|
|
117
|
+
sessionId: string,
|
|
118
|
+
input: ClearConstituentaValuesInput
|
|
119
|
+
): Promise<SessionModelState> {
|
|
120
|
+
const envelope = this.sessions.get(sessionId);
|
|
121
|
+
return this.evaluation.clearConstituentaValues(envelope.state, input.items);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public getModelState(sessionId: string): SessionModelState {
|
|
125
|
+
const envelope = this.sessions.get(sessionId);
|
|
126
|
+
return structuredClone(envelope.state.model);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public evaluateExpression(sessionId: string, input: EvaluateExpressionInput): EvaluationResult {
|
|
130
|
+
const envelope = this.sessions.get(sessionId);
|
|
131
|
+
return this.evaluation.evaluateExpression(envelope.state, input.expression, input.cstType);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public evaluateConstituenta(sessionId: string, input: EvaluateConstituentaInput): EvaluationResult {
|
|
135
|
+
const envelope = this.sessions.get(sessionId);
|
|
136
|
+
return this.evaluation.evaluateConstituenta(envelope.state, input.constituentId);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public recalculateModel(sessionId: string): RecalculateModelResult {
|
|
140
|
+
const envelope = this.sessions.get(sessionId);
|
|
141
|
+
return this.evaluation.recalculateModel(envelope.state);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ConstituentaState } from './constituenta';
|
|
2
|
+
import { type SessionModelState } from './model-value';
|
|
3
|
+
|
|
4
|
+
export interface SessionHandle {
|
|
5
|
+
sessionId: string;
|
|
6
|
+
contractVersion: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SessionRevision {
|
|
10
|
+
revisionId: string;
|
|
11
|
+
at: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SessionState {
|
|
16
|
+
sessionId: string;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
updatedAt: string;
|
|
19
|
+
revisions: SessionRevision[];
|
|
20
|
+
items: ConstituentaState[];
|
|
21
|
+
model: SessionModelState;
|
|
22
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type AnalysisResult, type AnalyzeExpressionInput } from './analysis';
|
|
2
|
+
import {
|
|
3
|
+
type AddOrUpdateConstituentaInput,
|
|
4
|
+
type AddOrUpdateConstituentaResult
|
|
5
|
+
} from './constituenta';
|
|
6
|
+
import {
|
|
7
|
+
type DiagnosticRecord,
|
|
8
|
+
type ListDiagnosticsFilters
|
|
9
|
+
} from './diagnostic';
|
|
10
|
+
import {
|
|
11
|
+
type EvaluateConstituentaInput,
|
|
12
|
+
type EvaluateExpressionInput,
|
|
13
|
+
type EvaluationResult
|
|
14
|
+
} from './evaluation';
|
|
15
|
+
import {
|
|
16
|
+
type ClearConstituentaValuesInput,
|
|
17
|
+
type RecalculateModelResult,
|
|
18
|
+
type SessionModelState,
|
|
19
|
+
type SetConstituentaValueInput,
|
|
20
|
+
type SetConstituentaValuesInput
|
|
21
|
+
} from './model-value';
|
|
22
|
+
import {
|
|
23
|
+
type SessionHandle,
|
|
24
|
+
type SessionRevision,
|
|
25
|
+
type SessionState
|
|
26
|
+
} from './session';
|
|
27
|
+
|
|
28
|
+
export const CONTRACT_VERSION = '1.2.0';
|
|
29
|
+
|
|
30
|
+
export interface RSToolAgentContract {
|
|
31
|
+
readonly contractVersion: string;
|
|
32
|
+
createSession(initial?: Partial<SessionState>): SessionHandle;
|
|
33
|
+
addOrUpdateConstituenta(sessionId: string, input: AddOrUpdateConstituentaInput): AddOrUpdateConstituentaResult;
|
|
34
|
+
analyzeExpression(sessionId: string, input: AnalyzeExpressionInput): AnalysisResult;
|
|
35
|
+
getFormState(sessionId: string): SessionState;
|
|
36
|
+
listDiagnostics(sessionId: string, filters?: ListDiagnosticsFilters): DiagnosticRecord[];
|
|
37
|
+
commitStep(sessionId: string, message?: string): SessionRevision;
|
|
38
|
+
exportSession(sessionId: string): string;
|
|
39
|
+
importSession(payload: string): SessionHandle;
|
|
40
|
+
setConstituentaValue(sessionId: string, input: SetConstituentaValueInput): Promise<SessionModelState>;
|
|
41
|
+
setConstituentaValues(sessionId: string, input: SetConstituentaValuesInput): Promise<SessionModelState>;
|
|
42
|
+
clearConstituentaValues(sessionId: string, input: ClearConstituentaValuesInput): Promise<SessionModelState>;
|
|
43
|
+
getModelState(sessionId: string): SessionModelState;
|
|
44
|
+
evaluateExpression(sessionId: string, input: EvaluateExpressionInput): EvaluationResult;
|
|
45
|
+
evaluateConstituenta(sessionId: string, input: EvaluateConstituentaInput): EvaluationResult;
|
|
46
|
+
recalculateModel(sessionId: string): RecalculateModelResult;
|
|
47
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type DiagnosticRecord,
|
|
5
|
+
type ListDiagnosticsFilters,
|
|
6
|
+
type SessionHandle,
|
|
7
|
+
type SessionRevision,
|
|
8
|
+
type SessionState
|
|
9
|
+
} from '../models';
|
|
10
|
+
|
|
11
|
+
interface SessionEnvelope {
|
|
12
|
+
state: SessionState;
|
|
13
|
+
diagnostics: DiagnosticRecord[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class SessionStore {
|
|
17
|
+
private sessions = new Map<string, SessionEnvelope>();
|
|
18
|
+
|
|
19
|
+
public create(initial?: Partial<SessionState>, contractVersion?: string): SessionHandle {
|
|
20
|
+
const now = new Date().toISOString();
|
|
21
|
+
const sessionId = initial?.sessionId ?? randomUUID();
|
|
22
|
+
const state: SessionState = {
|
|
23
|
+
sessionId,
|
|
24
|
+
createdAt: initial?.createdAt ?? now,
|
|
25
|
+
updatedAt: now,
|
|
26
|
+
revisions: initial?.revisions ?? [],
|
|
27
|
+
items: initial?.items ?? [],
|
|
28
|
+
model: initial?.model ?? { items: [] }
|
|
29
|
+
};
|
|
30
|
+
this.sessions.set(sessionId, {
|
|
31
|
+
state,
|
|
32
|
+
diagnostics: []
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
sessionId,
|
|
36
|
+
contractVersion: contractVersion ?? '1.0.0'
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public get(sessionId: string): SessionEnvelope {
|
|
41
|
+
const found = this.sessions.get(sessionId);
|
|
42
|
+
if (!found) {
|
|
43
|
+
throw new Error(`Unknown session: ${sessionId}`);
|
|
44
|
+
}
|
|
45
|
+
return found;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public replaceState(sessionId: string, nextState: SessionState): void {
|
|
49
|
+
const found = this.get(sessionId);
|
|
50
|
+
found.state = {
|
|
51
|
+
...nextState,
|
|
52
|
+
updatedAt: new Date().toISOString()
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public addRevision(sessionId: string, message?: string): SessionRevision {
|
|
57
|
+
const found = this.get(sessionId);
|
|
58
|
+
const revision: SessionRevision = {
|
|
59
|
+
revisionId: randomUUID(),
|
|
60
|
+
at: new Date().toISOString(),
|
|
61
|
+
message
|
|
62
|
+
};
|
|
63
|
+
found.state.revisions.push(revision);
|
|
64
|
+
found.state.updatedAt = revision.at;
|
|
65
|
+
return revision;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public appendDiagnostics(sessionId: string, records: DiagnosticRecord[]): void {
|
|
69
|
+
const found = this.get(sessionId);
|
|
70
|
+
found.diagnostics.push(...records);
|
|
71
|
+
found.state.updatedAt = new Date().toISOString();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public listDiagnostics(sessionId: string, filters?: ListDiagnosticsFilters): DiagnosticRecord[] {
|
|
75
|
+
const found = this.get(sessionId);
|
|
76
|
+
if (!filters?.constituentId) {
|
|
77
|
+
return [...found.diagnostics];
|
|
78
|
+
}
|
|
79
|
+
return found.diagnostics.filter(record => record.constituentId === filters.constituentId);
|
|
80
|
+
}
|
|
81
|
+
}
|