@rsconcept/rstool 1.2.2 → 1.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.
Files changed (111) hide show
  1. package/README.md +4 -4
  2. package/dist/{agent-workflow-Gk0Vfnv1.d.ts → agent-workflow-D22FeODL.d.ts} +6 -6
  3. package/dist/{analysis-LLnPhmGa.d.ts → analysis-DA44pfgA.d.ts} +4 -3
  4. package/dist/common-D6TFl6l_.d.ts +11 -0
  5. package/dist/{constituenta-DnGR6bnM.d.ts → constituenta-QY5w1x91.d.ts} +7 -7
  6. package/dist/diagnostic-C6TRoBgt.d.ts +38 -0
  7. package/dist/diagnostic-assembly-DRunIcKQ.js +54 -0
  8. package/dist/diagnostic-assembly-DRunIcKQ.js.map +1 -0
  9. package/dist/{evaluation-Cns8BFm4.d.ts → evaluation-Cd4S7-3t.d.ts} +4 -3
  10. package/dist/index.d.ts +12 -12
  11. package/dist/index.js +4 -3
  12. package/dist/mappers/model-adapter.d.ts +3 -3
  13. package/dist/mappers/model-adapter.js +1 -199
  14. package/dist/mappers/schema-adapter.d.ts +5 -5
  15. package/dist/mappers/schema-adapter.js +8 -8
  16. package/dist/mappers/schema-adapter.js.map +1 -1
  17. package/dist/mappers/types.d.ts +6 -6
  18. package/dist/mappers/types.js +6 -10
  19. package/dist/mappers/types.js.map +1 -1
  20. package/dist/model-adapter-D-YmIvQW.js +225 -0
  21. package/dist/model-adapter-D-YmIvQW.js.map +1 -0
  22. package/dist/{model-value-BbonPzMz.d.ts → model-value-BUhr2IjA.d.ts} +2 -2
  23. package/dist/models/agent-workflow.d.ts +1 -1
  24. package/dist/models/analysis.d.ts +1 -1
  25. package/dist/models/common.d.ts +2 -2
  26. package/dist/models/common.js +1 -1
  27. package/dist/models/constituenta.d.ts +1 -1
  28. package/dist/models/diagnostic.d.ts +2 -2
  29. package/dist/models/diagnostic.js +3 -1
  30. package/dist/models/evaluation.d.ts +1 -1
  31. package/dist/models/index.d.ts +11 -11
  32. package/dist/models/index.js +3 -2
  33. package/dist/models/model-value.d.ts +1 -1
  34. package/dist/models/rstool-agent.d.ts +1 -1
  35. package/dist/models/rstool-agent.js +1 -1
  36. package/dist/models/session.d.ts +1 -1
  37. package/dist/models/tool-contract.d.ts +1 -1
  38. package/dist/models/tool-contract.js +1 -1
  39. package/dist/models/tool-contract.js.map +1 -1
  40. package/dist/{models-9G6ur_pg.js → models-DTLctDmB.js} +72 -15
  41. package/dist/models-DTLctDmB.js.map +1 -0
  42. package/dist/{rstool-agent-D2cQze_b.d.ts → rstool-agent-BMVAyMAG.d.ts} +11 -10
  43. package/dist/session/session-store.d.ts +6 -6
  44. package/dist/session/session-store.js +1 -1
  45. package/dist/{session-ChexW8i7.d.ts → session-CI4rLYby.d.ts} +3 -3
  46. package/dist/{tool-contract-0uRGhEfW.d.ts → tool-contract-sQfr-DYD.d.ts} +12 -12
  47. package/dist/wrapper/stdio-wrapper.js +1 -1
  48. package/docs/BASE-SELECTION.md +92 -0
  49. package/docs/CONCEPTUAL-SCHEMA.md +20 -15
  50. package/docs/CONSTITUENTA.md +3 -3
  51. package/docs/DIAGNOSTICS.md +63 -20
  52. package/docs/DOMAIN.md +27 -21
  53. package/docs/README.md +1 -0
  54. package/docs/SYNTAX.md +3 -2
  55. package/docs/TYPIFICATION.md +3 -9
  56. package/examples/README.md +2 -0
  57. package/examples/agent-client.ts +15 -3
  58. package/examples/audit-diagnostics.ts +40 -0
  59. package/examples/chocolate-nim/build-rsform.ts +15 -0
  60. package/examples/chocolate-nim/build-rsmodel.ts +15 -22
  61. package/examples/chocolate-nim/model-demo.ts +11 -0
  62. package/examples/chocolate-nim/rsform-session.json +329 -310
  63. package/examples/chocolate-nim/rsmodel-session.json +311 -311
  64. package/examples/diagnostics-utils.ts +25 -0
  65. package/examples/expression-bank/bank-constituents.ts +10 -10
  66. package/examples/expression-bank/build-rsform.ts +8 -0
  67. package/examples/expression-bank/rsform-session.json +1501 -1501
  68. package/examples/kinship/build-rsform.ts +16 -1
  69. package/examples/kinship/build-rsmodel.ts +15 -56
  70. package/examples/kinship/model-demo.ts +35 -0
  71. package/examples/kinship/rsform-session.json +300 -215
  72. package/examples/kinship/rsmodel-session.json +216 -216
  73. package/examples/movd/build-rsform.ts +15 -0
  74. package/examples/movd/build-rsmodel.ts +15 -46
  75. package/examples/movd/model-demo.ts +31 -0
  76. package/examples/movd/rsform-session.json +359 -270
  77. package/examples/movd/rsmodel-session.json +263 -263
  78. package/examples/sample/build-rsform.ts +43 -3
  79. package/examples/sample/build-rsmodel.ts +26 -2
  80. package/examples/sample/rsform-session.json +41 -12
  81. package/examples/sample/rsmodel-session.json +37 -37
  82. package/examples/template-apply/build-rsform.ts +26 -9
  83. package/examples/template-apply/rsform-session.json +100 -45
  84. package/package.json +3 -2
  85. package/skills/rstool-helper/EXAMPLES.md +22 -1
  86. package/skills/rstool-helper/GUIDE.md +23 -28
  87. package/skills/rstool-helper/REFERENCE.md +28 -23
  88. package/src/index.ts +7 -4
  89. package/src/mappers/diagnostic-assembly.ts +96 -0
  90. package/src/mappers/diagnostics-collector.test.ts +63 -0
  91. package/src/mappers/diagnostics-collector.ts +73 -0
  92. package/src/mappers/model-adapter.ts +28 -99
  93. package/src/mappers/rsform-builder.ts +127 -0
  94. package/src/mappers/schema-adapter.ts +19 -15
  95. package/src/mappers/types.ts +12 -12
  96. package/src/models/agent-workflow.ts +2 -2
  97. package/src/models/analysis.ts +3 -2
  98. package/src/models/common.ts +0 -12
  99. package/src/models/constituenta.ts +4 -4
  100. package/src/models/diagnostic.ts +15 -10
  101. package/src/models/evaluation.ts +3 -8
  102. package/src/models/index.ts +8 -2
  103. package/src/models/rstool-agent.test.ts +66 -5
  104. package/src/models/rstool-agent.ts +22 -10
  105. package/src/models/tool-contract.ts +4 -4
  106. package/src/session/persistence.ts +2 -2
  107. package/src/session/session-store.ts +14 -7
  108. package/dist/common-DHJalS-Q.d.ts +0 -22
  109. package/dist/diagnostic-D9yl_mEL.d.ts +0 -19
  110. package/dist/mappers/model-adapter.js.map +0 -1
  111. package/dist/models-9G6ur_pg.js.map +0 -1
@@ -27,6 +27,7 @@
27
27
  - API, методы, stdio: [REFERENCE.md](REFERENCE.md).
28
28
  - Примеры: [EXAMPLES.md](EXAMPLES.md).
29
29
  - Термины предметной области: [../../docs/DOMAIN.md](../../docs/DOMAIN.md).
30
+ - **`X#` / `C#` / `Z`:** [../../docs/BASE-SELECTION.md](../../docs/BASE-SELECTION.md) — когда какое основание вводить.
30
31
  - Концептуализация, создание схемы: [../../docs/CONCEPTUAL-SCHEMA.md](../../docs/CONCEPTUAL-SCHEMA.md).
31
32
  - Конституенты и валидация: [../../docs/CONSTITUENTA.md](../../docs/CONSTITUENTA.md).
32
33
  - Синтаксис родов структур: [../../docs/SYNTAX.md](../../docs/SYNTAX.md).
@@ -45,7 +46,7 @@
45
46
  Когда схема уже есть и нужны точечные правки, новые конституенты или проверка формул.
46
47
 
47
48
  1. Продолжи текущую сессию или вызови `ensureSession`; большинство методов создают сессию лениво.
48
- 2. Развивай схему через `applySchemaPatch`: можно опускать `id` и `cstType` для стандартных alias `X/C/S/D/A/F/P`.
49
+ 2. Развивай схему через `applySchemaPatch`: можно опускать `id` и `cstType` — они выводятся из префикса `alias` (`X/C/S/D/F/P/A/T/N`).
49
50
  3. Для пачки конституент передавай все элементы вместе — `applySchemaPatch` упорядочит поставщиков раньше потребителей.
50
51
  4. Перед patch в `applySchemaPatch` при сомнении вызывай `analyzeExpression`.
51
52
  5. Исправляй диагностики по диапазону `from` / `to` в `definitionFormal` ([DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md)).
@@ -58,29 +59,24 @@
58
59
  ### Оценка или правка КС из Portal
59
60
 
60
61
  Когда пользователь даёт UI-ссылку вида `https://portal.acconcept.ru/rsforms/:id`,
61
- не открывай SPA и не парси HTML. Преобразуй ссылку в REST API по правилам
62
- [PORTAL-API.md](../../docs/PORTAL-API.md): метаданные `GET /api/rsforms/:id`,
63
- полная КС `GET /api/rsforms/:id/details`, версия `GET /api/library/:id/versions/:version`.
64
- Если id неизвестен — контекстный поиск `GET /api/library/context-search?q=...`, затем
65
- метаданные по найденным id: `GET /api/library/by-ids?ids=...` (curl и поля поиска — в том же файле).
62
+ не открывай SPA и не парси HTML: преобразуй ссылку в REST-запрос по правилам
63
+ [PORTAL-API.md](../../docs/PORTAL-API.md) — там пути, контекстный поиск, правила доступа и curl.
66
64
 
67
65
  Portal JSON из `/details` не является форматом `exportSession`. Используй `importData(payload)` —
68
- auto определит `portal-details` или `portal-schema`. Явный kind: `'portal-details'`, `'portal-schema'`, `'session'`.
66
+ kind `auto` (default) определит `portal-details` или `portal-schema`. Явный kind: `'portal-details'`, `'portal-schema'`, `'session'`.
69
67
 
70
68
  ### Концептуализация (КС из содержания источника)
71
69
 
72
70
  Когда нужно построить или существенно развить КС по тексту ПО, требованиям или предметному описанию. Правила редукции, слоёв и паттернов — [CONCEPTUAL-SCHEMA.md](../../docs/CONCEPTUAL-SCHEMA.md).
73
71
 
74
72
  1. Собери словарь: неопределяемые понятия, ключевые отношения, сценарий синтеза.
75
- 2. Добавь неопределяемые понятия `X#`, `C#`, `S#` с конвенциями из источника (не копируй дословно).
76
- 3. Проверь независимость неопределяемых понятий — они не выводятся друг из друга.
77
- Если отношение или множество выражается через уже введенные `X#`, `C#`, `S#`, оформляй его как `D#` / `F#` / `P#`.
78
- 4. Введи `F#` и `D#` для раскрытия проекций и функциональных связей в неопределяемых понятиях.
79
- 5. Добавь `A#` на структуры, выражающие ограничения предметной области. Возможно стоит ввести промежуточные производные термы и терм-функции.
80
- 6. Подбери промежуточные производные понятия `D#`, `F#`, `P#` так, чтобы выразить целевые понятия без использования не введенных в схеме понятий.
81
- 7. Перед каждым patch — `analyzeExpression`; при ошибках — [цикл диагностик](#цикл-диагностик).
82
- 8. Сомнительную семантику проверь на маленькой КМ ([цикл КМ](#проверка-на-маленькой-км), [MODEL-TESTING.md](../../docs/MODEL-TESTING.md)).
83
- 9. Перед показом результата пройди [чеклист ревью КС](#ревью-концептуальной-схемы).
73
+ 2. Добавь независимые неопределяемые понятия `X#`, `C#`, `S#` с конвенциями из источника (не копируй дословно). Для выбора `X#` vs `C#` vs `Z` — [BASE-SELECTION.md](../../docs/BASE-SELECTION.md).
74
+ 3. Всё, что выводится из уже введённых понятий, оформляй как `D#` / `F#` / `P#`, а не как новое `X#` / `C#` / `S#`.
75
+ 4. Вводи промежуточные `D#`, `F#`, `P#` для проекций, функциональных связей и повторяющихся фрагментов формул.
76
+ 5. Добавь `A#` и `T#` после терминов, на которые они опираются.
77
+ 6. Перед каждым patch — `analyzeExpression`; при ошибках — [цикл диагностик](#цикл-диагностик).
78
+ 7. Сомнительную семантику проверь на маленькой КМ ([цикл КМ](#проверка-на-маленькой-км), [MODEL-TESTING.md](../../docs/MODEL-TESTING.md)).
79
+ 8. Перед показом результата пройди [чеклист ревью КС](#ревью-концептуальной-схемы).
84
80
 
85
81
  Дальше — обычное [редактирование и проверка](#редактирование-и-проверка-схемы): commit, export.
86
82
 
@@ -88,12 +84,13 @@ auto определит `portal-details` или `portal-schema`. Явный kind
88
84
 
89
85
  Когда `analysis.success === false` или `listDiagnostics` не пуст. Коды и типичные ошибки — [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md).
90
86
 
91
- 1. Запусти `analyzeExpression` (черновик без сохранения).
92
- 2. Прочитай `code`, `from`, `to`, `params` в `analysis.diagnostics` или через `listDiagnostics`.
93
- 3. Сопоставь `code` с исправлением в [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md).
94
- 4. Исправь именно диапазон в `definitionFormal`.
95
- 5. Не повторяй вызов без изменения ввода.
96
- 6. После успеха — ещё один `applySchemaPatch` с исправленным элементом.
87
+ 1. Вызови `listDiagnostics()` — смотри `kind`: `expression` (формула), `schema` (омонимы, дубликаты, термин/конвенция), `model` (интерпретация).
88
+ 2. Для черновика формулы — `analyzeExpression` (без сохранения).
89
+ 3. Прочитай `name`, `severity`, `alias`, `from`, `to`, `params` в `analysis.diagnostics` или через `listDiagnostics({ kind })`.
90
+ 4. Сопоставь `name` (или `code`) с исправлением в [DIAGNOSTICS.md](../../docs/DIAGNOSTICS.md).
91
+ 5. Исправь именно диапазон в `definitionFormal` (expression) или метаданные/данные модели (schema/model).
92
+ 6. Не повторяй вызов без изменения ввода.
93
+ 7. После успеха — ещё один `applySchemaPatch` / `setModelValues` с исправлением.
97
94
 
98
95
  ### Проверка на маленькой КМ
99
96
 
@@ -110,12 +107,9 @@ auto определит `portal-details` или `portal-schema`. Явный kind
110
107
 
111
108
  ### Порядок объявления конституент
112
109
 
113
- 1. В начале схемы — `basic`, `constant`.
114
- 2. Родовые структуры производные и ключевые понятия, аксиомы и утверждения в произвольном порядке.
115
- 3. Конституенты в топологическом порядке.
116
- 4. Предпочитай размещать связанные с родовыми структурами конституенты вместе с этой структурой.
110
+ В начале схемы — `basic` и `constant`, затем независимые `structure`, затем производные (`term`, `function`, `predicate`, `axiom`, `statement`) в топологическом порядке. Производные при родовой структуре держи рядом с ней, если это не ломает зависимости.
117
111
 
118
- Правила валидации по типам — [CONSTITUENTA.md](../../docs/CONSTITUENTA.md).
112
+ Правила валидации по типам — [CONSTITUENTA.md](../../docs/CONSTITUENTA.md); разворачивание понятий при проектировании — [CONCEPTUAL-SCHEMA.md](../../docs/CONCEPTUAL-SCHEMA.md).
119
113
 
120
114
  ## Формат ответа агента
121
115
 
@@ -160,7 +154,7 @@ auto определит `portal-details` или `portal-schema`. Явный kind
160
154
  ### Сессия и rstool
161
155
 
162
156
  - [ ] Используется текущая сессия (`getSessionState`) или явный `sessionId`, если сессий несколько.
163
- - [ ] Для стандартных alias `X/C/S/D/A/F/P` можно не указывать `cstType` в `applySchemaPatch`.
157
+ - [ ] Для стандартных `X/C/S/D/F/P/A/T/N` можно не указывать `cstType` в `applySchemaPatch`.
164
158
  - [ ] Для пачки конституент предпочитай один `applySchemaPatch` с массивом `items`.
165
159
 
166
160
  ### Перед показом, commit и export
@@ -177,6 +171,7 @@ auto определит `portal-details` или `portal-schema`. Явный kind
177
171
  - [ ] Атрибутивная и родоструктурная экспликации не смешаны без явного запроса.
178
172
  - [ ] Нет избыточной проверки принадлежности к собственной типизации (`x∈X1`, если `x` уже в `X1`).
179
173
  - [ ] Неопределяемые понятия независимы; производные отношения вынесены в `D#` / `F#` / `P#`, а не объявлены новыми структурами.
174
+ - [ ] `X#` — только предметные сущности; `C#` — только если нужны порядок/арифметика над именованным конечным набором в КМ; чистая целочисленная математика — `Z` без лишней конституенты ([BASE-SELECTION.md](../../docs/BASE-SELECTION.md)).
180
175
  - [ ] Покрыты главные сущности, роли, отношения, утверждения.
181
176
  - [ ] В текстовых определениях используются только понятия из схемы.
182
177
  - [ ] Повторяющиеся части формальных определений вынесены в производные понятия `D#` и `F#`.
@@ -3,30 +3,30 @@
3
3
  ## Контракт rstool
4
4
 
5
5
  - Пакет: `@rsconcept/rstool`
6
- - Версия контракта: `2.0.0` (`CONTRACT_VERSION`)
6
+ - Версия контракта: `3.0.0` (`CONTRACT_VERSION`)
7
7
  - Основной класс: `RSToolAgent`
8
8
  - Публичные импорты: `@rsconcept/rstool` и `@rsconcept/rstool/wrapper`
9
9
 
10
10
  ### Методы library API
11
11
 
12
- | Метод | Назначение |
13
- | ---------------------------------------------- | -------------------------------------------- |
14
- | `ensureSession(initial?)` | Вернуть текущую сессию или создать новую |
15
- | `createSession(initial?)` | Новая in-memory сессия |
16
- | `getCurrentSession()` | Текущая активная сессия или `null` |
17
- | `setCurrentSession(sessionId)` | Сделать сессию текущей |
18
- | `applySchemaPatch(input, sessionId?)` | Единственный путь правки схемы |
19
- | `getSessionState(detail?, sessionId?)` | `summary` (default) или `full` |
20
- | `listDiagnostics(filters?, sessionId?)` | Активные диагностики |
21
- | `analyzeExpression(input, sessionId?)` | Разбор без сохранения (`recordDiagnostics?`) |
22
- | `commitStep(message?, sessionId?)` | Ревизия |
23
- | `exportSession(sessionId?)` | JSON сессии |
24
- | `exportPortal({ kind, format? }, sessionId?)` | Portal Load from JSON |
25
- | `importData(payload, kind?)` | Импорт сессии / Portal |
26
- | `setModelValues({ set?, clear? }, sessionId?)` | Значения модели |
27
- | `getModelState(sessionId?)` | Состояние интерпретации |
28
- | `evaluate(input, sessionId?)` | Scratch или конституента |
29
- | `recalculateModel(sessionId?)` | Пересчёт |
12
+ | Метод | Назначение |
13
+ | ---------------------------------------------- | ------------------------------------------------------------------ |
14
+ | `ensureSession(initial?)` | Вернуть текущую сессию или создать новую |
15
+ | `createSession(initial?)` | Новая in-memory сессия |
16
+ | `getCurrentSession()` | Текущая активная сессия или `null` |
17
+ | `setCurrentSession(sessionId)` | Сделать сессию текущей |
18
+ | `applySchemaPatch(input, sessionId?)` | Единственный путь правки схемы |
19
+ | `getSessionState(detail?, sessionId?)` | `summary` (default) или `full` |
20
+ | `listDiagnostics(filters?, sessionId?)` | Активные диагностики (`kind`: `expression` \| `schema` \| `model`) |
21
+ | `analyzeExpression(input, sessionId?)` | Разбор без сохранения (`recordDiagnostics?`) |
22
+ | `commitStep(message?, sessionId?)` | Ревизия |
23
+ | `exportSession(sessionId?)` | JSON сессии |
24
+ | `exportPortal({ kind, format? }, sessionId?)` | Portal Load from JSON |
25
+ | `importData(payload, kind?)` | Импорт сессии / Portal (`kind` см. ниже) |
26
+ | `setModelValues({ set?, clear? }, sessionId?)` | Значения модели (async — нужен `await`) |
27
+ | `getModelState(sessionId?)` | Состояние интерпретации |
28
+ | `evaluate(input, sessionId?)` | Scratch или конституента |
29
+ | `recalculateModel(sessionId?)` | Пересчёт |
30
30
 
31
31
  ### `applySchemaPatch`
32
32
 
@@ -38,6 +38,15 @@ tool.applySchemaPatch({
38
38
  });
39
39
  ```
40
40
 
41
+ - `items` упорядочиваются автоматически: поставщики раньше потребителей.
42
+ - `id` и `cstType` можно опускать — выводятся из префикса `alias` (`X/C/S/D/F/P/A/T/N`).
43
+ - `mode`: `'atomic'` (default; откат всей пачки при первой ошибке) или `'best_effort'` (применяются корректные элементы).
44
+ - `commitMessage` фиксирует ревизию после успешного patch.
45
+
46
+ ### `importData(payload, kind?)`
47
+
48
+ `kind`: `'auto'` (default; определяет формат по содержимому), `'session'`, `'portal-details'` (ответ `GET /api/rsforms/:id/details`), `'portal-schema'` (Portal Load from JSON). После импорта новая сессия становится текущей.
49
+
41
50
  ### Протокол stdio
42
51
 
43
52
  Плоские `params` (без `input`). Опционально: переменная окружения `RSTOOL_PERSISTENCE_DIR` — каталог для сохранения сессий между перезапусками обёртки (в library API — `new RSToolAgent({ persistenceDir })`).
@@ -55,10 +64,6 @@ tool.applySchemaPatch({
55
64
  {"id":"10","method":"importData","params":{"payload":"..."}}
56
65
  ```
57
66
 
58
- ### MCP tools
59
-
60
- 17 tools — см. `rstool-mcp/README.md`.
61
-
62
67
  ## Документация
63
68
 
64
69
  Воркфлоу: `GUIDE.md`. Примеры: `EXAMPLES.md`. Язык: `docs/*.md`.
package/src/index.ts CHANGED
@@ -2,14 +2,16 @@
2
2
  * Public API for `@rsconcept/rstool`.
3
3
  *
4
4
  * Primary entry points: {@link RSToolAgent} (in-process) and {@link RSToolWrapperClient}
5
- * (stdio child process). Types describe the v2 agent contract.
5
+ * (stdio child process). Types describe the v3 agent contract.
6
6
  */
7
- export { toPublicAnalysis, toPublicError, type DomainAnalysisLike, type DomainErrorLike } from './mappers/types';
7
+ export { toDiagnostic, toPublicAnalysis, type DomainAnalysisLike, type DomainErrorLike } from './mappers/types';
8
8
  export {
9
9
  CONTRACT_VERSION,
10
10
  CstType,
11
+ DiagnosticKind,
11
12
  EvalStatus,
12
13
  RSErrorCode,
14
+ RSDiagnosticCode,
13
15
  RSToolAgent,
14
16
  ValueClass,
15
17
  type AgentConstituentaPatch,
@@ -20,7 +22,9 @@ export {
20
22
  type BasicBinding,
21
23
  type ConstituentaDraft,
22
24
  type ConstituentaState,
23
- type DiagnosticRecord,
25
+ type CstDiagnostic,
26
+ type Diagnostic,
27
+ type DiagnosticSeverity,
24
28
  type EvaluateInput,
25
29
  type EvaluationResult,
26
30
  type ExportFormat,
@@ -40,7 +44,6 @@ export {
40
44
  type RecalculateModelResult,
41
45
  type RSToolAgentContract,
42
46
  type RSToolAgentOptions,
43
- type RSToolErrorDescription,
44
47
  type RSToolValue,
45
48
  type SessionHandle,
46
49
  type SessionModelState,
@@ -0,0 +1,96 @@
1
+ import {
2
+ type Constituenta,
3
+ type CstDiagnostic,
4
+ DiagnosticKind,
5
+ modelStatusCstDiagnostic,
6
+ RSDiagnosticCode
7
+ } from '@rsconcept/domain/library';
8
+ import { EvalStatus } from '@rsconcept/domain/library/rsmodel';
9
+ import { isCritical, RSErrorCode, type RSErrorDescription } from '@rsconcept/domain/rslang/error';
10
+
11
+ export { DiagnosticKind, RSDiagnosticCode };
12
+
13
+ /** Severity of a {@link Diagnostic}. */
14
+ export type DiagnosticSeverity = 'error' | 'warning';
15
+
16
+ /** Agent-facing diagnostic with redundant fields for autonomous repair. */
17
+ export interface Diagnostic extends CstDiagnostic {
18
+ name: string;
19
+ severity: DiagnosticSeverity;
20
+ constituentId?: number;
21
+ alias?: string;
22
+ expression: string;
23
+ from: number;
24
+ to: number;
25
+ }
26
+
27
+ /** Context for attributing a {@link Diagnostic} to a constituent. */
28
+ export interface DiagnosticTarget {
29
+ constituentId?: number;
30
+ alias?: string;
31
+ }
32
+
33
+ const DIAGNOSTIC_NAMES: ReadonlyMap<number, string> = new Map([
34
+ ...Object.entries(RSErrorCode).map(([name, code]) => [code, name] as const),
35
+ ...Object.entries(RSDiagnosticCode).map(([name, code]) => [code, name] as const)
36
+ ]);
37
+
38
+ export function getDiagnosticName(code: number): string {
39
+ return DIAGNOSTIC_NAMES.get(code) ?? `0x${code.toString(16).toUpperCase()}`;
40
+ }
41
+
42
+ export function getDiagnosticSeverity(code: number): DiagnosticSeverity {
43
+ return isCritical(code as RSErrorCode) ? 'error' : 'warning';
44
+ }
45
+
46
+ export function expandCstDiagnostic(cst: Constituenta, diagnostic: CstDiagnostic): Diagnostic {
47
+ const expression = expressionForCstDiagnostic(cst, diagnostic);
48
+ return {
49
+ ...diagnostic,
50
+ name: getDiagnosticName(diagnostic.code),
51
+ severity: getDiagnosticSeverity(diagnostic.code),
52
+ constituentId: cst.id,
53
+ alias: cst.alias,
54
+ expression,
55
+ from: 0,
56
+ to: diagnostic.kind === DiagnosticKind.MODEL ? 0 : Math.max(expression.length, 1)
57
+ };
58
+ }
59
+
60
+ export function expressionDiagnostic(
61
+ error: RSErrorDescription,
62
+ expression: string,
63
+ target?: DiagnosticTarget
64
+ ): Diagnostic {
65
+ return {
66
+ kind: DiagnosticKind.EXPRESSION,
67
+ code: error.code,
68
+ name: getDiagnosticName(error.code),
69
+ severity: getDiagnosticSeverity(error.code),
70
+ constituentId: target?.constituentId,
71
+ alias: target?.alias,
72
+ expression,
73
+ from: error.from,
74
+ to: error.to,
75
+ ...(error.params ? { params: error.params } : {})
76
+ };
77
+ }
78
+
79
+ export function modelStatusDiagnostic(status: EvalStatus, cst: Constituenta): Diagnostic | null {
80
+ const base = modelStatusCstDiagnostic(status, cst.cst_type);
81
+ return base ? expandCstDiagnostic(cst, base) : null;
82
+ }
83
+
84
+ function expressionForCstDiagnostic(cst: Constituenta, diagnostic: CstDiagnostic): string {
85
+ switch (diagnostic.code) {
86
+ case RSDiagnosticCode.schemaHomonym:
87
+ case RSDiagnosticCode.schemaMissingTerm:
88
+ return cst.term_resolved;
89
+ case RSDiagnosticCode.schemaMissingConvention:
90
+ return cst.convention;
91
+ case RSDiagnosticCode.schemaTypeMismatch:
92
+ return cst.typification_manual;
93
+ default:
94
+ return cst.definition_formal;
95
+ }
96
+ }
@@ -0,0 +1,63 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { CstType, RSDiagnosticCode, type SessionState, ValueClass } from '../models';
4
+ import { collectSessionDiagnostics } from './diagnostics-collector';
5
+
6
+ describe('collectSessionDiagnostics', () => {
7
+ it('collects expression, schema, and model diagnostics together', () => {
8
+ const session = {
9
+ sessionId: 'test',
10
+ alias: '',
11
+ title: '',
12
+ comment: '',
13
+ createdAt: '',
14
+ updatedAt: '',
15
+ revisions: [],
16
+ model: { items: [] },
17
+ items: [
18
+ {
19
+ id: 1,
20
+ alias: 'X1',
21
+ cstType: CstType.BASE,
22
+ definitionFormal: 'Z',
23
+ term: '',
24
+ definitionText: '',
25
+ convention: '',
26
+ analysis: {
27
+ success: false,
28
+ type: null,
29
+ valueClass: null,
30
+ diagnostics: [{ code: 0x8862, from: 0, to: 1, params: [CstType.BASE, 'X1'] }]
31
+ }
32
+ },
33
+ {
34
+ id: 2,
35
+ alias: 'X2',
36
+ cstType: CstType.BASE,
37
+ definitionFormal: '',
38
+ term: 'person',
39
+ definitionText: '',
40
+ convention: 'people',
41
+ analysis: { success: true, type: {}, valueClass: ValueClass.VALUE, diagnostics: [] }
42
+ },
43
+ {
44
+ id: 3,
45
+ alias: 'X3',
46
+ cstType: CstType.BASE,
47
+ definitionFormal: '',
48
+ term: 'Person',
49
+ definitionText: '',
50
+ convention: 'other',
51
+ analysis: { success: true, type: {}, valueClass: ValueClass.VALUE, diagnostics: [] }
52
+ }
53
+ ]
54
+ };
55
+
56
+ const records = collectSessionDiagnostics(session as SessionState);
57
+ expect(records.some(record => record.kind === 'expression')).toBe(true);
58
+ expect(records.some(record => record.code === RSDiagnosticCode.schemaHomonym)).toBe(true);
59
+ expect(records.some(record => record.code === RSDiagnosticCode.schemaMissingTerm)).toBe(true);
60
+ expect(records.some(record => record.code === RSDiagnosticCode.modelEmpty)).toBe(true);
61
+ expect(records.every(record => record.name.length > 0)).toBe(true);
62
+ });
63
+ });
@@ -0,0 +1,73 @@
1
+ import { isModelIssue } from '@rsconcept/domain/library/rsmodel-api';
2
+ import { type RSErrorDescription } from '@rsconcept/domain/rslang';
3
+
4
+ import { type SessionState } from '../models';
5
+ import {
6
+ DiagnosticKind,
7
+ expandCstDiagnostic,
8
+ expressionDiagnostic,
9
+ modelStatusDiagnostic,
10
+ type Diagnostic
11
+ } from './diagnostic-assembly';
12
+ import { ModelAdapter } from './model-adapter';
13
+ import { enrichSessionConstituents } from './rsform-builder';
14
+
15
+ const modelAdapter = new ModelAdapter();
16
+
17
+ /** Collect expression, schema, and model diagnostics for a session (Portal parity). */
18
+ export function collectSessionDiagnostics(session: SessionState): Diagnostic[] {
19
+ const records: Diagnostic[] = [];
20
+
21
+ for (const item of session.items) {
22
+ for (const diagnostic of item.analysis.diagnostics) {
23
+ const base = diagnostic.name
24
+ ? diagnostic
25
+ : expressionDiagnostic(
26
+ {
27
+ code: diagnostic.code,
28
+ from: diagnostic.from,
29
+ to: diagnostic.to,
30
+ params: diagnostic.params
31
+ } as RSErrorDescription,
32
+ item.definitionFormal,
33
+ {
34
+ constituentId: item.id,
35
+ alias: item.alias
36
+ }
37
+ );
38
+ records.push({
39
+ ...base,
40
+ kind: DiagnosticKind.EXPRESSION,
41
+ constituentId: base.constituentId ?? item.id,
42
+ alias: base.alias ?? item.alias
43
+ });
44
+ }
45
+ }
46
+
47
+ const enriched = enrichSessionConstituents(session);
48
+ for (const { cst } of enriched) {
49
+ for (const diagnostic of cst.diagnostics) {
50
+ if (diagnostic.kind !== DiagnosticKind.SCHEMA) {
51
+ continue;
52
+ }
53
+ records.push(expandCstDiagnostic(cst, diagnostic));
54
+ }
55
+ }
56
+
57
+ if (session.items.length > 0) {
58
+ const schema = enriched.map(entry => entry.cst);
59
+ const engine = modelAdapter.createEngine(session);
60
+ engine.recalculateAll();
61
+ for (const cst of schema) {
62
+ if (!isModelIssue(engine, cst)) {
63
+ continue;
64
+ }
65
+ const diagnostic = modelStatusDiagnostic(engine.getCstStatus(cst.id), cst);
66
+ if (diagnostic) {
67
+ records.push(diagnostic);
68
+ }
69
+ }
70
+ }
71
+
72
+ return records;
73
+ }
@@ -1,8 +1,7 @@
1
- import { Graph } from '@rsconcept/domain/graph/graph';
2
- import { extractGlobals } from '@rsconcept/domain/rslang/api';
3
- import { type ExpressionType, RSLangAnalyzer, type Value, type ValueClass } from '@rsconcept/domain/rslang';
1
+ import { DiagnosticKind, expressionDiagnostic } from './diagnostic-assembly';
2
+ import { type RSErrorDescription, type Value } from '@rsconcept/domain/rslang';
4
3
  import { isBaseSet } from '@rsconcept/domain/library/rsform-api';
5
- import { type Constituenta, CstType, type RSForm } from '@rsconcept/domain/library/rsform';
4
+ import { CstType, type RSForm } from '@rsconcept/domain/library/rsform';
6
5
  import { RSEngine, type RSEngineServices } from '@rsconcept/domain/library/rsengine';
7
6
  import { type BasicBinding, EvalStatus, type RSModel } from '@rsconcept/domain/library/rsmodel';
8
7
  import {
@@ -14,14 +13,13 @@ import {
14
13
  } from '@rsconcept/domain/library/rsmodel-api';
15
14
 
16
15
  import {
17
- type ConstituentaState,
18
16
  type EvaluationResult,
19
17
  type RecalculateModelResult,
20
18
  type SessionModelState,
21
19
  type SessionState,
22
20
  type SetConstituentaValueInput
23
21
  } from '../models';
24
- import { toPublicError } from './types';
22
+ import { buildRSFormFromSession } from './rsform-builder';
25
23
 
26
24
  const SESSION_MODEL_ID = 0;
27
25
 
@@ -73,7 +71,14 @@ export class ModelAdapter {
73
71
  ? EvalStatus.EVAL_FAIL
74
72
  : EvalStatus.EMPTY
75
73
  : EvalStatus.HAS_DATA;
76
- return toPublicEvaluationResult(result.value, result.errors, result.iterations, result.cacheHits, status);
74
+ return toPublicEvaluationResult(
75
+ result.value,
76
+ result.errors,
77
+ result.iterations,
78
+ result.cacheHits,
79
+ status,
80
+ expression
81
+ );
77
82
  }
78
83
 
79
84
  public evaluateConstituenta(session: SessionState, constituentId: number): EvaluationResult {
@@ -84,7 +89,15 @@ export class ModelAdapter {
84
89
  const engine = this.createEngine(session);
85
90
  const result = engine.calculateCst(constituentId);
86
91
  const status = engine.getCstStatus(constituentId);
87
- return toPublicEvaluationResult(result.value, result.errors, result.iterations, result.cacheHits, status);
92
+ return toPublicEvaluationResult(
93
+ result.value,
94
+ result.errors,
95
+ result.iterations,
96
+ result.cacheHits,
97
+ status,
98
+ cst.definitionFormal,
99
+ { constituentId: cst.id, alias: cst.alias }
100
+ );
88
101
  }
89
102
 
90
103
  public recalculateModel(session: SessionState): RecalculateModelResult {
@@ -155,55 +168,6 @@ function createInMemoryServices(session: SessionState): RSEngineServices {
155
168
  };
156
169
  }
157
170
 
158
- function buildRSFormFromSession(session: SessionState): RSForm {
159
- const graph = new Graph<number>();
160
- const cstByAlias = new Map<string, Constituenta>();
161
- const cstByID = new Map<number, Constituenta>();
162
- const analyzer = new RSLangAnalyzer();
163
-
164
- const items = session.items.map(item => {
165
- const cst = toFrontendConstituenta(item);
166
- cstByAlias.set(cst.alias, cst);
167
- cstByID.set(cst.id, cst);
168
- graph.addNode(cst.id);
169
- if (item.cstType === CstType.BASE) {
170
- analyzer.addBase(cst.alias);
171
- }
172
- if (cst.effectiveType) {
173
- analyzer.setGlobal(cst.alias, cst.effectiveType, cst.analysis.valueClass as ValueClass | null);
174
- }
175
- return cst;
176
- });
177
-
178
- for (const cst of items) {
179
- for (const alias of extractGlobals(cst.definition_formal)) {
180
- const source = cstByAlias.get(alias);
181
- if (source) {
182
- graph.addEdge(source.id, cst.id);
183
- }
184
- }
185
- }
186
-
187
- return {
188
- id: 0,
189
- items,
190
- cstByAlias,
191
- cstByID,
192
- graph,
193
- analyzer,
194
- inheritance: [],
195
- attribution: [],
196
- attribution_graph: graph.clone(),
197
- oss: [],
198
- models: [],
199
- editors: [],
200
- versions: [],
201
- is_produced: false,
202
- is_attributive: false,
203
- version: 'latest'
204
- } as unknown as RSForm;
205
- }
206
-
207
171
  function buildRSModelFromSession(session: SessionState): RSModel {
208
172
  return {
209
173
  id: SESSION_MODEL_ID,
@@ -217,54 +181,19 @@ function buildRSModelFromSession(session: SessionState): RSModel {
217
181
  } as unknown as RSModel;
218
182
  }
219
183
 
220
- function toFrontendConstituenta(item: ConstituentaState): Constituenta {
221
- const effectiveType = (item.analysis.type ?? null) as ExpressionType | null;
222
- return {
223
- id: item.id,
224
- alias: item.alias,
225
- cst_type: item.cstType,
226
- definition_formal: item.definitionFormal,
227
- definition_raw: item.definitionFormal,
228
- definition_resolved: item.definitionFormal,
229
- term_raw: item.term,
230
- term_resolved: item.term,
231
- term_forms: [],
232
- convention: item.convention,
233
- typification_manual: '',
234
- value_is_property: false,
235
- crucial: false,
236
- attributes: [],
237
- homonyms: [],
238
- formalDuplicates: [],
239
- analysis: {
240
- success: item.analysis.success,
241
- type: effectiveType,
242
- valueClass: item.analysis.valueClass
243
- },
244
- effectiveType,
245
- is_type_mismatch: false,
246
- schema: 0,
247
- cst_class: 'derived',
248
- status: item.analysis.success ? 'verified' : 'incorrect',
249
- is_template: false,
250
- is_simple_expression: true,
251
- parent_schema_index: 0,
252
- parent_schema: null,
253
- is_inherited: false,
254
- has_inherited_children: false,
255
- spawn: [],
256
- spawn_alias: []
257
- } as unknown as Constituenta;
258
- }
259
-
260
184
  function toPublicEvaluationResult(
261
185
  value: Value | null,
262
186
  errors: { code: number; from: number; to: number; params?: readonly string[] }[],
263
187
  iterations: number,
264
188
  cacheHits: number,
265
- status: EvalStatus
189
+ status: EvalStatus,
190
+ expression: string,
191
+ target?: { constituentId?: number; alias?: string }
266
192
  ): EvaluationResult {
267
- const diagnostics = errors.map(toPublicError);
193
+ const diagnostics = errors.map(error => ({
194
+ ...expressionDiagnostic(error as RSErrorDescription, expression, target),
195
+ kind: DiagnosticKind.MODEL
196
+ }));
268
197
  return {
269
198
  success: diagnostics.length === 0 && value !== null,
270
199
  value: value as EvaluationResult['value'],