@contractspec/example.policy-safe-knowledge-assistant 3.7.16 → 3.7.18
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 +45 -45
- package/CHANGELOG.md +40 -0
- package/dist/browser/docs/index.js +3 -27
- package/dist/browser/docs/policy-safe-knowledge-assistant.docblock.js +3 -27
- package/dist/browser/example.js +1 -37
- package/dist/browser/handlers/index.js +5 -334
- package/dist/browser/handlers/policy-safe-knowledge-assistant.handlers.js +5 -334
- package/dist/browser/index.js +7 -913
- package/dist/browser/orchestrator/buildAnswer.js +1 -73
- package/dist/browser/policy-safe-knowledge-assistant.feature.js +1 -64
- package/dist/browser/seed/fixtures.js +1 -35
- package/dist/browser/seed/index.js +1 -35
- package/dist/browser/seeders/index.js +2 -12
- package/dist/browser/ui/PolicySafeKnowledgeAssistantDashboard.js +1 -420
- package/dist/browser/ui/hooks/usePolicySafeKnowledgeAssistant.js +1 -154
- package/dist/browser/ui/index.js +1 -420
- package/dist/docs/index.js +3 -27
- package/dist/docs/policy-safe-knowledge-assistant.docblock.js +3 -27
- package/dist/example.js +1 -37
- package/dist/handlers/index.js +5 -334
- package/dist/handlers/policy-safe-knowledge-assistant.handlers.js +5 -334
- package/dist/index.js +7 -913
- package/dist/node/docs/index.js +3 -27
- package/dist/node/docs/policy-safe-knowledge-assistant.docblock.js +3 -27
- package/dist/node/example.js +1 -37
- package/dist/node/handlers/index.js +5 -334
- package/dist/node/handlers/policy-safe-knowledge-assistant.handlers.js +5 -334
- package/dist/node/index.js +7 -913
- package/dist/node/orchestrator/buildAnswer.js +1 -73
- package/dist/node/policy-safe-knowledge-assistant.feature.js +1 -64
- package/dist/node/seed/fixtures.js +1 -35
- package/dist/node/seed/index.js +1 -35
- package/dist/node/seeders/index.js +2 -12
- package/dist/node/ui/PolicySafeKnowledgeAssistantDashboard.js +1 -420
- package/dist/node/ui/hooks/usePolicySafeKnowledgeAssistant.js +1 -154
- package/dist/node/ui/index.js +1 -420
- package/dist/orchestrator/buildAnswer.js +1 -73
- package/dist/policy-safe-knowledge-assistant.feature.js +1 -64
- package/dist/seed/fixtures.js +1 -35
- package/dist/seed/index.js +1 -35
- package/dist/seeders/index.js +2 -12
- package/dist/ui/PolicySafeKnowledgeAssistantDashboard.js +1 -420
- package/dist/ui/hooks/usePolicySafeKnowledgeAssistant.js +1 -154
- package/dist/ui/index.js +1 -420
- package/package.json +15 -15
|
@@ -1,73 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
enforceAllowedScope,
|
|
4
|
-
enforceCitations,
|
|
5
|
-
validateEnvelope
|
|
6
|
-
} from "@contractspec/example.locale-jurisdiction-gate/policy/guard";
|
|
7
|
-
async function buildPolicySafeAnswer(input) {
|
|
8
|
-
const env = validateEnvelope(input.envelope);
|
|
9
|
-
if (!env.ok) {
|
|
10
|
-
return {
|
|
11
|
-
locale: input.envelope.locale ?? "en-GB",
|
|
12
|
-
jurisdiction: input.envelope.regulatoryContext?.jurisdiction ?? "UNKNOWN",
|
|
13
|
-
allowedScope: input.envelope.allowedScope ?? "education_only",
|
|
14
|
-
sections: [{ heading: "Request blocked", body: env.error.message }],
|
|
15
|
-
citations: [],
|
|
16
|
-
disclaimers: ["This system refuses to answer without a valid envelope."],
|
|
17
|
-
riskFlags: [env.error.code],
|
|
18
|
-
refused: true,
|
|
19
|
-
refusalReason: env.error.code
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
const results = await input.kbSearch({
|
|
23
|
-
snapshotId: env.value.kbSnapshotId,
|
|
24
|
-
jurisdiction: env.value.regulatoryContext?.jurisdiction ?? "UNKNOWN",
|
|
25
|
-
query: input.question
|
|
26
|
-
});
|
|
27
|
-
const citations = results.items.map((item) => ({
|
|
28
|
-
kbSnapshotId: env.value.kbSnapshotId,
|
|
29
|
-
sourceType: "ruleVersion",
|
|
30
|
-
sourceId: item.ruleVersionId,
|
|
31
|
-
title: "Curated rule version",
|
|
32
|
-
excerpt: item.excerpt
|
|
33
|
-
}));
|
|
34
|
-
const draft = {
|
|
35
|
-
locale: env.value.locale,
|
|
36
|
-
jurisdiction: env.value.regulatoryContext?.jurisdiction ?? "UNKNOWN",
|
|
37
|
-
allowedScope: env.value.allowedScope,
|
|
38
|
-
sections: [
|
|
39
|
-
{
|
|
40
|
-
heading: "Answer (KB-derived)",
|
|
41
|
-
body: results.items.length > 0 ? `This answer is derived from ${results.items.length} curated rule version(s) in the referenced snapshot.` : "No curated knowledge found in the referenced snapshot."
|
|
42
|
-
}
|
|
43
|
-
],
|
|
44
|
-
citations,
|
|
45
|
-
disclaimers: ["Educational demo only."],
|
|
46
|
-
riskFlags: []
|
|
47
|
-
};
|
|
48
|
-
const scope = enforceAllowedScope(env.value.allowedScope, draft);
|
|
49
|
-
if (!scope.ok) {
|
|
50
|
-
return {
|
|
51
|
-
...draft,
|
|
52
|
-
sections: [{ heading: "Escalation required", body: scope.error.message }],
|
|
53
|
-
refused: true,
|
|
54
|
-
refusalReason: scope.error.code,
|
|
55
|
-
riskFlags: [...draft.riskFlags ?? [], scope.error.code]
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
const cited = enforceCitations(draft);
|
|
59
|
-
if (!cited.ok) {
|
|
60
|
-
return {
|
|
61
|
-
...draft,
|
|
62
|
-
sections: [{ heading: "Request blocked", body: cited.error.message }],
|
|
63
|
-
citations: [],
|
|
64
|
-
refused: true,
|
|
65
|
-
refusalReason: cited.error.code,
|
|
66
|
-
riskFlags: [...draft.riskFlags ?? [], cited.error.code]
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
return draft;
|
|
70
|
-
}
|
|
71
|
-
export {
|
|
72
|
-
buildPolicySafeAnswer
|
|
73
|
-
};
|
|
1
|
+
import{enforceAllowedScope as G,enforceCitations as H,validateEnvelope as I}from"@contractspec/example.locale-jurisdiction-gate/policy/guard";async function K(k){let b=I(k.envelope);if(!b.ok)return{locale:k.envelope.locale??"en-GB",jurisdiction:k.envelope.regulatoryContext?.jurisdiction??"UNKNOWN",allowedScope:k.envelope.allowedScope??"education_only",sections:[{heading:"Request blocked",body:b.error.message}],citations:[],disclaimers:["This system refuses to answer without a valid envelope."],riskFlags:[b.error.code],refused:!0,refusalReason:b.error.code};let B=await k.kbSearch({snapshotId:b.value.kbSnapshotId,jurisdiction:b.value.regulatoryContext?.jurisdiction??"UNKNOWN",query:k.question}),F=B.items.map((D)=>({kbSnapshotId:b.value.kbSnapshotId,sourceType:"ruleVersion",sourceId:D.ruleVersionId,title:"Curated rule version",excerpt:D.excerpt})),h={locale:b.value.locale,jurisdiction:b.value.regulatoryContext?.jurisdiction??"UNKNOWN",allowedScope:b.value.allowedScope,sections:[{heading:"Answer (KB-derived)",body:B.items.length>0?`This answer is derived from ${B.items.length} curated rule version(s) in the referenced snapshot.`:"No curated knowledge found in the referenced snapshot."}],citations:F,disclaimers:["Educational demo only."],riskFlags:[]},q=G(b.value.allowedScope,h);if(!q.ok)return{...h,sections:[{heading:"Escalation required",body:q.error.message}],refused:!0,refusalReason:q.error.code,riskFlags:[...h.riskFlags??[],q.error.code]};let z=H(h);if(!z.ok)return{...h,sections:[{heading:"Request blocked",body:z.error.message}],citations:[],refused:!0,refusalReason:z.error.code,riskFlags:[...h.riskFlags??[],z.error.code]};return h}export{K as buildPolicySafeAnswer};
|
|
@@ -1,64 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { defineFeature } from "@contractspec/lib.contracts-spec";
|
|
3
|
-
var PolicySafeKnowledgeAssistantFeature = defineFeature({
|
|
4
|
-
meta: {
|
|
5
|
-
key: "policy-safe-knowledge-assistant",
|
|
6
|
-
version: "1.0.0",
|
|
7
|
-
title: "Policy-safe Knowledge Assistant",
|
|
8
|
-
description: "All-in-one example composing locale/jurisdiction gate + versioned KB + HITL pipeline + learning hub.",
|
|
9
|
-
domain: "knowledge",
|
|
10
|
-
owners: ["@examples"],
|
|
11
|
-
tags: ["assistant", "knowledge", "policy", "hitl", "learning"],
|
|
12
|
-
stability: "experimental"
|
|
13
|
-
},
|
|
14
|
-
operations: [
|
|
15
|
-
{ key: "assistant.answer", version: "1.0.0" },
|
|
16
|
-
{ key: "assistant.explainConcept", version: "1.0.0" },
|
|
17
|
-
{ key: "kb.ingestSource", version: "1.0.0" },
|
|
18
|
-
{ key: "kb.upsertRuleVersion", version: "1.0.0" },
|
|
19
|
-
{ key: "kb.approveRuleVersion", version: "1.0.0" },
|
|
20
|
-
{ key: "kb.publishSnapshot", version: "1.0.0" },
|
|
21
|
-
{ key: "kb.search", version: "1.0.0" },
|
|
22
|
-
{ key: "kbPipeline.runWatch", version: "1.0.0" },
|
|
23
|
-
{ key: "kbPipeline.createReviewTask", version: "1.0.0" },
|
|
24
|
-
{ key: "kbPipeline.submitDecision", version: "1.0.0" },
|
|
25
|
-
{ key: "kbPipeline.publishIfReady", version: "1.0.0" }
|
|
26
|
-
],
|
|
27
|
-
events: [
|
|
28
|
-
{ key: "assistant.answer.requested", version: "1.0.0" },
|
|
29
|
-
{ key: "assistant.answer.blocked", version: "1.0.0" },
|
|
30
|
-
{ key: "assistant.answer.delivered", version: "1.0.0" },
|
|
31
|
-
{ key: "kb.source.ingested", version: "1.0.0" },
|
|
32
|
-
{ key: "kb.ruleVersion.created", version: "1.0.0" },
|
|
33
|
-
{ key: "kb.ruleVersion.approved", version: "1.0.0" },
|
|
34
|
-
{ key: "kb.snapshot.published", version: "1.0.0" },
|
|
35
|
-
{ key: "kb.change.detected", version: "1.0.0" },
|
|
36
|
-
{ key: "kb.review.requested", version: "1.0.0" },
|
|
37
|
-
{ key: "kb.review.decided", version: "1.0.0" }
|
|
38
|
-
],
|
|
39
|
-
presentations: [],
|
|
40
|
-
opToPresentation: [],
|
|
41
|
-
presentationsTargets: [],
|
|
42
|
-
capabilities: {
|
|
43
|
-
requires: [
|
|
44
|
-
{ key: "identity", version: "1.0.0" },
|
|
45
|
-
{ key: "audit-trail", version: "1.0.0" },
|
|
46
|
-
{ key: "notifications", version: "1.0.0" },
|
|
47
|
-
{ key: "jobs", version: "1.0.0" },
|
|
48
|
-
{ key: "feature-flags", version: "1.0.0" },
|
|
49
|
-
{ key: "files", version: "1.0.0" },
|
|
50
|
-
{ key: "metering", version: "1.0.0" },
|
|
51
|
-
{ key: "learning-journey", version: "1.0.0" }
|
|
52
|
-
]
|
|
53
|
-
},
|
|
54
|
-
policies: [{ key: "policy-safe.policy.gate", version: "1.0.0" }],
|
|
55
|
-
knowledge: [{ key: "policy-safe.knowledge.rules", version: "1.0.0" }],
|
|
56
|
-
jobs: [{ key: "policy-safe.job.pipeline-watch", version: "1.0.0" }],
|
|
57
|
-
docs: [
|
|
58
|
-
"docs.examples.policy-safe-knowledge-assistant.goal",
|
|
59
|
-
"docs.examples.policy-safe-knowledge-assistant.usage"
|
|
60
|
-
]
|
|
61
|
-
});
|
|
62
|
-
export {
|
|
63
|
-
PolicySafeKnowledgeAssistantFeature
|
|
64
|
-
};
|
|
1
|
+
import{defineFeature as g}from"@contractspec/lib.contracts-spec";var j=g({meta:{key:"policy-safe-knowledge-assistant",version:"1.0.0",title:"Policy-safe Knowledge Assistant",description:"All-in-one example composing locale/jurisdiction gate + versioned KB + HITL pipeline + learning hub.",domain:"knowledge",owners:["@examples"],tags:["assistant","knowledge","policy","hitl","learning"],stability:"experimental"},operations:[{key:"assistant.answer",version:"1.0.0"},{key:"assistant.explainConcept",version:"1.0.0"},{key:"kb.ingestSource",version:"1.0.0"},{key:"kb.upsertRuleVersion",version:"1.0.0"},{key:"kb.approveRuleVersion",version:"1.0.0"},{key:"kb.publishSnapshot",version:"1.0.0"},{key:"kb.search",version:"1.0.0"},{key:"kbPipeline.runWatch",version:"1.0.0"},{key:"kbPipeline.createReviewTask",version:"1.0.0"},{key:"kbPipeline.submitDecision",version:"1.0.0"},{key:"kbPipeline.publishIfReady",version:"1.0.0"}],events:[{key:"assistant.answer.requested",version:"1.0.0"},{key:"assistant.answer.blocked",version:"1.0.0"},{key:"assistant.answer.delivered",version:"1.0.0"},{key:"kb.source.ingested",version:"1.0.0"},{key:"kb.ruleVersion.created",version:"1.0.0"},{key:"kb.ruleVersion.approved",version:"1.0.0"},{key:"kb.snapshot.published",version:"1.0.0"},{key:"kb.change.detected",version:"1.0.0"},{key:"kb.review.requested",version:"1.0.0"},{key:"kb.review.decided",version:"1.0.0"}],presentations:[],opToPresentation:[],presentationsTargets:[],capabilities:{requires:[{key:"identity",version:"1.0.0"},{key:"audit-trail",version:"1.0.0"},{key:"notifications",version:"1.0.0"},{key:"jobs",version:"1.0.0"},{key:"feature-flags",version:"1.0.0"},{key:"files",version:"1.0.0"},{key:"metering",version:"1.0.0"},{key:"learning-journey",version:"1.0.0"}]},policies:[{key:"policy-safe.policy.gate",version:"1.0.0"}],knowledge:[{key:"policy-safe.knowledge.rules",version:"1.0.0"}],jobs:[{key:"policy-safe.job.pipeline-watch",version:"1.0.0"}],docs:["docs.examples.policy-safe-knowledge-assistant.goal","docs.examples.policy-safe-knowledge-assistant.usage"]});export{j as PolicySafeKnowledgeAssistantFeature};
|
|
@@ -1,35 +1 @@
|
|
|
1
|
-
|
|
2
|
-
var DEMO_FIXTURES = {
|
|
3
|
-
jurisdictions: ["EU", "FR"],
|
|
4
|
-
locales: ["en-GB", "fr-FR"],
|
|
5
|
-
demoOrgId: "org_demo",
|
|
6
|
-
demoUserId: "user_demo",
|
|
7
|
-
sources: {
|
|
8
|
-
EU_SOURCE_1: {
|
|
9
|
-
jurisdiction: "EU",
|
|
10
|
-
authority: "DemoAuthority",
|
|
11
|
-
title: "EU Demo Source v1",
|
|
12
|
-
fetchedAt: new Date("2026-01-01T00:00:00.000Z"),
|
|
13
|
-
hash: "hash_eu_v1",
|
|
14
|
-
fileId: "file_eu_v1"
|
|
15
|
-
},
|
|
16
|
-
EU_SOURCE_2: {
|
|
17
|
-
jurisdiction: "EU",
|
|
18
|
-
authority: "DemoAuthority",
|
|
19
|
-
title: "EU Demo Source v2",
|
|
20
|
-
fetchedAt: new Date("2026-02-01T00:00:00.000Z"),
|
|
21
|
-
hash: "hash_eu_v2",
|
|
22
|
-
fileId: "file_eu_v2"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
rules: {
|
|
26
|
-
EU_RULE_TAX: {
|
|
27
|
-
id: "rule_eu_tax",
|
|
28
|
-
jurisdiction: "EU",
|
|
29
|
-
topicKey: "tax_reporting"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
export {
|
|
34
|
-
DEMO_FIXTURES
|
|
35
|
-
};
|
|
1
|
+
var e={jurisdictions:["EU","FR"],locales:["en-GB","fr-FR"],demoOrgId:"org_demo",demoUserId:"user_demo",sources:{EU_SOURCE_1:{jurisdiction:"EU",authority:"DemoAuthority",title:"EU Demo Source v1",fetchedAt:new Date("2026-01-01T00:00:00.000Z"),hash:"hash_eu_v1",fileId:"file_eu_v1"},EU_SOURCE_2:{jurisdiction:"EU",authority:"DemoAuthority",title:"EU Demo Source v2",fetchedAt:new Date("2026-02-01T00:00:00.000Z"),hash:"hash_eu_v2",fileId:"file_eu_v2"}},rules:{EU_RULE_TAX:{id:"rule_eu_tax",jurisdiction:"EU",topicKey:"tax_reporting"}}};export{e as DEMO_FIXTURES};
|
|
@@ -1,35 +1 @@
|
|
|
1
|
-
|
|
2
|
-
var DEMO_FIXTURES = {
|
|
3
|
-
jurisdictions: ["EU", "FR"],
|
|
4
|
-
locales: ["en-GB", "fr-FR"],
|
|
5
|
-
demoOrgId: "org_demo",
|
|
6
|
-
demoUserId: "user_demo",
|
|
7
|
-
sources: {
|
|
8
|
-
EU_SOURCE_1: {
|
|
9
|
-
jurisdiction: "EU",
|
|
10
|
-
authority: "DemoAuthority",
|
|
11
|
-
title: "EU Demo Source v1",
|
|
12
|
-
fetchedAt: new Date("2026-01-01T00:00:00.000Z"),
|
|
13
|
-
hash: "hash_eu_v1",
|
|
14
|
-
fileId: "file_eu_v1"
|
|
15
|
-
},
|
|
16
|
-
EU_SOURCE_2: {
|
|
17
|
-
jurisdiction: "EU",
|
|
18
|
-
authority: "DemoAuthority",
|
|
19
|
-
title: "EU Demo Source v2",
|
|
20
|
-
fetchedAt: new Date("2026-02-01T00:00:00.000Z"),
|
|
21
|
-
hash: "hash_eu_v2",
|
|
22
|
-
fileId: "file_eu_v2"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
rules: {
|
|
26
|
-
EU_RULE_TAX: {
|
|
27
|
-
id: "rule_eu_tax",
|
|
28
|
-
jurisdiction: "EU",
|
|
29
|
-
topicKey: "tax_reporting"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
export {
|
|
34
|
-
DEMO_FIXTURES
|
|
35
|
-
};
|
|
1
|
+
var e={jurisdictions:["EU","FR"],locales:["en-GB","fr-FR"],demoOrgId:"org_demo",demoUserId:"user_demo",sources:{EU_SOURCE_1:{jurisdiction:"EU",authority:"DemoAuthority",title:"EU Demo Source v1",fetchedAt:new Date("2026-01-01T00:00:00.000Z"),hash:"hash_eu_v1",fileId:"file_eu_v1"},EU_SOURCE_2:{jurisdiction:"EU",authority:"DemoAuthority",title:"EU Demo Source v2",fetchedAt:new Date("2026-02-01T00:00:00.000Z"),hash:"hash_eu_v2",fileId:"file_eu_v2"}},rules:{EU_RULE_TAX:{id:"rule_eu_tax",jurisdiction:"EU",topicKey:"tax_reporting"}}};export{e as DEMO_FIXTURES};
|
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { projectId, db } = params;
|
|
4
|
-
const existing = await db.query(`SELECT COUNT(*) as count FROM psa_user_context WHERE "projectId" = $1`, [projectId]);
|
|
5
|
-
if (existing.rows[0]?.count > 0)
|
|
6
|
-
return;
|
|
7
|
-
await db.execute(`INSERT INTO psa_user_context ("projectId", locale, jurisdiction, "allowedScope")
|
|
8
|
-
VALUES ($1, $2, $3, $4)`, [projectId, "en-GB", "EU", "education_only"]);
|
|
9
|
-
}
|
|
10
|
-
export {
|
|
11
|
-
seedPolicyKnowledgeAssistant
|
|
12
|
-
};
|
|
1
|
+
async function s(o){let{projectId:t,db:e}=o;if((await e.query('SELECT COUNT(*) as count FROM psa_user_context WHERE "projectId" = $1',[t])).rows[0]?.count>0)return;await e.execute(`INSERT INTO psa_user_context ("projectId", locale, jurisdiction, "allowedScope")
|
|
2
|
+
VALUES ($1, $2, $3, $4)`,[t,"en-GB","EU","education_only"])}export{s as seedPolicyKnowledgeAssistant};
|
|
@@ -1,420 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
-
"use client";
|
|
5
|
-
function isCitationLike(value) {
|
|
6
|
-
if (!value || typeof value !== "object")
|
|
7
|
-
return false;
|
|
8
|
-
const v = value;
|
|
9
|
-
return typeof v.kbSnapshotId === "string" && typeof v.sourceId === "string";
|
|
10
|
-
}
|
|
11
|
-
function toCitations(value) {
|
|
12
|
-
if (!Array.isArray(value))
|
|
13
|
-
return [];
|
|
14
|
-
return value.filter(isCitationLike).map((c) => ({
|
|
15
|
-
kbSnapshotId: c.kbSnapshotId,
|
|
16
|
-
sourceId: c.sourceId,
|
|
17
|
-
excerpt: c.excerpt
|
|
18
|
-
}));
|
|
19
|
-
}
|
|
20
|
-
function usePolicySafeKnowledgeAssistant() {
|
|
21
|
-
const { handlers, projectId } = useTemplateRuntime();
|
|
22
|
-
const api = handlers.policySafeKnowledgeAssistant;
|
|
23
|
-
const [state, setState] = useState({
|
|
24
|
-
context: null,
|
|
25
|
-
loading: true,
|
|
26
|
-
error: null,
|
|
27
|
-
lastAnswer: null,
|
|
28
|
-
lastRuleId: null,
|
|
29
|
-
lastRuleVersionId: null,
|
|
30
|
-
lastSnapshotId: null,
|
|
31
|
-
lastReviewTaskId: null
|
|
32
|
-
});
|
|
33
|
-
const refreshContext = useCallback(async () => {
|
|
34
|
-
try {
|
|
35
|
-
setState((s) => ({ ...s, loading: true, error: null }));
|
|
36
|
-
const ctx = await api.getUserContext({ projectId });
|
|
37
|
-
setState((s) => ({
|
|
38
|
-
...s,
|
|
39
|
-
context: {
|
|
40
|
-
locale: ctx.locale,
|
|
41
|
-
jurisdiction: ctx.jurisdiction,
|
|
42
|
-
allowedScope: ctx.allowedScope,
|
|
43
|
-
kbSnapshotId: ctx.kbSnapshotId
|
|
44
|
-
},
|
|
45
|
-
loading: false
|
|
46
|
-
}));
|
|
47
|
-
} catch (e) {
|
|
48
|
-
setState((s) => ({
|
|
49
|
-
...s,
|
|
50
|
-
loading: false,
|
|
51
|
-
error: e instanceof Error ? e : new Error("Unknown error")
|
|
52
|
-
}));
|
|
53
|
-
}
|
|
54
|
-
}, [api, projectId]);
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
refreshContext();
|
|
57
|
-
}, [refreshContext]);
|
|
58
|
-
const setContext = useCallback(async (input) => {
|
|
59
|
-
const ctx = await api.setUserContext({ projectId, ...input });
|
|
60
|
-
setState((s) => ({
|
|
61
|
-
...s,
|
|
62
|
-
context: {
|
|
63
|
-
locale: ctx.locale,
|
|
64
|
-
jurisdiction: ctx.jurisdiction,
|
|
65
|
-
allowedScope: ctx.allowedScope,
|
|
66
|
-
kbSnapshotId: ctx.kbSnapshotId
|
|
67
|
-
}
|
|
68
|
-
}));
|
|
69
|
-
}, [api, projectId]);
|
|
70
|
-
const askAssistant = useCallback(async (question) => {
|
|
71
|
-
const answerUnknown = await api.answer({ projectId, question });
|
|
72
|
-
const answer = answerUnknown;
|
|
73
|
-
setState((s) => ({
|
|
74
|
-
...s,
|
|
75
|
-
lastAnswer: {
|
|
76
|
-
refused: answer.refused,
|
|
77
|
-
refusalReason: answer.refusalReason,
|
|
78
|
-
sections: answer.sections,
|
|
79
|
-
citations: toCitations(answerUnknown.citations)
|
|
80
|
-
}
|
|
81
|
-
}));
|
|
82
|
-
}, [api, projectId]);
|
|
83
|
-
const createDemoRule = useCallback(async () => {
|
|
84
|
-
const rule = await api.createRule({
|
|
85
|
-
projectId,
|
|
86
|
-
jurisdiction: state.context?.jurisdiction ?? "EU",
|
|
87
|
-
topicKey: "tax_reporting"
|
|
88
|
-
});
|
|
89
|
-
setState((s) => ({ ...s, lastRuleId: rule.id }));
|
|
90
|
-
return rule.id;
|
|
91
|
-
}, [api, projectId, state.context?.jurisdiction]);
|
|
92
|
-
const upsertRuleVersion = useCallback(async (input) => {
|
|
93
|
-
const rv = await api.upsertRuleVersion({
|
|
94
|
-
projectId,
|
|
95
|
-
ruleId: input.ruleId,
|
|
96
|
-
content: input.content,
|
|
97
|
-
sourceRefs: [{ sourceDocumentId: "src_demo", excerpt: "demo excerpt" }]
|
|
98
|
-
});
|
|
99
|
-
setState((s) => ({ ...s, lastRuleVersionId: rv.id }));
|
|
100
|
-
return rv.id;
|
|
101
|
-
}, [api, projectId]);
|
|
102
|
-
const approveRuleVersion = useCallback(async (ruleVersionId) => {
|
|
103
|
-
await api.approveRuleVersion({ ruleVersionId, approver: "demo_expert" });
|
|
104
|
-
}, [api]);
|
|
105
|
-
const publishSnapshot = useCallback(async () => {
|
|
106
|
-
const snap = await api.publishSnapshot({
|
|
107
|
-
projectId,
|
|
108
|
-
jurisdiction: state.context?.jurisdiction ?? "EU",
|
|
109
|
-
asOfDate: new Date("2026-02-01T00:00:00.000Z")
|
|
110
|
-
});
|
|
111
|
-
setState((s) => ({ ...s, lastSnapshotId: snap.id }));
|
|
112
|
-
await refreshContext();
|
|
113
|
-
return snap.id;
|
|
114
|
-
}, [api, projectId, refreshContext, state.context?.jurisdiction]);
|
|
115
|
-
const simulateHighRiskChangeAndApprove = useCallback(async (ruleVersionId) => {
|
|
116
|
-
const cand = await api.createChangeCandidate({
|
|
117
|
-
projectId,
|
|
118
|
-
jurisdiction: state.context?.jurisdiction ?? "EU",
|
|
119
|
-
diffSummary: "Simulated change (demo)",
|
|
120
|
-
riskLevel: "high",
|
|
121
|
-
proposedRuleVersionIds: [ruleVersionId]
|
|
122
|
-
});
|
|
123
|
-
const review = await api.createReviewTask({ changeCandidateId: cand.id });
|
|
124
|
-
setState((s) => ({ ...s, lastReviewTaskId: review.id }));
|
|
125
|
-
await api.submitDecision({
|
|
126
|
-
reviewTaskId: review.id,
|
|
127
|
-
decision: "approve",
|
|
128
|
-
decidedByRole: "expert",
|
|
129
|
-
decidedBy: "demo_expert"
|
|
130
|
-
});
|
|
131
|
-
await api.publishIfReady({
|
|
132
|
-
jurisdiction: state.context?.jurisdiction ?? "EU"
|
|
133
|
-
});
|
|
134
|
-
return review.id;
|
|
135
|
-
}, [api, projectId, state.context?.jurisdiction]);
|
|
136
|
-
const derived = useMemo(() => ({ projectId }), [projectId]);
|
|
137
|
-
return {
|
|
138
|
-
state,
|
|
139
|
-
derived,
|
|
140
|
-
actions: {
|
|
141
|
-
refreshContext,
|
|
142
|
-
setContext,
|
|
143
|
-
askAssistant,
|
|
144
|
-
createDemoRule,
|
|
145
|
-
upsertRuleVersion,
|
|
146
|
-
approveRuleVersion,
|
|
147
|
-
publishSnapshot,
|
|
148
|
-
simulateHighRiskChangeAndApprove
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// src/ui/PolicySafeKnowledgeAssistantDashboard.tsx
|
|
154
|
-
import {
|
|
155
|
-
Button,
|
|
156
|
-
ErrorState,
|
|
157
|
-
LoaderBlock,
|
|
158
|
-
StatCard,
|
|
159
|
-
StatCardGroup
|
|
160
|
-
} from "@contractspec/lib.design-system";
|
|
161
|
-
import { Card } from "@contractspec/lib.ui-kit-web/ui/card";
|
|
162
|
-
import { Input } from "@contractspec/lib.ui-kit-web/ui/input";
|
|
163
|
-
import {
|
|
164
|
-
Select,
|
|
165
|
-
SelectContent,
|
|
166
|
-
SelectItem,
|
|
167
|
-
SelectTrigger,
|
|
168
|
-
SelectValue
|
|
169
|
-
} from "@contractspec/lib.ui-kit-web/ui/select";
|
|
170
|
-
import { Textarea } from "@contractspec/lib.ui-kit-web/ui/textarea";
|
|
171
|
-
import { useCallback as useCallback2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
172
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
173
|
-
"use client";
|
|
174
|
-
function PolicySafeKnowledgeAssistantDashboard() {
|
|
175
|
-
const { state, actions } = usePolicySafeKnowledgeAssistant();
|
|
176
|
-
const [question, setQuestion] = useState2("reporting obligations");
|
|
177
|
-
const [content, setContent] = useState2("EU: Reporting obligations v2 (updated)");
|
|
178
|
-
const [locale, setLocale] = useState2("en-GB");
|
|
179
|
-
const [jurisdiction, setJurisdiction] = useState2("EU");
|
|
180
|
-
const [allowedScope, setAllowedScope] = useState2("education_only");
|
|
181
|
-
const snapshotId = state.context?.kbSnapshotId ?? state.lastSnapshotId ?? null;
|
|
182
|
-
const stats = useMemo2(() => {
|
|
183
|
-
return [
|
|
184
|
-
{ label: "Locale", value: state.context?.locale ?? "—" },
|
|
185
|
-
{ label: "Jurisdiction", value: state.context?.jurisdiction ?? "—" },
|
|
186
|
-
{ label: "Scope", value: state.context?.allowedScope ?? "—" },
|
|
187
|
-
{ label: "KB Snapshot", value: snapshotId ?? "—" }
|
|
188
|
-
];
|
|
189
|
-
}, [
|
|
190
|
-
snapshotId,
|
|
191
|
-
state.context?.allowedScope,
|
|
192
|
-
state.context?.jurisdiction,
|
|
193
|
-
state.context?.locale
|
|
194
|
-
]);
|
|
195
|
-
const handleSetContext = useCallback2(async () => {
|
|
196
|
-
await actions.setContext({ locale, jurisdiction, allowedScope });
|
|
197
|
-
}, [actions, allowedScope, jurisdiction, locale]);
|
|
198
|
-
const handleAsk = useCallback2(async () => {
|
|
199
|
-
await actions.askAssistant(question);
|
|
200
|
-
}, [actions, question]);
|
|
201
|
-
const handleAdminPublishFlow = useCallback2(async () => {
|
|
202
|
-
const ruleId = state.lastRuleId ?? await actions.createDemoRule();
|
|
203
|
-
const rvId = await actions.upsertRuleVersion({ ruleId, content });
|
|
204
|
-
await actions.approveRuleVersion(rvId);
|
|
205
|
-
await actions.simulateHighRiskChangeAndApprove(rvId);
|
|
206
|
-
await actions.publishSnapshot();
|
|
207
|
-
}, [actions, content, state.lastRuleId]);
|
|
208
|
-
if (state.loading && !state.context) {
|
|
209
|
-
return /* @__PURE__ */ jsxDEV(LoaderBlock, {
|
|
210
|
-
label: "Loading demo..."
|
|
211
|
-
}, undefined, false, undefined, this);
|
|
212
|
-
}
|
|
213
|
-
if (state.error) {
|
|
214
|
-
return /* @__PURE__ */ jsxDEV(ErrorState, {
|
|
215
|
-
title: "Failed to load demo",
|
|
216
|
-
description: state.error.message,
|
|
217
|
-
onRetry: actions.refreshContext,
|
|
218
|
-
retryLabel: "Retry"
|
|
219
|
-
}, undefined, false, undefined, this);
|
|
220
|
-
}
|
|
221
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
222
|
-
className: "space-y-6",
|
|
223
|
-
children: [
|
|
224
|
-
/* @__PURE__ */ jsxDEV(StatCardGroup, {
|
|
225
|
-
children: stats.map((s) => /* @__PURE__ */ jsxDEV(StatCard, {
|
|
226
|
-
label: s.label,
|
|
227
|
-
value: String(s.value)
|
|
228
|
-
}, s.label, false, undefined, this))
|
|
229
|
-
}, undefined, false, undefined, this),
|
|
230
|
-
/* @__PURE__ */ jsxDEV(Card, {
|
|
231
|
-
className: "p-4",
|
|
232
|
-
children: [
|
|
233
|
-
/* @__PURE__ */ jsxDEV("h3", {
|
|
234
|
-
className: "font-semibold text-lg",
|
|
235
|
-
children: "1) Onboarding (explicit locale + jurisdiction)"
|
|
236
|
-
}, undefined, false, undefined, this),
|
|
237
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
238
|
-
className: "mt-3 grid gap-3 md:grid-cols-3",
|
|
239
|
-
children: [
|
|
240
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
241
|
-
children: [
|
|
242
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
243
|
-
className: "mb-1 font-semibold text-muted-foreground text-xs uppercase tracking-wide",
|
|
244
|
-
children: "Locale"
|
|
245
|
-
}, undefined, false, undefined, this),
|
|
246
|
-
/* @__PURE__ */ jsxDEV(Input, {
|
|
247
|
-
value: locale,
|
|
248
|
-
onChange: (e) => setLocale(e.target.value)
|
|
249
|
-
}, undefined, false, undefined, this)
|
|
250
|
-
]
|
|
251
|
-
}, undefined, true, undefined, this),
|
|
252
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
253
|
-
children: [
|
|
254
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
255
|
-
className: "mb-1 font-semibold text-muted-foreground text-xs uppercase tracking-wide",
|
|
256
|
-
children: "Jurisdiction"
|
|
257
|
-
}, undefined, false, undefined, this),
|
|
258
|
-
/* @__PURE__ */ jsxDEV(Input, {
|
|
259
|
-
value: jurisdiction,
|
|
260
|
-
onChange: (e) => setJurisdiction(e.target.value)
|
|
261
|
-
}, undefined, false, undefined, this)
|
|
262
|
-
]
|
|
263
|
-
}, undefined, true, undefined, this),
|
|
264
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
265
|
-
children: [
|
|
266
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
267
|
-
className: "mb-1 font-semibold text-muted-foreground text-xs uppercase tracking-wide",
|
|
268
|
-
children: "Allowed scope"
|
|
269
|
-
}, undefined, false, undefined, this),
|
|
270
|
-
/* @__PURE__ */ jsxDEV(Select, {
|
|
271
|
-
value: allowedScope,
|
|
272
|
-
onValueChange: (v) => setAllowedScope(v),
|
|
273
|
-
children: [
|
|
274
|
-
/* @__PURE__ */ jsxDEV(SelectTrigger, {
|
|
275
|
-
children: /* @__PURE__ */ jsxDEV(SelectValue, {
|
|
276
|
-
placeholder: "Select scope"
|
|
277
|
-
}, undefined, false, undefined, this)
|
|
278
|
-
}, undefined, false, undefined, this),
|
|
279
|
-
/* @__PURE__ */ jsxDEV(SelectContent, {
|
|
280
|
-
children: [
|
|
281
|
-
/* @__PURE__ */ jsxDEV(SelectItem, {
|
|
282
|
-
value: "education_only",
|
|
283
|
-
children: "education_only"
|
|
284
|
-
}, undefined, false, undefined, this),
|
|
285
|
-
/* @__PURE__ */ jsxDEV(SelectItem, {
|
|
286
|
-
value: "generic_info",
|
|
287
|
-
children: "generic_info"
|
|
288
|
-
}, undefined, false, undefined, this),
|
|
289
|
-
/* @__PURE__ */ jsxDEV(SelectItem, {
|
|
290
|
-
value: "escalation_required",
|
|
291
|
-
children: "escalation_required"
|
|
292
|
-
}, undefined, false, undefined, this)
|
|
293
|
-
]
|
|
294
|
-
}, undefined, true, undefined, this)
|
|
295
|
-
]
|
|
296
|
-
}, undefined, true, undefined, this)
|
|
297
|
-
]
|
|
298
|
-
}, undefined, true, undefined, this)
|
|
299
|
-
]
|
|
300
|
-
}, undefined, true, undefined, this),
|
|
301
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
302
|
-
className: "mt-4 flex gap-2",
|
|
303
|
-
children: [
|
|
304
|
-
/* @__PURE__ */ jsxDEV(Button, {
|
|
305
|
-
onPress: handleSetContext,
|
|
306
|
-
children: "Save context"
|
|
307
|
-
}, undefined, false, undefined, this),
|
|
308
|
-
/* @__PURE__ */ jsxDEV(Button, {
|
|
309
|
-
variant: "outline",
|
|
310
|
-
onPress: actions.refreshContext,
|
|
311
|
-
children: "Refresh"
|
|
312
|
-
}, undefined, false, undefined, this)
|
|
313
|
-
]
|
|
314
|
-
}, undefined, true, undefined, this)
|
|
315
|
-
]
|
|
316
|
-
}, undefined, true, undefined, this),
|
|
317
|
-
/* @__PURE__ */ jsxDEV(Card, {
|
|
318
|
-
className: "p-4",
|
|
319
|
-
children: [
|
|
320
|
-
/* @__PURE__ */ jsxDEV("h3", {
|
|
321
|
-
className: "font-semibold text-lg",
|
|
322
|
-
children: "2) Ask the assistant (must cite KB snapshot)"
|
|
323
|
-
}, undefined, false, undefined, this),
|
|
324
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
325
|
-
className: "mt-3 flex flex-col gap-3",
|
|
326
|
-
children: [
|
|
327
|
-
/* @__PURE__ */ jsxDEV(Input, {
|
|
328
|
-
value: question,
|
|
329
|
-
onChange: (e) => setQuestion(e.target.value)
|
|
330
|
-
}, undefined, false, undefined, this),
|
|
331
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
332
|
-
className: "flex gap-2",
|
|
333
|
-
children: /* @__PURE__ */ jsxDEV(Button, {
|
|
334
|
-
onPress: handleAsk,
|
|
335
|
-
children: "Ask"
|
|
336
|
-
}, undefined, false, undefined, this)
|
|
337
|
-
}, undefined, false, undefined, this)
|
|
338
|
-
]
|
|
339
|
-
}, undefined, true, undefined, this),
|
|
340
|
-
state.lastAnswer ? /* @__PURE__ */ jsxDEV("div", {
|
|
341
|
-
className: "mt-4 space-y-3",
|
|
342
|
-
children: [
|
|
343
|
-
state.lastAnswer.refused ? /* @__PURE__ */ jsxDEV("div", {
|
|
344
|
-
className: "text-red-600 text-sm",
|
|
345
|
-
children: [
|
|
346
|
-
"Refused: ",
|
|
347
|
-
state.lastAnswer.refusalReason ?? "UNKNOWN"
|
|
348
|
-
]
|
|
349
|
-
}, undefined, true, undefined, this) : null,
|
|
350
|
-
state.lastAnswer.sections.map((s, idx) => /* @__PURE__ */ jsxDEV("div", {
|
|
351
|
-
children: [
|
|
352
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
353
|
-
className: "font-semibold text-sm",
|
|
354
|
-
children: s.heading
|
|
355
|
-
}, undefined, false, undefined, this),
|
|
356
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
357
|
-
className: "text-muted-foreground text-sm",
|
|
358
|
-
children: s.body
|
|
359
|
-
}, undefined, false, undefined, this)
|
|
360
|
-
]
|
|
361
|
-
}, `${s.heading}-${idx}`, true, undefined, this)),
|
|
362
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
363
|
-
className: "font-semibold text-sm",
|
|
364
|
-
children: "Citations"
|
|
365
|
-
}, undefined, false, undefined, this),
|
|
366
|
-
/* @__PURE__ */ jsxDEV("ul", {
|
|
367
|
-
className: "list-disc pl-5 text-muted-foreground text-sm",
|
|
368
|
-
children: state.lastAnswer.citations.map((c) => /* @__PURE__ */ jsxDEV("li", {
|
|
369
|
-
children: [
|
|
370
|
-
c.kbSnapshotId,
|
|
371
|
-
" — ",
|
|
372
|
-
c.sourceId
|
|
373
|
-
]
|
|
374
|
-
}, `${c.kbSnapshotId}-${c.sourceId}`, true, undefined, this))
|
|
375
|
-
}, undefined, false, undefined, this)
|
|
376
|
-
]
|
|
377
|
-
}, undefined, true, undefined, this) : null
|
|
378
|
-
]
|
|
379
|
-
}, undefined, true, undefined, this),
|
|
380
|
-
/* @__PURE__ */ jsxDEV(Card, {
|
|
381
|
-
className: "p-4",
|
|
382
|
-
children: [
|
|
383
|
-
/* @__PURE__ */ jsxDEV("h3", {
|
|
384
|
-
className: "font-semibold text-lg",
|
|
385
|
-
children: "3) Admin: publish a new snapshot (HITL)"
|
|
386
|
-
}, undefined, false, undefined, this),
|
|
387
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
388
|
-
className: "mt-3 space-y-3",
|
|
389
|
-
children: [
|
|
390
|
-
/* @__PURE__ */ jsxDEV(Textarea, {
|
|
391
|
-
value: content,
|
|
392
|
-
onChange: (e) => setContent(e.target.value)
|
|
393
|
-
}, undefined, false, undefined, this),
|
|
394
|
-
/* @__PURE__ */ jsxDEV(Button, {
|
|
395
|
-
onPress: handleAdminPublishFlow,
|
|
396
|
-
children: "Simulate change → review → approve → publish snapshot"
|
|
397
|
-
}, undefined, false, undefined, this)
|
|
398
|
-
]
|
|
399
|
-
}, undefined, true, undefined, this)
|
|
400
|
-
]
|
|
401
|
-
}, undefined, true, undefined, this),
|
|
402
|
-
/* @__PURE__ */ jsxDEV(Card, {
|
|
403
|
-
className: "p-4",
|
|
404
|
-
children: [
|
|
405
|
-
/* @__PURE__ */ jsxDEV("h3", {
|
|
406
|
-
className: "font-semibold text-lg",
|
|
407
|
-
children: "4) Learning hub (patterns)"
|
|
408
|
-
}, undefined, false, undefined, this),
|
|
409
|
-
/* @__PURE__ */ jsxDEV("p", {
|
|
410
|
-
className: "mt-2 text-muted-foreground text-sm",
|
|
411
|
-
children: "This template includes drills, ambient coach, and quests as reusable Learning Journey tracks. The interactive learning UI is demonstrated in dedicated Learning Journey examples."
|
|
412
|
-
}, undefined, false, undefined, this)
|
|
413
|
-
]
|
|
414
|
-
}, undefined, true, undefined, this)
|
|
415
|
-
]
|
|
416
|
-
}, undefined, true, undefined, this);
|
|
417
|
-
}
|
|
418
|
-
export {
|
|
419
|
-
PolicySafeKnowledgeAssistantDashboard
|
|
420
|
-
};
|
|
1
|
+
import{useTemplateRuntime as f}from"@contractspec/lib.example-shared-ui";import{useCallback as F,useEffect as m,useMemo as A,useState as k}from"react";function v(O){if(!O||typeof O!=="object")return!1;let z=O;return typeof z.kbSnapshotId==="string"&&typeof z.sourceId==="string"}function h(O){if(!Array.isArray(O))return[];return O.filter(v).map((z)=>({kbSnapshotId:z.kbSnapshotId,sourceId:z.sourceId,excerpt:z.excerpt}))}function V(){let{handlers:O,projectId:z}=f(),Z=O.policySafeKnowledgeAssistant,[E,$]=k({context:null,loading:!0,error:null,lastAnswer:null,lastRuleId:null,lastRuleVersionId:null,lastSnapshotId:null,lastReviewTaskId:null}),G=F(async()=>{try{$((Y)=>({...Y,loading:!0,error:null}));let X=await Z.getUserContext({projectId:z});$((Y)=>({...Y,context:{locale:X.locale,jurisdiction:X.jurisdiction,allowedScope:X.allowedScope,kbSnapshotId:X.kbSnapshotId},loading:!1}))}catch(X){$((Y)=>({...Y,loading:!1,error:X instanceof Error?X:Error("Unknown error")}))}},[Z,z]);m(()=>{G()},[G]);let H=F(async(X)=>{let Y=await Z.setUserContext({projectId:z,...X});$((D)=>({...D,context:{locale:Y.locale,jurisdiction:Y.jurisdiction,allowedScope:Y.allowedScope,kbSnapshotId:Y.kbSnapshotId}}))},[Z,z]),U=F(async(X)=>{let Y=await Z.answer({projectId:z,question:X}),D=Y;$((W)=>({...W,lastAnswer:{refused:D.refused,refusalReason:D.refusalReason,sections:D.sections,citations:h(Y.citations)}}))},[Z,z]),J=F(async()=>{let X=await Z.createRule({projectId:z,jurisdiction:E.context?.jurisdiction??"EU",topicKey:"tax_reporting"});return $((Y)=>({...Y,lastRuleId:X.id})),X.id},[Z,z,E.context?.jurisdiction]),P=F(async(X)=>{let Y=await Z.upsertRuleVersion({projectId:z,ruleId:X.ruleId,content:X.content,sourceRefs:[{sourceDocumentId:"src_demo",excerpt:"demo excerpt"}]});return $((D)=>({...D,lastRuleVersionId:Y.id})),Y.id},[Z,z]),M=F(async(X)=>{await Z.approveRuleVersion({ruleVersionId:X,approver:"demo_expert"})},[Z]),R=F(async()=>{let X=await Z.publishSnapshot({projectId:z,jurisdiction:E.context?.jurisdiction??"EU",asOfDate:new Date("2026-02-01T00:00:00.000Z")});return $((Y)=>({...Y,lastSnapshotId:X.id})),await G(),X.id},[Z,z,G,E.context?.jurisdiction]),y=F(async(X)=>{let Y=await Z.createChangeCandidate({projectId:z,jurisdiction:E.context?.jurisdiction??"EU",diffSummary:"Simulated change (demo)",riskLevel:"high",proposedRuleVersionIds:[X]}),D=await Z.createReviewTask({changeCandidateId:Y.id});return $((W)=>({...W,lastReviewTaskId:D.id})),await Z.submitDecision({reviewTaskId:D.id,decision:"approve",decidedByRole:"expert",decidedBy:"demo_expert"}),await Z.publishIfReady({jurisdiction:E.context?.jurisdiction??"EU"}),D.id},[Z,z,E.context?.jurisdiction]),b=A(()=>({projectId:z}),[z]);return{state:E,derived:b,actions:{refreshContext:G,setContext:H,askAssistant:U,createDemoRule:J,upsertRuleVersion:P,approveRuleVersion:M,publishSnapshot:R,simulateHighRiskChangeAndApprove:y}}}import{Button as L,ErrorState as I,LoaderBlock as w,StatCard as C,StatCardGroup as S}from"@contractspec/lib.design-system";import{Card as T}from"@contractspec/lib.ui-kit-web/ui/card";import{Input as q}from"@contractspec/lib.ui-kit-web/ui/input";import{Select as d,SelectContent as p,SelectItem as B,SelectTrigger as u,SelectValue as l}from"@contractspec/lib.ui-kit-web/ui/select";import{Textarea as j}from"@contractspec/lib.ui-kit-web/ui/textarea";import{useCallback as g,useMemo as c,useState as Q}from"react";import{jsx as N,jsxs as _}from"react/jsx-runtime";function Nz(){let{state:O,actions:z}=V(),[Z,E]=Q("reporting obligations"),[$,G]=Q("EU: Reporting obligations v2 (updated)"),[H,U]=Q("en-GB"),[J,P]=Q("EU"),[M,R]=Q("education_only"),y=O.context?.kbSnapshotId??O.lastSnapshotId??null,b=c(()=>{return[{label:"Locale",value:O.context?.locale??"—"},{label:"Jurisdiction",value:O.context?.jurisdiction??"—"},{label:"Scope",value:O.context?.allowedScope??"—"},{label:"KB Snapshot",value:y??"—"}]},[y,O.context?.allowedScope,O.context?.jurisdiction,O.context?.locale]),X=g(async()=>{await z.setContext({locale:H,jurisdiction:J,allowedScope:M})},[z,M,J,H]),Y=g(async()=>{await z.askAssistant(Z)},[z,Z]),D=g(async()=>{let W=O.lastRuleId??await z.createDemoRule(),K=await z.upsertRuleVersion({ruleId:W,content:$});await z.approveRuleVersion(K),await z.simulateHighRiskChangeAndApprove(K),await z.publishSnapshot()},[z,$,O.lastRuleId]);if(O.loading&&!O.context)return N(w,{label:"Loading demo..."});if(O.error)return N(I,{title:"Failed to load demo",description:O.error.message,onRetry:z.refreshContext,retryLabel:"Retry"});return _("div",{className:"space-y-6",children:[N(S,{children:b.map((W)=>N(C,{label:W.label,value:String(W.value)},W.label))}),_(T,{className:"p-4",children:[N("h3",{className:"font-semibold text-lg",children:"1) Onboarding (explicit locale + jurisdiction)"}),_("div",{className:"mt-3 grid gap-3 md:grid-cols-3",children:[_("div",{children:[N("div",{className:"mb-1 font-semibold text-muted-foreground text-xs uppercase tracking-wide",children:"Locale"}),N(q,{value:H,onChange:(W)=>U(W.target.value)})]}),_("div",{children:[N("div",{className:"mb-1 font-semibold text-muted-foreground text-xs uppercase tracking-wide",children:"Jurisdiction"}),N(q,{value:J,onChange:(W)=>P(W.target.value)})]}),_("div",{children:[N("div",{className:"mb-1 font-semibold text-muted-foreground text-xs uppercase tracking-wide",children:"Allowed scope"}),_(d,{value:M,onValueChange:(W)=>R(W),children:[N(u,{children:N(l,{placeholder:"Select scope"})}),_(p,{children:[N(B,{value:"education_only",children:"education_only"}),N(B,{value:"generic_info",children:"generic_info"}),N(B,{value:"escalation_required",children:"escalation_required"})]})]})]})]}),_("div",{className:"mt-4 flex gap-2",children:[N(L,{onPress:X,children:"Save context"}),N(L,{variant:"outline",onPress:z.refreshContext,children:"Refresh"})]})]}),_(T,{className:"p-4",children:[N("h3",{className:"font-semibold text-lg",children:"2) Ask the assistant (must cite KB snapshot)"}),_("div",{className:"mt-3 flex flex-col gap-3",children:[N(q,{value:Z,onChange:(W)=>E(W.target.value)}),N("div",{className:"flex gap-2",children:N(L,{onPress:Y,children:"Ask"})})]}),O.lastAnswer?_("div",{className:"mt-4 space-y-3",children:[O.lastAnswer.refused?_("div",{className:"text-red-600 text-sm",children:["Refused: ",O.lastAnswer.refusalReason??"UNKNOWN"]}):null,O.lastAnswer.sections.map((W,K)=>_("div",{children:[N("div",{className:"font-semibold text-sm",children:W.heading}),N("div",{className:"text-muted-foreground text-sm",children:W.body})]},`${W.heading}-${K}`)),N("div",{className:"font-semibold text-sm",children:"Citations"}),N("ul",{className:"list-disc pl-5 text-muted-foreground text-sm",children:O.lastAnswer.citations.map((W)=>_("li",{children:[W.kbSnapshotId," — ",W.sourceId]},`${W.kbSnapshotId}-${W.sourceId}`))})]}):null]}),_(T,{className:"p-4",children:[N("h3",{className:"font-semibold text-lg",children:"3) Admin: publish a new snapshot (HITL)"}),_("div",{className:"mt-3 space-y-3",children:[N(j,{value:$,onChange:(W)=>G(W.target.value)}),N(L,{onPress:D,children:"Simulate change → review → approve → publish snapshot"})]})]}),_(T,{className:"p-4",children:[N("h3",{className:"font-semibold text-lg",children:"4) Learning hub (patterns)"}),N("p",{className:"mt-2 text-muted-foreground text-sm",children:"This template includes drills, ambient coach, and quests as reusable Learning Journey tracks. The interactive learning UI is demonstrated in dedicated Learning Journey examples."})]})]})}export{Nz as PolicySafeKnowledgeAssistantDashboard};
|