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