@lssm/example.locale-jurisdiction-gate 0.0.0-canary-20251213172311 → 0.0.0-canary-20251215231151
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/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +3 -3
- package/README.md +1 -0
- package/package.json +2 -2
- package/src/contracts/assistant.ts +6 -4
- package/src/contracts/index.ts +0 -2
- package/src/docs/index.ts +0 -2
- package/src/docs/locale-jurisdiction-gate.docblock.ts +0 -2
- package/src/entities/index.ts +0 -2
- package/src/entities/models.ts +15 -6
- package/src/events.ts +0 -2
- package/src/example.ts +0 -2
- package/src/feature.ts +0 -2
- package/src/handlers/demo.handlers.ts +19 -14
- package/src/handlers/index.ts +0 -2
- package/src/index.ts +0 -2
- package/src/policy/guard.test.ts +0 -2
- package/src/policy/guard.ts +16 -10
- package/src/policy/index.ts +0 -2
- package/src/policy/types.ts +4 -3
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/tsdown.config.js +1 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
$ bun build:bundle && bun build:types
|
|
2
2
|
$ tsdown
|
|
3
|
-
[34mℹ[39m tsdown [2mv0.17.
|
|
3
|
+
[34mℹ[39m tsdown [2mv0.17.4[22m powered by rolldown [2mv1.0.0-beta.53[22m
|
|
4
4
|
[34mℹ[39m config file: [4m/home/runner/work/contractspec/contractspec/packages/examples/locale-jurisdiction-gate/tsdown.config.js[24m
|
|
5
|
-
[34mℹ[39m entry: [34msrc/events.ts, src/example.ts, src/feature.ts, src/index.ts, src/
|
|
5
|
+
[34mℹ[39m entry: [34msrc/events.ts, src/example.ts, src/feature.ts, src/index.ts, src/contracts/assistant.ts, src/contracts/index.ts, src/docs/index.ts, src/docs/locale-jurisdiction-gate.docblock.ts, src/entities/index.ts, src/entities/models.ts, src/handlers/demo.handlers.ts, src/handlers/index.ts, src/policy/guard.ts, src/policy/index.ts, src/policy/types.ts[39m
|
|
6
6
|
[34mℹ[39m target: [34mesnext[39m
|
|
7
7
|
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
8
8
|
[34mℹ[39m Build start
|
|
@@ -22,5 +22,5 @@ $ tsdown
|
|
|
22
22
|
[34mℹ[39m [2mdist/[22m[1mdocs/index.js[22m [2m0.05 kB[22m [2m│ gzip: 0.07 kB[22m
|
|
23
23
|
[34mℹ[39m [2mdist/[22m[1mpolicy/types.js[22m [2m0.00 kB[22m [2m│ gzip: 0.02 kB[22m
|
|
24
24
|
[34mℹ[39m 15 files, total: 14.27 kB
|
|
25
|
-
[32m✔[39m Build complete in [
|
|
25
|
+
[32m✔[39m Build complete in [32m42ms[39m
|
|
26
26
|
$ tsc --noEmit
|
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @lssm/example.locale-jurisdiction-gate
|
|
2
2
|
|
|
3
|
-
## 0.0.0-canary-
|
|
3
|
+
## 0.0.0-canary-20251215231151
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
7
|
- Updated dependencies [3086383]
|
|
8
|
-
- @lssm/lib.contracts@0.0.0-canary-
|
|
9
|
-
- @lssm/lib.schema@0.0.0-canary-
|
|
8
|
+
- @lssm/lib.contracts@0.0.0-canary-20251215231151
|
|
9
|
+
- @lssm/lib.schema@0.0.0-canary-20251215231151
|
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lssm/example.locale-jurisdiction-gate",
|
|
3
|
-
"version": "0.0.0-canary-
|
|
3
|
+
"version": "0.0.0-canary-20251215231151",
|
|
4
4
|
"description": "Example: enforce locale + jurisdiction + kbSnapshotId + allowed scope for assistant calls (fail-closed).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@lssm/tool.tsdown": "workspace:*",
|
|
45
45
|
"@lssm/tool.typescript": "workspace:*",
|
|
46
|
-
"tsdown": "^0.17.
|
|
46
|
+
"tsdown": "^0.17.4",
|
|
47
47
|
"typescript": "^5.9.3"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { defineCommand } from '@lssm/lib.contracts';
|
|
2
2
|
import { ScalarTypeEnum, defineSchemaModel } from '@lssm/lib.schema';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AssistantAnswerIRModel,
|
|
6
|
+
LLMCallEnvelopeModel,
|
|
7
|
+
} from '../entities/models';
|
|
5
8
|
|
|
6
9
|
const AssistantQuestionInput = defineSchemaModel({
|
|
7
10
|
name: 'AssistantQuestionInput',
|
|
@@ -63,7 +66,8 @@ export const AssistantAnswerContract = defineCommand({
|
|
|
63
66
|
when: 'answer has no citations',
|
|
64
67
|
},
|
|
65
68
|
SCOPE_VIOLATION: {
|
|
66
|
-
description:
|
|
69
|
+
description:
|
|
70
|
+
'Answer violates allowed scope and must be refused/escalated',
|
|
67
71
|
http: 403,
|
|
68
72
|
gqlCode: 'SCOPE_VIOLATION',
|
|
69
73
|
when: 'output includes forbidden content under the given allowedScope',
|
|
@@ -92,5 +96,3 @@ export const AssistantExplainConceptContract = defineCommand({
|
|
|
92
96
|
},
|
|
93
97
|
policy: { auth: 'user' },
|
|
94
98
|
});
|
|
95
|
-
|
|
96
|
-
|
package/src/contracts/index.ts
CHANGED
package/src/docs/index.ts
CHANGED
package/src/entities/index.ts
CHANGED
package/src/entities/models.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ScalarTypeEnum,
|
|
3
|
+
defineEnum,
|
|
4
|
+
defineSchemaModel,
|
|
5
|
+
} from '@lssm/lib.schema';
|
|
2
6
|
|
|
3
7
|
export const AllowedScopeEnum = defineEnum('AllowedScope', [
|
|
4
8
|
'education_only',
|
|
@@ -10,8 +14,14 @@ export const UserProfileModel = defineSchemaModel({
|
|
|
10
14
|
name: 'UserProfile',
|
|
11
15
|
description: 'User profile inputs used to derive regulatory context.',
|
|
12
16
|
fields: {
|
|
13
|
-
preferredLocale: {
|
|
14
|
-
|
|
17
|
+
preferredLocale: {
|
|
18
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
19
|
+
isOptional: true,
|
|
20
|
+
},
|
|
21
|
+
residencyCountry: {
|
|
22
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
23
|
+
isOptional: true,
|
|
24
|
+
},
|
|
15
25
|
taxResidenceCountry: {
|
|
16
26
|
type: ScalarTypeEnum.String_unsecure(),
|
|
17
27
|
isOptional: true,
|
|
@@ -46,7 +56,8 @@ export const LLMCallEnvelopeModel = defineSchemaModel({
|
|
|
46
56
|
|
|
47
57
|
export const AssistantCitationModel = defineSchemaModel({
|
|
48
58
|
name: 'AssistantCitation',
|
|
49
|
-
description:
|
|
59
|
+
description:
|
|
60
|
+
'Citation referencing a KB snapshot + a specific item within it.',
|
|
50
61
|
fields: {
|
|
51
62
|
kbSnapshotId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
52
63
|
sourceType: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
@@ -97,5 +108,3 @@ export const AssistantAnswerIRModel = defineSchemaModel({
|
|
|
97
108
|
refusalReason: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
98
109
|
},
|
|
99
110
|
});
|
|
100
|
-
|
|
101
|
-
|
package/src/events.ts
CHANGED
package/src/example.ts
CHANGED
package/src/feature.ts
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
enforceAllowedScope,
|
|
3
|
+
enforceCitations,
|
|
4
|
+
validateEnvelope,
|
|
5
|
+
} from '../policy/guard';
|
|
2
6
|
|
|
3
7
|
type AllowedScope = 'education_only' | 'generic_info' | 'escalation_required';
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
interface AssistantAnswerIR {
|
|
6
10
|
locale: string;
|
|
7
11
|
jurisdiction: string;
|
|
8
12
|
allowedScope: AllowedScope;
|
|
9
|
-
sections:
|
|
10
|
-
citations:
|
|
13
|
+
sections: { heading: string; body: string }[];
|
|
14
|
+
citations: {
|
|
11
15
|
kbSnapshotId: string;
|
|
12
16
|
sourceType: string;
|
|
13
17
|
sourceId: string;
|
|
14
18
|
title?: string;
|
|
15
19
|
excerpt?: string;
|
|
16
|
-
}
|
|
20
|
+
}[];
|
|
17
21
|
disclaimers?: string[];
|
|
18
22
|
riskFlags?: string[];
|
|
19
23
|
refused?: boolean;
|
|
20
24
|
refusalReason?: string;
|
|
21
|
-
}
|
|
25
|
+
}
|
|
22
26
|
|
|
23
27
|
export interface DemoAssistantHandlers {
|
|
24
28
|
answer(input: {
|
|
@@ -64,7 +68,8 @@ export function createDemoAssistantHandlers(): DemoAssistantHandlers {
|
|
|
64
68
|
if (!env.ok) {
|
|
65
69
|
return {
|
|
66
70
|
locale: input.envelope.locale ?? 'en-US',
|
|
67
|
-
jurisdiction:
|
|
71
|
+
jurisdiction:
|
|
72
|
+
input.envelope.regulatoryContext?.jurisdiction ?? 'UNKNOWN',
|
|
68
73
|
allowedScope: input.envelope.allowedScope ?? 'education_only',
|
|
69
74
|
sections: [
|
|
70
75
|
{
|
|
@@ -73,7 +78,9 @@ export function createDemoAssistantHandlers(): DemoAssistantHandlers {
|
|
|
73
78
|
},
|
|
74
79
|
],
|
|
75
80
|
citations: [],
|
|
76
|
-
disclaimers: [
|
|
81
|
+
disclaimers: [
|
|
82
|
+
'This system refuses to answer without a valid envelope.',
|
|
83
|
+
],
|
|
77
84
|
riskFlags: [env.error.code],
|
|
78
85
|
refused: true,
|
|
79
86
|
refusalReason: env.error.code,
|
|
@@ -121,9 +128,7 @@ export function createDemoAssistantHandlers(): DemoAssistantHandlers {
|
|
|
121
128
|
if (!cited.ok) {
|
|
122
129
|
return {
|
|
123
130
|
...draft,
|
|
124
|
-
sections: [
|
|
125
|
-
{ heading: 'Request blocked', body: cited.error.message },
|
|
126
|
-
],
|
|
131
|
+
sections: [{ heading: 'Request blocked', body: cited.error.message }],
|
|
127
132
|
citations: [],
|
|
128
133
|
refused: true,
|
|
129
134
|
refusalReason: cited.error.code,
|
|
@@ -135,7 +140,9 @@ export function createDemoAssistantHandlers(): DemoAssistantHandlers {
|
|
|
135
140
|
}
|
|
136
141
|
|
|
137
142
|
async function explainConcept(input: {
|
|
138
|
-
envelope: DemoAssistantHandlers['explainConcept'] extends (
|
|
143
|
+
envelope: DemoAssistantHandlers['explainConcept'] extends (
|
|
144
|
+
a: infer A
|
|
145
|
+
) => any
|
|
139
146
|
? A extends { envelope: infer E }
|
|
140
147
|
? E
|
|
141
148
|
: never
|
|
@@ -150,5 +157,3 @@ export function createDemoAssistantHandlers(): DemoAssistantHandlers {
|
|
|
150
157
|
|
|
151
158
|
return { answer, explainConcept };
|
|
152
159
|
}
|
|
153
|
-
|
|
154
|
-
|
package/src/handlers/index.ts
CHANGED
package/src/index.ts
CHANGED
package/src/policy/guard.test.ts
CHANGED
package/src/policy/guard.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import type { GateError, GateResult } from './types';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
interface EnvelopeLike {
|
|
4
4
|
locale?: string;
|
|
5
5
|
kbSnapshotId?: string;
|
|
6
6
|
allowedScope?: 'education_only' | 'generic_info' | 'escalation_required';
|
|
7
7
|
regulatoryContext?: { jurisdiction?: string };
|
|
8
|
-
}
|
|
8
|
+
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface AnswerLike {
|
|
11
11
|
citations?: unknown[];
|
|
12
|
-
sections?:
|
|
12
|
+
sections?: { heading: string; body: string }[];
|
|
13
13
|
refused?: boolean;
|
|
14
14
|
refusalReason?: string;
|
|
15
15
|
allowedScope?: 'education_only' | 'generic_info' | 'escalation_required';
|
|
16
|
-
}
|
|
16
|
+
}
|
|
17
17
|
|
|
18
18
|
const SUPPORTED_LOCALES = new Set<string>(['en-US', 'en-GB', 'fr-FR']);
|
|
19
19
|
|
|
@@ -31,7 +31,10 @@ export function validateEnvelope(
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
if (!envelope.regulatoryContext?.jurisdiction) {
|
|
34
|
-
return {
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
error: err('JURISDICTION_REQUIRED', 'jurisdiction is required'),
|
|
37
|
+
};
|
|
35
38
|
}
|
|
36
39
|
if (!envelope.kbSnapshotId) {
|
|
37
40
|
return {
|
|
@@ -53,7 +56,10 @@ export function enforceCitations(answer: AnswerLike): GateResult<AnswerLike> {
|
|
|
53
56
|
if (!Array.isArray(citations) || citations.length === 0) {
|
|
54
57
|
return {
|
|
55
58
|
ok: false,
|
|
56
|
-
error: err(
|
|
59
|
+
error: err(
|
|
60
|
+
'CITATIONS_REQUIRED',
|
|
61
|
+
'answers must include at least one citation'
|
|
62
|
+
),
|
|
57
63
|
};
|
|
58
64
|
}
|
|
59
65
|
return { ok: true, value: answer };
|
|
@@ -80,7 +86,9 @@ export function enforceAllowedScope(
|
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
const bodies = (answer.sections ?? []).map((s) => s.body).join('\n');
|
|
83
|
-
const violations = EDUCATION_ONLY_FORBIDDEN_PATTERNS.some((re) =>
|
|
89
|
+
const violations = EDUCATION_ONLY_FORBIDDEN_PATTERNS.some((re) =>
|
|
90
|
+
re.test(bodies)
|
|
91
|
+
);
|
|
84
92
|
if (violations) {
|
|
85
93
|
return {
|
|
86
94
|
ok: false,
|
|
@@ -92,5 +100,3 @@ export function enforceAllowedScope(
|
|
|
92
100
|
}
|
|
93
101
|
return { ok: true, value: answer };
|
|
94
102
|
}
|
|
95
|
-
|
|
96
|
-
|
package/src/policy/index.ts
CHANGED
package/src/policy/types.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export type AllowedScope =
|
|
1
|
+
export type AllowedScope =
|
|
2
|
+
| 'education_only'
|
|
3
|
+
| 'generic_info'
|
|
4
|
+
| 'escalation_required';
|
|
2
5
|
|
|
3
6
|
export interface GateError {
|
|
4
7
|
code:
|
|
@@ -13,5 +16,3 @@ export interface GateError {
|
|
|
13
16
|
export type GateResult<T> =
|
|
14
17
|
| { ok: true; value: T }
|
|
15
18
|
| { ok: false; error: GateError };
|
|
16
|
-
|
|
17
|
-
|
package/tsconfig.json
CHANGED