@rsconcept/rstool 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/{agent-workflow-Gk0Vfnv1.d.ts → agent-workflow-D22FeODL.d.ts} +6 -6
- package/dist/{analysis-LLnPhmGa.d.ts → analysis-DA44pfgA.d.ts} +4 -3
- package/dist/common-D6TFl6l_.d.ts +11 -0
- package/dist/{constituenta-DnGR6bnM.d.ts → constituenta-QY5w1x91.d.ts} +7 -7
- package/dist/diagnostic-C6TRoBgt.d.ts +38 -0
- package/dist/diagnostic-assembly-DRunIcKQ.js +54 -0
- package/dist/diagnostic-assembly-DRunIcKQ.js.map +1 -0
- package/dist/{evaluation-Cns8BFm4.d.ts → evaluation-Cd4S7-3t.d.ts} +4 -3
- package/dist/index.d.ts +12 -12
- package/dist/index.js +4 -3
- package/dist/mappers/model-adapter.d.ts +3 -3
- package/dist/mappers/model-adapter.js +1 -199
- package/dist/mappers/schema-adapter.d.ts +5 -5
- package/dist/mappers/schema-adapter.js +13 -13
- package/dist/mappers/schema-adapter.js.map +1 -1
- package/dist/mappers/types.d.ts +6 -6
- package/dist/mappers/types.js +6 -10
- package/dist/mappers/types.js.map +1 -1
- package/dist/model-adapter-D-YmIvQW.js +225 -0
- package/dist/model-adapter-D-YmIvQW.js.map +1 -0
- package/dist/{model-value-BbonPzMz.d.ts → model-value-BUhr2IjA.d.ts} +2 -2
- package/dist/models/agent-workflow.d.ts +1 -1
- package/dist/models/analysis.d.ts +1 -1
- package/dist/models/common.d.ts +2 -2
- package/dist/models/common.js +1 -1
- package/dist/models/constituenta.d.ts +1 -1
- package/dist/models/diagnostic.d.ts +2 -2
- package/dist/models/diagnostic.js +3 -1
- package/dist/models/evaluation.d.ts +1 -1
- package/dist/models/index.d.ts +11 -11
- package/dist/models/index.js +3 -2
- package/dist/models/model-value.d.ts +1 -1
- package/dist/models/rstool-agent.d.ts +1 -1
- package/dist/models/rstool-agent.js +1 -1
- package/dist/models/session.d.ts +1 -1
- package/dist/models/tool-contract.d.ts +1 -1
- package/dist/models/tool-contract.js +1 -1
- package/dist/models/tool-contract.js.map +1 -1
- package/dist/{models-9G6ur_pg.js → models-B255GaEC.js} +76 -14
- package/dist/models-B255GaEC.js.map +1 -0
- package/dist/{rstool-agent-D2cQze_b.d.ts → rstool-agent-BGQAzFly.d.ts} +12 -10
- package/dist/session/session-store.d.ts +6 -6
- package/dist/session/session-store.js +1 -1
- package/dist/{session-ChexW8i7.d.ts → session-CI4rLYby.d.ts} +3 -3
- package/dist/{tool-contract-0uRGhEfW.d.ts → tool-contract-sQfr-DYD.d.ts} +12 -12
- package/dist/wrapper/stdio-wrapper.js +1 -1
- package/docs/DIAGNOSTICS.md +63 -20
- package/examples/README.md +2 -0
- package/examples/agent-client.ts +15 -3
- package/examples/audit-diagnostics.ts +40 -0
- package/examples/chocolate-nim/build-rsform.ts +15 -0
- package/examples/chocolate-nim/build-rsmodel.ts +15 -22
- package/examples/chocolate-nim/model-demo.ts +11 -0
- package/examples/chocolate-nim/rsform-session.json +329 -310
- package/examples/chocolate-nim/rsmodel-session.json +311 -311
- package/examples/diagnostics-utils.ts +27 -0
- package/examples/expression-bank/bank-constituents.ts +10 -10
- package/examples/expression-bank/build-rsform.ts +8 -0
- package/examples/expression-bank/rsform-session.json +1501 -1501
- package/examples/kinship/build-rsform.ts +16 -1
- package/examples/kinship/build-rsmodel.ts +15 -56
- package/examples/kinship/model-demo.ts +35 -0
- package/examples/kinship/rsform-session.json +300 -215
- package/examples/kinship/rsmodel-session.json +216 -216
- package/examples/movd/build-rsform.ts +15 -0
- package/examples/movd/build-rsmodel.ts +15 -46
- package/examples/movd/model-demo.ts +31 -0
- package/examples/movd/rsform-session.json +359 -270
- package/examples/movd/rsmodel-session.json +263 -263
- package/examples/sample/build-rsform.ts +43 -3
- package/examples/sample/build-rsmodel.ts +26 -2
- package/examples/sample/rsform-session.json +41 -12
- package/examples/sample/rsmodel-session.json +37 -37
- package/examples/template-apply/build-rsform.ts +31 -18
- package/examples/template-apply/rsform-session.json +100 -45
- package/package.json +3 -2
- package/skills/rstool-helper/EXAMPLES.md +3 -1
- package/skills/rstool-helper/GUIDE.md +7 -6
- package/skills/rstool-helper/REFERENCE.md +19 -19
- package/src/index.ts +7 -4
- package/src/mappers/diagnostic-assembly.ts +96 -0
- package/src/mappers/diagnostics-collector.test.ts +92 -0
- package/src/mappers/diagnostics-collector.ts +77 -0
- package/src/mappers/model-adapter.ts +29 -100
- package/src/mappers/rsform-builder.ts +127 -0
- package/src/mappers/schema-adapter.ts +15 -15
- package/src/mappers/types.ts +12 -12
- package/src/models/agent-workflow.ts +2 -2
- package/src/models/analysis.ts +3 -2
- package/src/models/common.ts +0 -12
- package/src/models/constituenta.ts +4 -4
- package/src/models/diagnostic.ts +15 -10
- package/src/models/evaluation.ts +3 -8
- package/src/models/index.ts +8 -2
- package/src/models/rstool-agent.test.ts +92 -5
- package/src/models/rstool-agent.ts +29 -11
- package/src/models/tool-contract.ts +4 -4
- package/src/session/persistence.ts +2 -2
- package/src/session/session-store.ts +14 -7
- package/dist/common-DHJalS-Q.d.ts +0 -22
- package/dist/diagnostic-D9yl_mEL.d.ts +0 -19
- package/dist/mappers/model-adapter.js.map +0 -1
- package/dist/models-9G6ur_pg.js.map +0 -1
|
@@ -6,33 +6,33 @@ import {
|
|
|
6
6
|
type AnalysisResult,
|
|
7
7
|
type ConstituentaDraft,
|
|
8
8
|
type ConstituentaState,
|
|
9
|
-
type
|
|
9
|
+
type Diagnostic,
|
|
10
10
|
type SessionState
|
|
11
11
|
} from '../models';
|
|
12
|
-
import { toPublicAnalysis
|
|
12
|
+
import { toPublicAnalysis } from './types';
|
|
13
13
|
|
|
14
14
|
export class SchemaAdapter {
|
|
15
15
|
public analyzeAgainstSession(
|
|
16
16
|
session: SessionState,
|
|
17
17
|
draft: ConstituentaDraft
|
|
18
|
-
): { result: AnalysisResult; diagnostics:
|
|
18
|
+
): { result: AnalysisResult; diagnostics: Diagnostic[] } {
|
|
19
19
|
const analyzer = this.buildAnalyzer(session);
|
|
20
20
|
const schema = this.toPseudoRSFormState(session, analyzer);
|
|
21
21
|
const analysis = getAnalysisFor(draft.definitionFormal, draft.cstType, schema as unknown as RSForm, draft.alias);
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
const target = { constituentId: draft.id, alias: draft.alias };
|
|
23
|
+
const result = toPublicAnalysis(
|
|
24
|
+
{
|
|
25
|
+
success: analysis.success,
|
|
26
|
+
type: analysis.type as Record<string, unknown> | null,
|
|
27
|
+
valueClass: analysis.valueClass,
|
|
28
|
+
errors: analysis.errors
|
|
29
|
+
},
|
|
30
|
+
draft.definitionFormal,
|
|
31
|
+
target
|
|
32
|
+
);
|
|
28
33
|
return {
|
|
29
34
|
result,
|
|
30
|
-
diagnostics:
|
|
31
|
-
sessionId: session.sessionId,
|
|
32
|
-
constituentId: draft.id,
|
|
33
|
-
expression: draft.definitionFormal,
|
|
34
|
-
error: toPublicError(error)
|
|
35
|
-
}))
|
|
35
|
+
diagnostics: result.diagnostics
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
|
package/src/mappers/types.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { DiagnosticKind, type DiagnosticTarget, expressionDiagnostic } from './diagnostic-assembly';
|
|
2
|
+
import { type RSErrorDescription, type ValueClass } from '@rsconcept/domain/rslang';
|
|
2
3
|
|
|
3
|
-
import { type AnalysisResult, type
|
|
4
|
+
import { type AnalysisResult, type Diagnostic } from '../models';
|
|
4
5
|
|
|
5
|
-
/** Minimal domain error shape before mapping to {@link
|
|
6
|
+
/** Minimal domain error shape before mapping to {@link Diagnostic}. */
|
|
6
7
|
export interface DomainErrorLike {
|
|
7
8
|
code: number;
|
|
8
9
|
from: number;
|
|
@@ -19,21 +20,20 @@ export interface DomainAnalysisLike {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/** Map a domain parser/semantic error to the public diagnostic shape. */
|
|
22
|
-
export function
|
|
23
|
-
return
|
|
24
|
-
code: error.code,
|
|
25
|
-
from: error.from,
|
|
26
|
-
to: error.to,
|
|
27
|
-
params: error.params
|
|
28
|
-
};
|
|
23
|
+
export function toDiagnostic(error: DomainErrorLike, expression: string, target?: DiagnosticTarget): Diagnostic {
|
|
24
|
+
return expressionDiagnostic(error as RSErrorDescription, expression, target);
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
/** Map a domain analysis result to the public {@link AnalysisResult} shape. */
|
|
32
|
-
export function toPublicAnalysis(
|
|
28
|
+
export function toPublicAnalysis(
|
|
29
|
+
analysis: DomainAnalysisLike,
|
|
30
|
+
expression: string,
|
|
31
|
+
target?: DiagnosticTarget
|
|
32
|
+
): AnalysisResult {
|
|
33
33
|
return {
|
|
34
34
|
success: analysis.success,
|
|
35
35
|
type: analysis.type,
|
|
36
36
|
valueClass: analysis.valueClass,
|
|
37
|
-
diagnostics: analysis.errors.map(
|
|
37
|
+
diagnostics: analysis.errors.map(error => expressionDiagnostic(error as RSErrorDescription, expression, target))
|
|
38
38
|
};
|
|
39
39
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type CstType } from './common';
|
|
2
2
|
import { type ApplyConstituentsMode, type ApplyConstituentsResult, type ConstituentaDraft } from './constituenta';
|
|
3
|
-
import { type
|
|
3
|
+
import { type Diagnostic } from './diagnostic';
|
|
4
4
|
import { type SessionHandle, type SessionRevision, type SessionState } from './session';
|
|
5
5
|
|
|
6
6
|
/** Level of detail returned by {@link RSToolAgent.getSessionState}. */
|
|
@@ -54,7 +54,7 @@ export interface SessionSummary {
|
|
|
54
54
|
modelItemCount: number;
|
|
55
55
|
diagnosticsCount: number;
|
|
56
56
|
items: SessionSummaryItem[];
|
|
57
|
-
diagnostics:
|
|
57
|
+
diagnostics: Diagnostic[];
|
|
58
58
|
lastRevision?: SessionRevision;
|
|
59
59
|
}
|
|
60
60
|
|
package/src/models/analysis.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Diagnostic } from './diagnostic';
|
|
2
|
+
import { type CstType, type ValueClass } from './common';
|
|
2
3
|
|
|
3
4
|
/** Input for {@link RSToolAgent.analyzeExpression}. */
|
|
4
5
|
export interface AnalyzeExpressionInput {
|
|
@@ -16,5 +17,5 @@ export interface AnalysisResult {
|
|
|
16
17
|
/** Inferred RS type AST as a plain object, or `null` on failure. */
|
|
17
18
|
type: Record<string, unknown> | null;
|
|
18
19
|
valueClass: ValueClass | null;
|
|
19
|
-
diagnostics:
|
|
20
|
+
diagnostics: Diagnostic[];
|
|
20
21
|
}
|
package/src/models/common.ts
CHANGED
|
@@ -10,15 +10,3 @@ export type { BasicBinding };
|
|
|
10
10
|
|
|
11
11
|
/** Runtime evaluation value: number, nested array (set/tuple), or boolean 0/1. */
|
|
12
12
|
export type RSToolValue = number | RSToolValue[];
|
|
13
|
-
|
|
14
|
-
/** Language or validation error with source span and optional format parameters. */
|
|
15
|
-
export interface RSToolErrorDescription {
|
|
16
|
-
/** Numeric error code (see {@link RSErrorCode}). */
|
|
17
|
-
code: number;
|
|
18
|
-
/** Start offset in the analyzed expression. */
|
|
19
|
-
from: number;
|
|
20
|
-
/** End offset in the analyzed expression. */
|
|
21
|
-
to: number;
|
|
22
|
-
/** Interpolation values for localized error messages. */
|
|
23
|
-
params?: readonly string[];
|
|
24
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AnalysisResult } from './analysis';
|
|
2
2
|
import { type CstType } from './common';
|
|
3
|
-
import { type
|
|
3
|
+
import { type Diagnostic } from './diagnostic';
|
|
4
4
|
|
|
5
5
|
/** Constituent payload before analysis and merge into session state. */
|
|
6
6
|
export interface ConstituentaDraft {
|
|
@@ -33,7 +33,7 @@ export interface AddOrUpdateConstituentaInput {
|
|
|
33
33
|
|
|
34
34
|
export interface AddOrUpdateConstituentaResult {
|
|
35
35
|
state: ConstituentaState;
|
|
36
|
-
diagnostics:
|
|
36
|
+
diagnostics: Diagnostic[];
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** How a multi-draft apply handles partial failures. */
|
|
@@ -48,6 +48,6 @@ export interface ApplyConstituentsInput {
|
|
|
48
48
|
export interface ApplyConstituentsResult {
|
|
49
49
|
success: boolean;
|
|
50
50
|
applied: ConstituentaState[];
|
|
51
|
-
failed: Array<{ draft: ConstituentaDraft; diagnostics:
|
|
52
|
-
diagnostics:
|
|
51
|
+
failed: Array<{ draft: ConstituentaDraft; diagnostics: Diagnostic[] }>;
|
|
52
|
+
diagnostics: Diagnostic[];
|
|
53
53
|
}
|
package/src/models/diagnostic.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type CstDiagnostic, DiagnosticKind, RSDiagnosticCode } from '@rsconcept/domain/library';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
export {
|
|
4
|
+
type Diagnostic,
|
|
5
|
+
type DiagnosticSeverity,
|
|
6
|
+
type DiagnosticTarget,
|
|
7
|
+
expandCstDiagnostic,
|
|
8
|
+
expressionDiagnostic,
|
|
9
|
+
getDiagnosticName,
|
|
10
|
+
getDiagnosticSeverity,
|
|
11
|
+
modelStatusDiagnostic
|
|
12
|
+
} from '../mappers/diagnostic-assembly';
|
|
13
|
+
|
|
14
|
+
export { type CstDiagnostic, DiagnosticKind, RSDiagnosticCode };
|
|
12
15
|
|
|
13
16
|
/** Filters for {@link RSToolAgent.listDiagnostics}. */
|
|
14
17
|
export interface ListDiagnosticsFilters {
|
|
15
18
|
constituentId?: number;
|
|
19
|
+
kind?: DiagnosticKind;
|
|
20
|
+
severity?: import('../mappers/diagnostic-assembly').DiagnosticSeverity;
|
|
16
21
|
}
|
package/src/models/evaluation.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type CstType,
|
|
4
|
-
type EvalStatus,
|
|
5
|
-
type RSToolErrorDescription,
|
|
6
|
-
type RSToolValue
|
|
7
|
-
} from './common';
|
|
1
|
+
import { type BasicBinding, type CstType, type EvalStatus, type RSToolValue } from './common';
|
|
2
|
+
import { type Diagnostic } from './diagnostic';
|
|
8
3
|
|
|
9
4
|
export interface EvaluateExpressionInput {
|
|
10
5
|
expression: string;
|
|
@@ -32,5 +27,5 @@ export interface EvaluationResult {
|
|
|
32
27
|
status: EvalStatus;
|
|
33
28
|
iterations: number;
|
|
34
29
|
cacheHits: number;
|
|
35
|
-
diagnostics:
|
|
30
|
+
diagnostics: Diagnostic[];
|
|
36
31
|
}
|
package/src/models/index.ts
CHANGED
|
@@ -14,11 +14,17 @@ export {
|
|
|
14
14
|
RSErrorCode,
|
|
15
15
|
ValueClass,
|
|
16
16
|
type BasicBinding,
|
|
17
|
-
type RSToolErrorDescription,
|
|
18
17
|
type RSToolValue
|
|
19
18
|
} from './common';
|
|
20
19
|
export { type ApplyConstituentsMode, type ConstituentaDraft, type ConstituentaState } from './constituenta';
|
|
21
|
-
export {
|
|
20
|
+
export {
|
|
21
|
+
type CstDiagnostic,
|
|
22
|
+
type Diagnostic,
|
|
23
|
+
DiagnosticKind,
|
|
24
|
+
type DiagnosticSeverity,
|
|
25
|
+
type ListDiagnosticsFilters,
|
|
26
|
+
RSDiagnosticCode
|
|
27
|
+
} from './diagnostic';
|
|
22
28
|
export { type EvaluateInput, type EvaluationResult } from './evaluation';
|
|
23
29
|
export {
|
|
24
30
|
type ExportFormat,
|
|
@@ -6,7 +6,7 @@ import { describe, expect, it } from 'vitest';
|
|
|
6
6
|
|
|
7
7
|
import { TUPLE_ID } from '@rsconcept/domain';
|
|
8
8
|
|
|
9
|
-
import { CstType, EvalStatus, RSErrorCode, RSToolAgent } from './index';
|
|
9
|
+
import { CstType, EvalStatus, RSErrorCode, RSDiagnosticCode, RSToolAgent } from './index';
|
|
10
10
|
|
|
11
11
|
function buildSampleForm(tool: RSToolAgent, sessionId: string) {
|
|
12
12
|
tool.applySchemaPatch(
|
|
@@ -44,6 +44,7 @@ describe('RSToolAgent', () => {
|
|
|
44
44
|
const analysis = tool.analyzeExpression({ expression: '(', cstType: CstType.TERM }, session.sessionId);
|
|
45
45
|
expect(analysis.success).toBe(false);
|
|
46
46
|
expect(analysis.diagnostics.length).toBeGreaterThan(0);
|
|
47
|
+
expect(analysis.diagnostics.every(record => record.constituentId === undefined)).toBe(true);
|
|
47
48
|
});
|
|
48
49
|
|
|
49
50
|
it('rejects formal definition for constants', () => {
|
|
@@ -56,7 +57,7 @@ describe('RSToolAgent', () => {
|
|
|
56
57
|
session.sessionId
|
|
57
58
|
);
|
|
58
59
|
expect(result.success).toBe(false);
|
|
59
|
-
expect(result.failed[0]?.diagnostics[0]?.
|
|
60
|
+
expect(result.failed[0]?.diagnostics[0]?.code).toBe(RSErrorCode.definitionNotAllowed);
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
it('rejects formal definition for basic sets', () => {
|
|
@@ -69,7 +70,7 @@ describe('RSToolAgent', () => {
|
|
|
69
70
|
session.sessionId
|
|
70
71
|
);
|
|
71
72
|
expect(result.success).toBe(false);
|
|
72
|
-
expect(result.failed[0]?.diagnostics[0]?.
|
|
73
|
+
expect(result.failed[0]?.diagnostics[0]?.code).toBe(RSErrorCode.definitionNotAllowed);
|
|
73
74
|
});
|
|
74
75
|
|
|
75
76
|
it('returns known analysis for empty base definition', () => {
|
|
@@ -412,6 +413,27 @@ describe('RSToolAgent modeling and evaluation', () => {
|
|
|
412
413
|
expect(model.items).toHaveLength(1);
|
|
413
414
|
expect(model.items[0]?.value).toEqual({ 0: 'zero' });
|
|
414
415
|
});
|
|
416
|
+
|
|
417
|
+
it('preserves exported diagnostics on session import', () => {
|
|
418
|
+
const tool = new RSToolAgent();
|
|
419
|
+
const session = tool.createSession();
|
|
420
|
+
tool.applySchemaPatch(
|
|
421
|
+
{
|
|
422
|
+
items: [
|
|
423
|
+
{ alias: 'X1', term: 'person' },
|
|
424
|
+
{ alias: 'X2', term: 'Person' }
|
|
425
|
+
]
|
|
426
|
+
},
|
|
427
|
+
session.sessionId
|
|
428
|
+
);
|
|
429
|
+
const beforeImport = tool.listDiagnostics(undefined, session.sessionId);
|
|
430
|
+
expect(beforeImport.some(record => record.code === RSDiagnosticCode.schemaHomonym)).toBe(true);
|
|
431
|
+
|
|
432
|
+
const exported = tool.exportSession(session.sessionId);
|
|
433
|
+
const imported = tool.importData(exported, 'session');
|
|
434
|
+
const afterImport = tool.listDiagnostics(undefined, imported.sessionId);
|
|
435
|
+
expect(afterImport).toEqual(beforeImport);
|
|
436
|
+
});
|
|
415
437
|
});
|
|
416
438
|
|
|
417
439
|
describe('RSToolAgent agent ergonomics', () => {
|
|
@@ -474,10 +496,72 @@ describe('RSToolAgent agent ergonomics', () => {
|
|
|
474
496
|
{ mode: 'best_effort', items: [{ alias: 'X1', cstType: CstType.BASE, definitionFormal: 'Z' }] },
|
|
475
497
|
session.sessionId
|
|
476
498
|
);
|
|
477
|
-
|
|
499
|
+
const expressionDiags = tool.listDiagnostics({ kind: 'expression' }, session.sessionId);
|
|
500
|
+
expect(expressionDiags).toHaveLength(1);
|
|
501
|
+
expect(expressionDiags[0]?.code).toBe(RSErrorCode.definitionNotAllowed);
|
|
478
502
|
|
|
479
503
|
tool.applySchemaPatch({ items: [{ alias: 'X1', cstType: CstType.BASE, definitionFormal: '' }] }, session.sessionId);
|
|
480
|
-
expect(tool.listDiagnostics(
|
|
504
|
+
expect(tool.listDiagnostics({ kind: 'expression' }, session.sessionId)).toHaveLength(0);
|
|
505
|
+
expect(tool.listDiagnostics({ kind: 'schema' }, session.sessionId).length).toBeGreaterThan(0);
|
|
506
|
+
expect(tool.listDiagnostics({ kind: 'model' }, session.sessionId)).toHaveLength(0);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('reports schema homonyms and missing convention', () => {
|
|
510
|
+
const tool = new RSToolAgent();
|
|
511
|
+
const session = tool.createSession();
|
|
512
|
+
tool.applySchemaPatch(
|
|
513
|
+
{
|
|
514
|
+
items: [
|
|
515
|
+
{ alias: 'X1', term: 'person', convention: 'people' },
|
|
516
|
+
{ alias: 'X2', term: 'Person', convention: 'other people' }
|
|
517
|
+
]
|
|
518
|
+
},
|
|
519
|
+
session.sessionId
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
const homonyms = tool.listDiagnostics({ kind: 'schema' }, session.sessionId);
|
|
523
|
+
expect(homonyms.some(record => record.code === RSDiagnosticCode.schemaHomonym)).toBe(true);
|
|
524
|
+
expect(homonyms.filter(record => record.code === RSDiagnosticCode.schemaHomonym)).toHaveLength(2);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it('reports formal duplicate definitions', () => {
|
|
528
|
+
const tool = new RSToolAgent();
|
|
529
|
+
const session = tool.createSession();
|
|
530
|
+
tool.applySchemaPatch(
|
|
531
|
+
{
|
|
532
|
+
items: [
|
|
533
|
+
{ alias: 'X1', convention: 'base' },
|
|
534
|
+
{ alias: 'S1', definitionFormal: 'ℬ(X1×X1)', convention: 'pairs' },
|
|
535
|
+
{ alias: 'D1', definitionFormal: 'Pr1(S1)' },
|
|
536
|
+
{ alias: 'D2', definitionFormal: 'Pr1(S1)' }
|
|
537
|
+
]
|
|
538
|
+
},
|
|
539
|
+
session.sessionId
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
const duplicates = tool
|
|
543
|
+
.listDiagnostics({ kind: 'schema' }, session.sessionId)
|
|
544
|
+
.filter(record => record.code === RSDiagnosticCode.schemaFormalDuplicate);
|
|
545
|
+
expect(duplicates).toHaveLength(2);
|
|
546
|
+
expect(duplicates[0]?.params?.[0]).toContain('D2');
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('reports model diagnostics for empty basic sets after model refresh', () => {
|
|
550
|
+
const tool = new RSToolAgent();
|
|
551
|
+
const session = tool.createSession();
|
|
552
|
+
tool.applySchemaPatch(
|
|
553
|
+
{
|
|
554
|
+
items: [{ alias: 'X1', convention: 'base', term: 'element' }]
|
|
555
|
+
},
|
|
556
|
+
session.sessionId
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
expect(tool.listDiagnostics({ kind: 'model' }, session.sessionId)).toHaveLength(0);
|
|
560
|
+
|
|
561
|
+
tool.recalculateModel(session.sessionId);
|
|
562
|
+
|
|
563
|
+
const modelIssues = tool.listDiagnostics({ kind: 'model' }, session.sessionId);
|
|
564
|
+
expect(modelIssues.some(record => record.code === RSDiagnosticCode.modelEmpty)).toBe(true);
|
|
481
565
|
});
|
|
482
566
|
|
|
483
567
|
it('does not record analyzeExpression diagnostics by default', () => {
|
|
@@ -919,6 +1003,9 @@ describe('RSToolAgent modeling semantics', () => {
|
|
|
919
1003
|
const a1 = recalculated.items.find(item => item.alias === 'A1');
|
|
920
1004
|
expect(a1?.status).toBe(EvalStatus.AXIOM_FALSE);
|
|
921
1005
|
expect(a1?.value).toBe(0);
|
|
1006
|
+
|
|
1007
|
+
const modelIssues = tool.listDiagnostics({ kind: 'model' }, session.sessionId);
|
|
1008
|
+
expect(modelIssues.some(record => record.code === RSDiagnosticCode.modelAxiomFalse)).toBe(true);
|
|
922
1009
|
});
|
|
923
1010
|
|
|
924
1011
|
it('throws when evaluate input is incomplete', () => {
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
portalSchemaToDrafts,
|
|
5
5
|
portalSchemaToSessionSeed
|
|
6
6
|
} from '../mappers/portal-adapter';
|
|
7
|
+
import { collectModelDiagnostics, collectSchemaDiagnostics } from '../mappers/diagnostics-collector';
|
|
7
8
|
import { ModelAdapter } from '../mappers/model-adapter';
|
|
8
9
|
import { SchemaAdapter } from '../mappers/schema-adapter';
|
|
9
10
|
import { orderDrafts, reorderSessionItemsByDrafts } from '../session/batch-apply';
|
|
@@ -26,7 +27,7 @@ import {
|
|
|
26
27
|
type ConstituentaDraft,
|
|
27
28
|
type ConstituentaState
|
|
28
29
|
} from './constituenta';
|
|
29
|
-
import { type
|
|
30
|
+
import { type Diagnostic, type ListDiagnosticsFilters } from './diagnostic';
|
|
30
31
|
import { type EvaluateInput, type EvaluationResult } from './evaluation';
|
|
31
32
|
import { detectImportKind, parseImportPayload } from './import-detect';
|
|
32
33
|
import { type ExportPortalInput, type ExportPortalResult, type ImportDataKind } from './import-export';
|
|
@@ -190,14 +191,11 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
190
191
|
cstType: input.cstType,
|
|
191
192
|
definitionFormal: input.expression
|
|
192
193
|
});
|
|
194
|
+
const sanitizedDiagnostics = diagnostics.map(item => ({ ...item, constituentId: undefined }));
|
|
193
195
|
if (input.recordDiagnostics) {
|
|
194
|
-
this.sessions.
|
|
195
|
-
id,
|
|
196
|
-
undefined,
|
|
197
|
-
diagnostics.map(item => ({ ...item, constituentId: undefined }))
|
|
198
|
-
);
|
|
196
|
+
this.sessions.setDiagnostics(id, [...collectSchemaDiagnostics(envelope.state), ...sanitizedDiagnostics]);
|
|
199
197
|
}
|
|
200
|
-
return result;
|
|
198
|
+
return { ...result, diagnostics: sanitizedDiagnostics };
|
|
201
199
|
}
|
|
202
200
|
|
|
203
201
|
/** @inheritdoc */
|
|
@@ -263,9 +261,11 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
263
261
|
const model = await this.evaluation.setConstituentaValues(state, { items: input.set });
|
|
264
262
|
state = { ...state, model };
|
|
265
263
|
this.sessions.replaceState(id, state);
|
|
264
|
+
this.refreshModelDiagnostics(id);
|
|
266
265
|
return model;
|
|
267
266
|
}
|
|
268
267
|
|
|
268
|
+
this.refreshModelDiagnostics(id);
|
|
269
269
|
return structuredClone(this.sessions.get(id).state.model);
|
|
270
270
|
} catch (error) {
|
|
271
271
|
this.sessions.restore(id, snapshot);
|
|
@@ -296,8 +296,11 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
296
296
|
|
|
297
297
|
/** @inheritdoc */
|
|
298
298
|
public recalculateModel(sessionId?: string): RecalculateModelResult {
|
|
299
|
-
const
|
|
300
|
-
|
|
299
|
+
const id = this.resolveSessionId(sessionId);
|
|
300
|
+
const envelope = this.sessions.get(id);
|
|
301
|
+
const result = this.evaluation.recalculateModel(envelope.state);
|
|
302
|
+
this.refreshModelDiagnostics(id);
|
|
303
|
+
return result;
|
|
301
304
|
}
|
|
302
305
|
|
|
303
306
|
private addOrUpdateConstituenta(
|
|
@@ -308,7 +311,6 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
308
311
|
const envelope = this.sessions.get(id);
|
|
309
312
|
const { result, diagnostics } = this.adapter.analyzeAgainstSession(envelope.state, input.draft);
|
|
310
313
|
const state = this.adapter.mergeStateWithDraft(envelope.state, input.draft, result);
|
|
311
|
-
this.sessions.replaceDiagnosticsForConstituent(id, input.draft.id, diagnostics);
|
|
312
314
|
return { state, diagnostics };
|
|
313
315
|
}
|
|
314
316
|
|
|
@@ -342,6 +344,7 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
342
344
|
const envelope = this.sessions.get(id);
|
|
343
345
|
reorderSessionItemsByDrafts(envelope.state.items, input.drafts, preBatchItemIds);
|
|
344
346
|
this.sessions.replaceState(id, envelope.state);
|
|
347
|
+
this.refreshDiagnostics(id);
|
|
345
348
|
|
|
346
349
|
return {
|
|
347
350
|
success: failed.length === 0,
|
|
@@ -418,11 +421,13 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
418
421
|
}
|
|
419
422
|
const data = parsed as {
|
|
420
423
|
state: SessionState;
|
|
421
|
-
diagnostics?:
|
|
424
|
+
diagnostics?: Diagnostic[];
|
|
422
425
|
};
|
|
423
426
|
const handle = this.sessions.create(normalizeImportedState(data.state), this.contractVersion);
|
|
424
427
|
if (data.diagnostics?.length) {
|
|
425
428
|
this.sessions.setDiagnostics(handle.sessionId, data.diagnostics);
|
|
429
|
+
} else {
|
|
430
|
+
this.refreshDiagnostics(handle.sessionId);
|
|
426
431
|
}
|
|
427
432
|
return this.trackSession(handle);
|
|
428
433
|
}
|
|
@@ -509,4 +514,17 @@ export class RSToolAgent implements RSToolAgentContract {
|
|
|
509
514
|
this.sessions.saveCurrentSessionId(handle.sessionId);
|
|
510
515
|
return handle;
|
|
511
516
|
}
|
|
517
|
+
|
|
518
|
+
private refreshDiagnostics(sessionId: string): void {
|
|
519
|
+
const envelope = this.sessions.get(sessionId);
|
|
520
|
+
this.sessions.setDiagnostics(sessionId, collectSchemaDiagnostics(envelope.state));
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
private refreshModelDiagnostics(sessionId: string): void {
|
|
524
|
+
const envelope = this.sessions.get(sessionId);
|
|
525
|
+
this.sessions.setDiagnostics(sessionId, [
|
|
526
|
+
...collectSchemaDiagnostics(envelope.state),
|
|
527
|
+
...collectModelDiagnostics(envelope.state)
|
|
528
|
+
]);
|
|
529
|
+
}
|
|
512
530
|
}
|
|
@@ -5,14 +5,14 @@ import {
|
|
|
5
5
|
type SessionStateResult
|
|
6
6
|
} from './agent-workflow';
|
|
7
7
|
import { type AnalysisResult, type AnalyzeExpressionInput } from './analysis';
|
|
8
|
-
import { type
|
|
8
|
+
import { type Diagnostic, type ListDiagnosticsFilters } from './diagnostic';
|
|
9
9
|
import { type EvaluateInput, type EvaluationResult } from './evaluation';
|
|
10
10
|
import { type ExportPortalInput, type ExportPortalResult, type ImportDataKind } from './import-export';
|
|
11
11
|
import { type RecalculateModelResult, type SessionModelState, type SetModelValuesInput } from './model-value';
|
|
12
12
|
import { type SessionHandle, type SessionRevision, type SessionState } from './session';
|
|
13
13
|
|
|
14
14
|
/** Agent-visible contract version; bump on breaking API changes. */
|
|
15
|
-
export const CONTRACT_VERSION = '
|
|
15
|
+
export const CONTRACT_VERSION = '3.0.0';
|
|
16
16
|
|
|
17
17
|
/** Options for constructing an {@link RSToolAgent}. */
|
|
18
18
|
export interface RSToolAgentOptions {
|
|
@@ -48,8 +48,8 @@ export interface RSToolAgentContract {
|
|
|
48
48
|
/** Return session summary (default) or full cloned state when `detail` is `'full'`. */
|
|
49
49
|
getSessionState(detail?: SessionStateDetail, sessionId?: string): SessionStateResult;
|
|
50
50
|
|
|
51
|
-
/** List diagnostics for the session, optionally filtered by constituent. */
|
|
52
|
-
listDiagnostics(filters?: ListDiagnosticsFilters, sessionId?: string):
|
|
51
|
+
/** List diagnostics for the session, optionally filtered by constituent or kind. */
|
|
52
|
+
listDiagnostics(filters?: ListDiagnosticsFilters, sessionId?: string): Diagnostic[];
|
|
53
53
|
|
|
54
54
|
/** Parse and type-check an RSLang expression against the current schema context. */
|
|
55
55
|
analyzeExpression(input: AnalyzeExpressionInput, sessionId?: string): AnalysisResult;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
import { type
|
|
4
|
+
import { type Diagnostic, type SessionState } from '../models';
|
|
5
5
|
|
|
6
6
|
export interface PersistedSessionEnvelope {
|
|
7
7
|
state: SessionState;
|
|
8
|
-
diagnostics:
|
|
8
|
+
diagnostics: Diagnostic[];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const CURRENT_SESSION_FILE = '_current.json';
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
CONTRACT_VERSION,
|
|
5
|
-
type
|
|
5
|
+
type Diagnostic,
|
|
6
6
|
type ListDiagnosticsFilters,
|
|
7
7
|
type SessionHandle,
|
|
8
8
|
type SessionRevision,
|
|
@@ -16,7 +16,7 @@ export interface SessionStoreOptions {
|
|
|
16
16
|
|
|
17
17
|
interface SessionEnvelope {
|
|
18
18
|
state: SessionState;
|
|
19
|
-
diagnostics:
|
|
19
|
+
diagnostics: Diagnostic[];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export class SessionStore {
|
|
@@ -97,7 +97,7 @@ export class SessionStore {
|
|
|
97
97
|
public replaceDiagnosticsForConstituent(
|
|
98
98
|
sessionId: string,
|
|
99
99
|
constituentId: number | undefined,
|
|
100
|
-
records:
|
|
100
|
+
records: Diagnostic[]
|
|
101
101
|
): void {
|
|
102
102
|
const found = this.get(sessionId);
|
|
103
103
|
found.diagnostics = found.diagnostics.filter(record => record.constituentId !== constituentId);
|
|
@@ -106,19 +106,26 @@ export class SessionStore {
|
|
|
106
106
|
this.persist(sessionId, found);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
public setDiagnostics(sessionId: string, records:
|
|
109
|
+
public setDiagnostics(sessionId: string, records: Diagnostic[]): void {
|
|
110
110
|
const found = this.get(sessionId);
|
|
111
111
|
found.diagnostics = [...records];
|
|
112
112
|
found.state.updatedAt = new Date().toISOString();
|
|
113
113
|
this.persist(sessionId, found);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
public listDiagnostics(sessionId: string, filters?: ListDiagnosticsFilters):
|
|
116
|
+
public listDiagnostics(sessionId: string, filters?: ListDiagnosticsFilters): Diagnostic[] {
|
|
117
117
|
const found = this.get(sessionId);
|
|
118
|
+
let records = found.diagnostics;
|
|
119
|
+
if (filters?.kind !== undefined) {
|
|
120
|
+
records = records.filter(record => record.kind === filters.kind);
|
|
121
|
+
}
|
|
122
|
+
if (filters?.severity !== undefined) {
|
|
123
|
+
records = records.filter(record => record.severity === filters.severity);
|
|
124
|
+
}
|
|
118
125
|
if (filters?.constituentId === undefined) {
|
|
119
|
-
return [...
|
|
126
|
+
return [...records];
|
|
120
127
|
}
|
|
121
|
-
return
|
|
128
|
+
return records.filter(record => record.constituentId === filters.constituentId);
|
|
122
129
|
}
|
|
123
130
|
|
|
124
131
|
public snapshot(sessionId: string): SessionEnvelope {
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { CstType as CstType$1 } from "@rsconcept/domain/library/rsform";
|
|
2
|
-
import { BasicBinding, EvalStatus as EvalStatus$1 } from "@rsconcept/domain/library/rsmodel";
|
|
3
|
-
import { ValueClass as ValueClass$1 } from "@rsconcept/domain/rslang";
|
|
4
|
-
import { RSErrorCode } from "@rsconcept/domain/rslang/error";
|
|
5
|
-
|
|
6
|
-
//#region src/models/common.d.ts
|
|
7
|
-
/** Runtime evaluation value: number, nested array (set/tuple), or boolean 0/1. */
|
|
8
|
-
type RSToolValue = number | RSToolValue[];
|
|
9
|
-
/** Language or validation error with source span and optional format parameters. */
|
|
10
|
-
interface RSToolErrorDescription {
|
|
11
|
-
/** Numeric error code (see {@link RSErrorCode}). */
|
|
12
|
-
code: number;
|
|
13
|
-
/** Start offset in the analyzed expression. */
|
|
14
|
-
from: number;
|
|
15
|
-
/** End offset in the analyzed expression. */
|
|
16
|
-
to: number;
|
|
17
|
-
/** Interpolation values for localized error messages. */
|
|
18
|
-
params?: readonly string[];
|
|
19
|
-
}
|
|
20
|
-
//#endregion
|
|
21
|
-
export { RSToolErrorDescription as a, RSErrorCode as i, CstType$1 as n, RSToolValue as o, EvalStatus$1 as r, ValueClass$1 as s, BasicBinding as t };
|
|
22
|
-
//# sourceMappingURL=common-DHJalS-Q.d.ts.map
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { a as RSToolErrorDescription } from "./common-DHJalS-Q.js";
|
|
2
|
-
|
|
3
|
-
//#region src/models/diagnostic.d.ts
|
|
4
|
-
/** Persisted diagnostic for a failed or warned expression in a session. */
|
|
5
|
-
interface DiagnosticRecord {
|
|
6
|
-
sessionId: string;
|
|
7
|
-
/** Constituent id when the diagnostic is tied to a stored item; omitted for scratch analysis. */
|
|
8
|
-
constituentId?: number;
|
|
9
|
-
/** Expression text that was analyzed. */
|
|
10
|
-
expression: string;
|
|
11
|
-
error: RSToolErrorDescription;
|
|
12
|
-
}
|
|
13
|
-
/** Filters for {@link RSToolAgent.listDiagnostics}. */
|
|
14
|
-
interface ListDiagnosticsFilters {
|
|
15
|
-
constituentId?: number;
|
|
16
|
-
}
|
|
17
|
-
//#endregion
|
|
18
|
-
export { ListDiagnosticsFilters as n, DiagnosticRecord as t };
|
|
19
|
-
//# sourceMappingURL=diagnostic-D9yl_mEL.d.ts.map
|