kibi-cli 2.0.0 → 2.1.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.
- package/dist/cli-register-proof.d.ts.map +1 -1
- package/dist/cli-register-proof.js +25 -1
- package/dist/cli-register-reporting.d.ts.map +1 -1
- package/dist/cli-register-reporting.js +6 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/commands/check-generated.d.ts +7 -0
- package/dist/commands/check-generated.d.ts.map +1 -0
- package/dist/commands/check-generated.js +192 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +3 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +122 -1
- package/dist/commands/engine.d.ts.map +1 -1
- package/dist/commands/engine.js +3 -1
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +13 -7
- package/dist/commands/proof-explain.d.ts +10 -0
- package/dist/commands/proof-explain.d.ts.map +1 -0
- package/dist/commands/proof-explain.js +45 -0
- package/dist/commands/proof-impact.d.ts +7 -0
- package/dist/commands/proof-impact.d.ts.map +1 -0
- package/dist/commands/proof-impact.js +39 -0
- package/dist/commands/proof-migrate-legacy.js +1 -1
- package/dist/commands/proof-prune.js +1 -1
- package/dist/commands/prove.js +4 -4
- package/dist/commands/report.d.ts.map +1 -1
- package/dist/commands/report.js +17 -2
- package/dist/commands/sync/manifest.d.ts +1 -0
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +18 -16
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +6 -0
- package/dist/engine.d.ts +3 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +134 -61
- package/dist/env/bootstrap.d.ts +84 -0
- package/dist/env/bootstrap.d.ts.map +1 -0
- package/dist/env/bootstrap.js +310 -0
- package/dist/extractors/manifest.d.ts.map +1 -1
- package/dist/extractors/manifest.js +1 -1
- package/dist/extractors/markdown.d.ts +2 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +19 -0
- package/dist/extractors/symbols-coordinator.d.ts +8 -0
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +40 -0
- package/dist/extractors/symbols-ts.d.ts +8 -19
- package/dist/extractors/symbols-ts.d.ts.map +1 -1
- package/dist/extractors/symbols-ts.js +33 -503
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +4 -5
- package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
- package/dist/operations/modeling/model-requirement.js +4 -0
- package/dist/operations/modeling/suggest-predicates.d.ts +1 -1
- package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
- package/dist/operations/modeling/suggest-predicates.js +30 -3
- package/dist/operations/mutation/symbol-granularity.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-granularity.js +9 -87
- package/dist/operations/planning/compile-intent.d.ts +49 -0
- package/dist/operations/planning/compile-intent.d.ts.map +1 -1
- package/dist/operations/planning/compile-intent.js +63 -3
- package/dist/operations/proof/baseline-diff.d.ts +41 -0
- package/dist/operations/proof/baseline-diff.d.ts.map +1 -0
- package/dist/operations/proof/baseline-diff.js +153 -0
- package/dist/operations/proof/explain.d.ts +75 -0
- package/dist/operations/proof/explain.d.ts.map +1 -0
- package/dist/operations/proof/explain.js +294 -0
- package/dist/operations/proof/impact.d.ts +45 -0
- package/dist/operations/proof/impact.d.ts.map +1 -0
- package/dist/operations/proof/impact.js +118 -0
- package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
- package/dist/operations/proof/ingest-proof.js +44 -3
- package/dist/operations/proof/migrate-legacy-receipts.d.ts.map +1 -1
- package/dist/operations/proof/migrate-legacy-receipts.js +21 -17
- package/dist/operations/proof/prune-receipts.js +1 -1
- package/dist/operations/proof/receipt-document.js +2 -2
- package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analysis-receipt.js +3 -9
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +18 -67
- package/dist/operations/semantic-advisor/plugin-orchestration.d.ts +63 -0
- package/dist/operations/semantic-advisor/plugin-orchestration.d.ts.map +1 -0
- package/dist/operations/semantic-advisor/plugin-orchestration.js +489 -0
- package/dist/operations/semantic-advisor/predicate-rules-core.d.ts +15 -2
- package/dist/operations/semantic-advisor/predicate-rules-core.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-core.js +7 -140
- package/dist/operations/semantic-advisor/predicate-rules-launcher.d.ts +11 -12
- package/dist/operations/semantic-advisor/predicate-rules-launcher.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-launcher.js +6 -94
- package/dist/operations/semantic-advisor/predicate-rules-policy.d.ts +16 -0
- package/dist/operations/semantic-advisor/predicate-rules-policy.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-policy.js +7 -150
- package/dist/operations/semantic-advisor/predicate-rules-product-tail.d.ts +9 -0
- package/dist/operations/semantic-advisor/predicate-rules-product-tail.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-product-tail.js +7 -102
- package/dist/operations/semantic-advisor/predicate-rules-product.d.ts +13 -0
- package/dist/operations/semantic-advisor/predicate-rules-product.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/predicate-rules-product.js +7 -133
- package/dist/operations/semantic-advisor/types.d.ts +1 -0
- package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
- package/dist/plugins/compose-ontology-packs.d.ts +51 -0
- package/dist/plugins/compose-ontology-packs.d.ts.map +1 -0
- package/dist/plugins/compose-ontology-packs.js +257 -0
- package/dist/plugins/compose-semantic-classifier.d.ts +42 -0
- package/dist/plugins/compose-semantic-classifier.d.ts.map +1 -0
- package/dist/plugins/compose-semantic-classifier.js +211 -0
- package/dist/plugins/external-allowlist.d.ts +10 -0
- package/dist/plugins/external-allowlist.d.ts.map +1 -0
- package/dist/plugins/external-allowlist.js +16 -0
- package/dist/plugins/index.d.ts +9 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +33 -0
- package/dist/plugins/load-plugin.d.ts +19 -0
- package/dist/plugins/load-plugin.d.ts.map +1 -0
- package/dist/plugins/load-plugin.js +59 -0
- package/dist/plugins/project-config.d.ts +28 -0
- package/dist/plugins/project-config.d.ts.map +1 -0
- package/dist/plugins/project-config.js +90 -0
- package/dist/plugins/registry.d.ts +93 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +338 -0
- package/dist/plugins/resolve-package.d.ts +47 -0
- package/dist/plugins/resolve-package.d.ts.map +1 -0
- package/dist/plugins/resolve-package.js +262 -0
- package/dist/plugins/source-analysis-service.d.ts +44 -0
- package/dist/plugins/source-analysis-service.d.ts.map +1 -0
- package/dist/plugins/source-analysis-service.js +232 -0
- package/dist/prolog/janitor.d.ts.map +1 -1
- package/dist/prolog/janitor.js +1 -1
- package/dist/prolog/store-lock.d.ts +1 -1
- package/dist/prolog/store-lock.d.ts.map +1 -1
- package/dist/proof/inspect.js +1 -1
- package/dist/proof/producers/playwright-case-id.js +2 -2
- package/dist/proof/producers/playwright-reporter.js +4 -4
- package/dist/public/extractors/symbols-coordinator.d.ts +1 -1
- package/dist/public/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/public/extractors/symbols-coordinator.js +1 -1
- package/dist/public/impact/analyzer.d.ts +1 -1
- package/dist/public/impact/analyzer.d.ts.map +1 -1
- package/dist/public/impact/analyzer.js +6 -4
- package/dist/public/operations/catalog.d.ts +4 -1
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +1 -1
- package/dist/public/operations/check-helpers.d.ts +1 -1
- package/dist/public/operations/check-helpers.d.ts.map +1 -1
- package/dist/public/operations/check-helpers.js +1 -1
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +35 -12
- package/dist/public/operations/discovery-entities.d.ts +12 -0
- package/dist/public/operations/discovery-entities.d.ts.map +1 -1
- package/dist/public/operations/discovery-entities.js +34 -0
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +23 -18
- package/dist/public/operations/prolog-json.d.ts +1 -1
- package/dist/public/operations/prolog-json.d.ts.map +1 -1
- package/dist/public/operations/prolog-json.js +7 -7
- package/dist/public/operations/runtime-types.d.ts +16 -1
- package/dist/public/operations/runtime-types.d.ts.map +1 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
- package/dist/public/operations/specs/check-rules.generated.js +2 -1
- package/dist/public/operations/specs/check.d.ts +1 -1
- package/dist/public/operations/specs/discovery.d.ts +3 -0
- package/dist/public/operations/specs/discovery.d.ts.map +1 -1
- package/dist/public/operations/specs/discovery.js +3 -0
- package/dist/public/operations/specs/proof.js +1 -1
- package/dist/public/operations/specs/reporting.d.ts +1 -0
- package/dist/public/operations/specs/reporting.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.js +2 -2
- package/dist/public/operations/specs/semantic.d.ts +1 -1
- package/dist/public/operations/specs/semantic.d.ts.map +1 -1
- package/dist/public/operations/specs/semantic.js +54 -4
- package/dist/public/operations/symbol-repair-plan.d.ts.map +1 -1
- package/dist/public/operations/symbol-repair-plan.js +3 -1
- package/dist/public/operations/types.d.ts +5 -0
- package/dist/public/operations/types.d.ts.map +1 -1
- package/dist/public/proof-protocol.js +4 -4
- package/dist/public/skills/kibi-usage/SKILL.md +4 -1
- package/dist/public/skills/kibi-usage/resources/proof.md +45 -0
- package/dist/public/symbol-granularity.d.ts +1 -1
- package/dist/public/symbol-granularity.d.ts.map +1 -1
- package/dist/public/symbol-granularity.generated.d.ts.map +1 -1
- package/dist/public/symbol-granularity.generated.js +38 -5
- package/dist/report/html-report.css +1239 -281
- package/dist/runtime/cli-runtime.d.ts.map +1 -1
- package/dist/runtime/cli-runtime.js +18 -0
- package/dist/traceability/symbol-extract.d.ts +24 -7
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +64 -208
- package/dist/utils/rule-registry.generated.d.ts +5 -0
- package/dist/utils/rule-registry.generated.d.ts.map +1 -1
- package/dist/utils/rule-registry.generated.js +6 -0
- package/package.json +14 -11
- package/src/public/extractors/symbols-coordinator.ts +1 -0
- package/src/public/impact/analyzer.ts +15 -12
- package/src/public/operations/check-executor.ts +1 -1
- package/src/public/operations/check-helpers.ts +2 -2
- package/src/public/operations/contracts.ts +40 -12
- package/src/public/operations/discovery-entities.ts +46 -0
- package/src/public/operations/discovery-executors.ts +39 -26
- package/src/public/operations/prolog-json.ts +13 -5
- package/src/public/operations/runtime-types.ts +21 -2
- package/src/public/operations/specs/check-rules.generated.ts +2 -1
- package/src/public/operations/specs/discovery.ts +3 -0
- package/src/public/operations/specs/proof.ts +1 -1
- package/src/public/operations/specs/reporting.ts +2 -2
- package/src/public/operations/specs/semantic.ts +79 -17
- package/src/public/operations/symbol-repair-plan.ts +3 -4
- package/src/public/operations/types.ts +5 -0
- package/src/public/proof-fingerprint.ts +1 -1
- package/src/public/proof-protocol-types.d.ts +1 -1
- package/src/public/proof-protocol.ts +4 -4
- package/src/public/skills/kibi-usage/SKILL.md +4 -1
- package/src/public/skills/kibi-usage/resources/proof.md +45 -0
- package/src/public/symbol-granularity.generated.ts +45 -12
- package/src/public/symbol-granularity.ts +3 -3
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { composeOntologyCatalog, composeOntologyMatches, } from "../../plugins/compose-ontology-packs.js";
|
|
19
|
+
import { composeSemanticClassification, } from "../../plugins/compose-semantic-classifier.js";
|
|
20
|
+
import { allowsExternalSemanticClassifier } from "../../plugins/external-allowlist.js";
|
|
21
|
+
import { analyzeSemanticAdvisorInput } from "./analyze-prose.js";
|
|
22
|
+
function hasConfiguredPlugins(registry) {
|
|
23
|
+
return registry.getProjectConfig().then((config) => {
|
|
24
|
+
return (config.plugins?.length ?? 0) > 0;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const ASSERTIVE_ROLES = new Set([
|
|
28
|
+
"normative",
|
|
29
|
+
"definition",
|
|
30
|
+
"condition",
|
|
31
|
+
"exception",
|
|
32
|
+
]);
|
|
33
|
+
function isAssertiveUnresolved(proposition) {
|
|
34
|
+
return (ASSERTIVE_ROLES.has(proposition.role) &&
|
|
35
|
+
["missing", "ambiguous", "ontology_gap"].includes(proposition.status));
|
|
36
|
+
}
|
|
37
|
+
function laneRank(lane) {
|
|
38
|
+
switch (lane) {
|
|
39
|
+
case "strict_property":
|
|
40
|
+
return 4;
|
|
41
|
+
case "predicate":
|
|
42
|
+
return 3;
|
|
43
|
+
case "rule":
|
|
44
|
+
return 2;
|
|
45
|
+
case "observation_review":
|
|
46
|
+
return 1;
|
|
47
|
+
default:
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function toolForLane(lane) {
|
|
52
|
+
switch (lane) {
|
|
53
|
+
case "strict_property":
|
|
54
|
+
return "kb_model_requirement";
|
|
55
|
+
case "predicate":
|
|
56
|
+
case "rule":
|
|
57
|
+
return "kb_suggest_predicates";
|
|
58
|
+
case "observation_review":
|
|
59
|
+
return "kb_model_requirement";
|
|
60
|
+
default:
|
|
61
|
+
return "kb_model_requirement";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Drop sync-path builtin ontology `predicate` suggestions.
|
|
66
|
+
*
|
|
67
|
+
* `analyzeSemanticAdvisorInput` always consults the builtin pack for
|
|
68
|
+
* maintenance-safe deterministic matching. When a replace ontology pack is
|
|
69
|
+
* active, those suggestions must not survive into the public receipt —
|
|
70
|
+
* including when the replace pack abstains with a valid empty match set.
|
|
71
|
+
*/
|
|
72
|
+
// implements REQ-capability-plugin-activation-disclosure-v1
|
|
73
|
+
export function stripSyncBuiltinOntologySuggestions(analysis) {
|
|
74
|
+
const suggestions = analysis.receipt.suggestions.filter((suggestion) => suggestion.kind !== "predicate");
|
|
75
|
+
if (suggestions.length === analysis.receipt.suggestions.length) {
|
|
76
|
+
return analysis;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
...analysis,
|
|
80
|
+
receipt: {
|
|
81
|
+
...analysis.receipt,
|
|
82
|
+
suggestions,
|
|
83
|
+
},
|
|
84
|
+
warnings: [
|
|
85
|
+
...analysis.warnings,
|
|
86
|
+
"Builtin ontology predicate suggestions cleared for replace-mode ontology pack.",
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Host-construct advisory modeling suggestions from validated ontology matches.
|
|
92
|
+
*
|
|
93
|
+
* When `replaced` is true, matches replace any remaining claim-keyed
|
|
94
|
+
* suggestion for the matched claim (after sync builtin predicates were
|
|
95
|
+
* stripped). When false (augment/builtin), matches only fill claims that
|
|
96
|
+
* lack a suggestion.
|
|
97
|
+
*/
|
|
98
|
+
// implements REQ-capability-plugin-activation-disclosure-v1
|
|
99
|
+
export function applyOntologyMatchSuggestions(analysis, ontologyMatches, options = { replaced: false }) {
|
|
100
|
+
if (analysis.receipt.logic_readiness === "modeled") {
|
|
101
|
+
return analysis;
|
|
102
|
+
}
|
|
103
|
+
if (ontologyMatches.length === 0 && !options.replaced) {
|
|
104
|
+
return analysis;
|
|
105
|
+
}
|
|
106
|
+
const propositions = analysis.receipt.propositions;
|
|
107
|
+
const suggestions = [
|
|
108
|
+
...analysis.receipt.suggestions,
|
|
109
|
+
];
|
|
110
|
+
const suggestionByClaim = new Map(suggestions.map((suggestion) => [suggestion.claim_key, suggestion]));
|
|
111
|
+
for (const match of ontologyMatches) {
|
|
112
|
+
const claimKey = (match.claimKey &&
|
|
113
|
+
propositions.some((proposition) => proposition.claim_key === match.claimKey)
|
|
114
|
+
? match.claimKey
|
|
115
|
+
: undefined) ??
|
|
116
|
+
propositions.find((proposition) => proposition.claim_text.includes(match.evidence))?.claim_key ??
|
|
117
|
+
propositions.find((proposition) => match.evidence.length > 0
|
|
118
|
+
? proposition.claim_text.includes(match.evidence)
|
|
119
|
+
: false)?.claim_key ??
|
|
120
|
+
propositions[0]?.claim_key;
|
|
121
|
+
if (!claimKey)
|
|
122
|
+
continue;
|
|
123
|
+
const proposition = propositions.find((entry) => entry.claim_key === claimKey);
|
|
124
|
+
if (!proposition)
|
|
125
|
+
continue;
|
|
126
|
+
const existing = suggestionByClaim.get(claimKey);
|
|
127
|
+
if (existing && !options.replaced)
|
|
128
|
+
continue;
|
|
129
|
+
if (existing &&
|
|
130
|
+
options.replaced &&
|
|
131
|
+
existing.kind !== "predicate" &&
|
|
132
|
+
existing.kind !== "ontology_gap") {
|
|
133
|
+
// Preserve strict/rule/ambiguity host suggestions under replace.
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const advisory = {
|
|
137
|
+
kind: "ontology_gap",
|
|
138
|
+
claim_key: claimKey,
|
|
139
|
+
claim_text: proposition.claim_text,
|
|
140
|
+
confidence: match.confidence,
|
|
141
|
+
evidence: match.evidence,
|
|
142
|
+
rationale: match.rationale ??
|
|
143
|
+
`Ontology pack '${match.packId}' matched ${match.predicateName}; review via kb_suggest_predicates before mutation.`,
|
|
144
|
+
suggested_next_tool: "kb_suggest_predicates",
|
|
145
|
+
recommendedPredicateSchema: {
|
|
146
|
+
predicate_name: match.predicateName,
|
|
147
|
+
argument_names: match.arguments.map((_, index) => `arg${index}`),
|
|
148
|
+
argument_types: match.arguments.map(() => "string"),
|
|
149
|
+
},
|
|
150
|
+
applyPlan: [],
|
|
151
|
+
};
|
|
152
|
+
if (existing) {
|
|
153
|
+
const index = suggestions.findIndex((entry) => entry.claim_key === existing.claim_key &&
|
|
154
|
+
entry.kind === existing.kind);
|
|
155
|
+
if (index >= 0)
|
|
156
|
+
suggestions[index] = advisory;
|
|
157
|
+
else
|
|
158
|
+
suggestions.push(advisory);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
suggestions.push(advisory);
|
|
162
|
+
}
|
|
163
|
+
suggestionByClaim.set(claimKey, advisory);
|
|
164
|
+
}
|
|
165
|
+
const tools = [
|
|
166
|
+
...new Set([
|
|
167
|
+
...analysis.receipt.suggested_next_tools,
|
|
168
|
+
...suggestions.map((suggestion) => suggestion.suggested_next_tool),
|
|
169
|
+
]),
|
|
170
|
+
];
|
|
171
|
+
return {
|
|
172
|
+
...analysis,
|
|
173
|
+
receipt: {
|
|
174
|
+
...analysis.receipt,
|
|
175
|
+
suggestions,
|
|
176
|
+
suggested_next_tools: tools,
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Apply host-validated classifier decisions as a bounded routing seam.
|
|
182
|
+
*
|
|
183
|
+
* Plugins may influence per-proposition routing, candidate lane, suggested
|
|
184
|
+
* next tools, and advisory ambiguity metadata. They must not delete
|
|
185
|
+
* propositions, erase assertive unresolved obligations, downgrade modeled
|
|
186
|
+
* completeness, manufacture typed grounding, or invent mutation/proof.
|
|
187
|
+
*/
|
|
188
|
+
// implements REQ-capability-plugin-activation-disclosure-v1
|
|
189
|
+
export function applyClassificationRouting(analysis, classification, ontologyMatches = [], options = {}) {
|
|
190
|
+
const receipt = analysis.receipt;
|
|
191
|
+
if (receipt.logic_readiness === "modeled") {
|
|
192
|
+
// Never downgrade semantic completeness via plugin classification.
|
|
193
|
+
return {
|
|
194
|
+
receipt: {
|
|
195
|
+
...receipt,
|
|
196
|
+
// Still expose advisory plugin metadata without changing completeness.
|
|
197
|
+
},
|
|
198
|
+
warnings: [
|
|
199
|
+
...analysis.warnings,
|
|
200
|
+
...(classification.fallbackUsed
|
|
201
|
+
? [
|
|
202
|
+
"Semantic classifier fell back to the builtin provider after an external classifier failure.",
|
|
203
|
+
]
|
|
204
|
+
: []),
|
|
205
|
+
...classification.diagnostics.map((diagnostic) => {
|
|
206
|
+
const model = diagnostic.model ? ` model=${diagnostic.model}` : "";
|
|
207
|
+
return `Semantic classifier '${diagnostic.pluginId}' failed (${diagnostic.code}${model}): ${diagnostic.message}`;
|
|
208
|
+
}),
|
|
209
|
+
],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const byClaim = new Map(classification.decisions.map((decision) => [decision.claimKey, decision]));
|
|
213
|
+
const classifierReplaced = options.classifierReplaced === true;
|
|
214
|
+
const propositions = receipt.propositions.map((proposition) => {
|
|
215
|
+
const decision = byClaim.get(proposition.claim_key);
|
|
216
|
+
if (!decision)
|
|
217
|
+
return proposition;
|
|
218
|
+
// `none` cannot erase an assertive unresolved obligation.
|
|
219
|
+
if (decision.lane === "none" && isAssertiveUnresolved(proposition)) {
|
|
220
|
+
return proposition;
|
|
221
|
+
}
|
|
222
|
+
if (decision.lane === "none")
|
|
223
|
+
return proposition;
|
|
224
|
+
let status = proposition.status;
|
|
225
|
+
if (decision.ambiguity?.ambiguous === true && status === "missing") {
|
|
226
|
+
status = "ambiguous";
|
|
227
|
+
}
|
|
228
|
+
else if (decision.lane === "predicate" && status === "missing") {
|
|
229
|
+
// Route toward predicate modeling without claiming the claim is grounded.
|
|
230
|
+
status = "missing";
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
...proposition,
|
|
234
|
+
status,
|
|
235
|
+
...(decision.ambiguity?.ambiguous === true
|
|
236
|
+
? {
|
|
237
|
+
reason: proposition.reason ??
|
|
238
|
+
"Classifier marked this claim as ambiguous; review before grounding.",
|
|
239
|
+
}
|
|
240
|
+
: {}),
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
const suggestionByClaim = new Map(receipt.suggestions.map((suggestion) => [suggestion.claim_key, suggestion]));
|
|
244
|
+
const suggestions = [...receipt.suggestions];
|
|
245
|
+
for (const decision of classification.decisions) {
|
|
246
|
+
if (decision.lane === "none")
|
|
247
|
+
continue;
|
|
248
|
+
const proposition = propositions.find((entry) => entry.claim_key === decision.claimKey);
|
|
249
|
+
if (!proposition)
|
|
250
|
+
continue;
|
|
251
|
+
const existing = suggestionByClaim.get(decision.claimKey);
|
|
252
|
+
const nextTool = toolForLane(decision.lane);
|
|
253
|
+
if (existing) {
|
|
254
|
+
// Re-route suggested tool when classifier lifts the lane; never remove.
|
|
255
|
+
if (laneRank(decision.lane) > 0 &&
|
|
256
|
+
existing.suggested_next_tool !== nextTool &&
|
|
257
|
+
(existing.kind === "ambiguity_observation" ||
|
|
258
|
+
existing.kind === "ontology_gap" ||
|
|
259
|
+
(decision.lane === "predicate" &&
|
|
260
|
+
existing.kind !== "strict_property"))) {
|
|
261
|
+
const updated = {
|
|
262
|
+
...existing,
|
|
263
|
+
suggested_next_tool: nextTool,
|
|
264
|
+
rationale: `${existing.rationale} Classifier lane '${decision.lane}' recommends ${nextTool}.`,
|
|
265
|
+
};
|
|
266
|
+
const index = suggestions.findIndex((entry) => entry.claim_key === existing.claim_key &&
|
|
267
|
+
entry.kind === existing.kind);
|
|
268
|
+
if (index >= 0)
|
|
269
|
+
suggestions[index] = updated;
|
|
270
|
+
suggestionByClaim.set(decision.claimKey, updated);
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
// Host-authored advisory suggestion from classifier routing only.
|
|
275
|
+
if (decision.lane === "predicate" || decision.lane === "rule") {
|
|
276
|
+
const advisory = {
|
|
277
|
+
kind: "ontology_gap",
|
|
278
|
+
claim_key: decision.claimKey,
|
|
279
|
+
claim_text: proposition.claim_text,
|
|
280
|
+
confidence: decision.confidence,
|
|
281
|
+
evidence: decision.signals?.join(", ") || decision.lane,
|
|
282
|
+
rationale: "Classifier recommends predicate/ontology modeling; the claim remains unresolved until grounded via kb_suggest_predicates.",
|
|
283
|
+
suggested_next_tool: "kb_suggest_predicates",
|
|
284
|
+
recommendedPredicateSchema: null,
|
|
285
|
+
applyPlan: [],
|
|
286
|
+
};
|
|
287
|
+
suggestions.push(advisory);
|
|
288
|
+
suggestionByClaim.set(decision.claimKey, advisory);
|
|
289
|
+
}
|
|
290
|
+
else if (decision.lane === "observation_review" ||
|
|
291
|
+
decision.ambiguity?.ambiguous === true) {
|
|
292
|
+
const advisory = {
|
|
293
|
+
kind: "ambiguity_observation",
|
|
294
|
+
claim_key: decision.claimKey,
|
|
295
|
+
claim_text: proposition.claim_text,
|
|
296
|
+
confidence: decision.confidence,
|
|
297
|
+
evidence: decision.signals?.join(", ") || decision.lane,
|
|
298
|
+
rationale: "Classifier flagged this claim for review before assertive modeling.",
|
|
299
|
+
ambiguity: ["needs_review"],
|
|
300
|
+
suggested_next_tool: "kb_model_requirement",
|
|
301
|
+
applyPlan: [],
|
|
302
|
+
};
|
|
303
|
+
suggestions.push(advisory);
|
|
304
|
+
suggestionByClaim.set(decision.claimKey, advisory);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// Replace mode owns the candidate lane. Start from none so a valid empty
|
|
308
|
+
// decisions[] abstention does not inherit sync analyze-prose routing.
|
|
309
|
+
let candidateLane = classifierReplaced
|
|
310
|
+
? "none"
|
|
311
|
+
: receipt.candidate_lane;
|
|
312
|
+
for (const decision of classification.decisions) {
|
|
313
|
+
if (laneRank(decision.lane) > laneRank(candidateLane)) {
|
|
314
|
+
candidateLane = decision.lane;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const tools = [
|
|
318
|
+
...new Set([
|
|
319
|
+
...receipt.suggested_next_tools,
|
|
320
|
+
...suggestions.map((suggestion) => suggestion.suggested_next_tool),
|
|
321
|
+
...(candidateLane !== "none" ? [toolForLane(candidateLane)] : []),
|
|
322
|
+
]),
|
|
323
|
+
];
|
|
324
|
+
const nextReceipt = {
|
|
325
|
+
...receipt,
|
|
326
|
+
candidate_lane: candidateLane,
|
|
327
|
+
propositions,
|
|
328
|
+
suggestions,
|
|
329
|
+
suggested_next_tools: tools,
|
|
330
|
+
summary: candidateLane === "none"
|
|
331
|
+
? receipt.summary
|
|
332
|
+
: `${receipt.summary} Classifier routing: ${candidateLane} → ${toolForLane(candidateLane)}.`,
|
|
333
|
+
};
|
|
334
|
+
const warnings = [...analysis.warnings];
|
|
335
|
+
if (classification.fallbackUsed) {
|
|
336
|
+
warnings.push("Semantic classifier fell back to the builtin provider after an external classifier failure.");
|
|
337
|
+
}
|
|
338
|
+
for (const diagnostic of classification.diagnostics) {
|
|
339
|
+
const model = diagnostic.model ? ` model=${diagnostic.model}` : "";
|
|
340
|
+
warnings.push(`Semantic classifier '${diagnostic.pluginId}' failed (${diagnostic.code}${model}): ${diagnostic.message}`);
|
|
341
|
+
}
|
|
342
|
+
if (classifierReplaced &&
|
|
343
|
+
classification.decisions.every((decision) => decision.lane === "none")) {
|
|
344
|
+
warnings.push("Replace semantic classifier abstained; sync analyze-prose candidate lane was not retained.");
|
|
345
|
+
}
|
|
346
|
+
if (candidateLane === "predicate" || candidateLane === "rule") {
|
|
347
|
+
warnings.push("Classifier recommends predicate/ontology review; unresolved claims remain unresolved until grounded.");
|
|
348
|
+
}
|
|
349
|
+
// Ontology matches apply after classifier routing so replace can override.
|
|
350
|
+
return applyOntologyMatchSuggestions({ receipt: nextReceipt, warnings }, ontologyMatches, { replaced: options.replaced === true });
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Async orchestration for allowlisted semantic operations.
|
|
354
|
+
* Maintenance paths must keep calling sync `analyzeSemanticAdvisorInput`.
|
|
355
|
+
*/
|
|
356
|
+
// implements REQ-capability-plugin-activation-disclosure-v1, REQ-capability-plugin-observable-behavior-v1
|
|
357
|
+
export async function analyzeSemanticAdvisorInputWithPlugins(input, options) {
|
|
358
|
+
const analysis = analyzeSemanticAdvisorInput(input);
|
|
359
|
+
if (!options.ensurePlugins ||
|
|
360
|
+
!allowsExternalSemanticClassifier(options.operationName)) {
|
|
361
|
+
return {
|
|
362
|
+
analysis,
|
|
363
|
+
classification: null,
|
|
364
|
+
ontologyCatalog: null,
|
|
365
|
+
ontologyMatches: [],
|
|
366
|
+
ontologyShadowMatches: [],
|
|
367
|
+
stamps: [],
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
const registry = await options.ensurePlugins();
|
|
371
|
+
const configured = await hasConfiguredPlugins(registry);
|
|
372
|
+
if (!configured) {
|
|
373
|
+
return {
|
|
374
|
+
analysis,
|
|
375
|
+
classification: null,
|
|
376
|
+
ontologyCatalog: null,
|
|
377
|
+
ontologyMatches: [],
|
|
378
|
+
ontologyShadowMatches: [],
|
|
379
|
+
stamps: [],
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
const stamps = [];
|
|
383
|
+
let classification = null;
|
|
384
|
+
let ontologyCatalog = null;
|
|
385
|
+
let ontologyMatches = [];
|
|
386
|
+
let ontologyShadowMatches = [];
|
|
387
|
+
let nextAnalysis = analysis;
|
|
388
|
+
if (analysis.receipt.logic_readiness !== "modeled") {
|
|
389
|
+
const propositions = analysis.receipt.propositions.map((proposition) => ({
|
|
390
|
+
claimKey: proposition.claim_key,
|
|
391
|
+
statement: proposition.claim_text,
|
|
392
|
+
role: proposition.role,
|
|
393
|
+
}));
|
|
394
|
+
const resolution = await registry.resolveSemanticClassifiers();
|
|
395
|
+
classification = await composeSemanticClassification(resolution, { propositions }, { operationName: options.operationName });
|
|
396
|
+
stamps.push(...classification.stamps);
|
|
397
|
+
}
|
|
398
|
+
const packResolution = await registry.resolveOntologyPacks();
|
|
399
|
+
ontologyCatalog = composeOntologyCatalog(packResolution);
|
|
400
|
+
stamps.push(...ontologyCatalog.stamps);
|
|
401
|
+
const contexts = nextAnalysis.receipt.propositions.map((proposition) => ({
|
|
402
|
+
claimKey: proposition.claim_key,
|
|
403
|
+
statement: proposition.claim_text,
|
|
404
|
+
role: proposition.role,
|
|
405
|
+
}));
|
|
406
|
+
const seen = new Set();
|
|
407
|
+
const collected = [];
|
|
408
|
+
const shadowCollected = [];
|
|
409
|
+
const shadowSeen = new Set();
|
|
410
|
+
for (const matchContext of contexts) {
|
|
411
|
+
const matched = composeOntologyMatches(packResolution, matchContext);
|
|
412
|
+
for (const candidate of matched.canonical) {
|
|
413
|
+
const key = [
|
|
414
|
+
candidate.schemaId,
|
|
415
|
+
candidate.polarity,
|
|
416
|
+
...candidate.arguments,
|
|
417
|
+
].join("\0");
|
|
418
|
+
if (seen.has(key))
|
|
419
|
+
continue;
|
|
420
|
+
seen.add(key);
|
|
421
|
+
collected.push(candidate);
|
|
422
|
+
}
|
|
423
|
+
for (const candidate of matched.shadow) {
|
|
424
|
+
const key = [
|
|
425
|
+
candidate.schemaId,
|
|
426
|
+
candidate.polarity,
|
|
427
|
+
...candidate.arguments,
|
|
428
|
+
].join("\0");
|
|
429
|
+
if (shadowSeen.has(key))
|
|
430
|
+
continue;
|
|
431
|
+
shadowSeen.add(key);
|
|
432
|
+
shadowCollected.push(candidate);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
ontologyMatches = collected;
|
|
436
|
+
ontologyShadowMatches = shadowCollected;
|
|
437
|
+
const replaced = ontologyCatalog?.replaced === true;
|
|
438
|
+
// Replace mode must clear sync-path builtin predicate suggestions even when
|
|
439
|
+
// the replace pack abstains with a valid empty match set.
|
|
440
|
+
if (replaced) {
|
|
441
|
+
nextAnalysis = stripSyncBuiltinOntologySuggestions(nextAnalysis);
|
|
442
|
+
}
|
|
443
|
+
if (classification) {
|
|
444
|
+
const classifierReplaced = !classification.fallbackUsed &&
|
|
445
|
+
classification.stamps.some((stamp) => stamp.mode === "replace");
|
|
446
|
+
nextAnalysis = applyClassificationRouting(nextAnalysis, classification, ontologyMatches, { replaced, classifierReplaced });
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
nextAnalysis = applyOntologyMatchSuggestions(nextAnalysis, ontologyMatches, {
|
|
450
|
+
replaced,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
analysis: nextAnalysis,
|
|
455
|
+
classification,
|
|
456
|
+
ontologyCatalog,
|
|
457
|
+
ontologyMatches,
|
|
458
|
+
ontologyShadowMatches,
|
|
459
|
+
stamps,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Run allowlisted semantic classifier composition without re-running advisor
|
|
464
|
+
* analysis.
|
|
465
|
+
*/
|
|
466
|
+
// implements REQ-capability-plugin-activation-disclosure-v1
|
|
467
|
+
export async function composeSemanticClassificationForOperation(context, operationName, input) {
|
|
468
|
+
if (!context?.ensurePlugins)
|
|
469
|
+
return null;
|
|
470
|
+
if (!allowsExternalSemanticClassifier(operationName))
|
|
471
|
+
return null;
|
|
472
|
+
const registry = await context.ensurePlugins();
|
|
473
|
+
if (!(await hasConfiguredPlugins(registry)))
|
|
474
|
+
return null;
|
|
475
|
+
const resolution = await registry.resolveSemanticClassifiers();
|
|
476
|
+
return composeSemanticClassification(resolution, input, { operationName });
|
|
477
|
+
}
|
|
478
|
+
// implements REQ-capability-plugin-activation-disclosure-v1
|
|
479
|
+
export async function composeOntologyCatalogForOperation(context, operationName) {
|
|
480
|
+
if (!context?.ensurePlugins)
|
|
481
|
+
return null;
|
|
482
|
+
if (!allowsExternalSemanticClassifier(operationName))
|
|
483
|
+
return null;
|
|
484
|
+
const registry = await context.ensurePlugins();
|
|
485
|
+
if (!(await hasConfiguredPlugins(registry)))
|
|
486
|
+
return null;
|
|
487
|
+
const resolution = await registry.resolveOntologyPacks();
|
|
488
|
+
return composeOntologyCatalog(resolution);
|
|
489
|
+
}
|
|
@@ -1,79 +1,92 @@
|
|
|
1
|
-
import { commaList } from "./predicate-rule.js";
|
|
2
1
|
export declare const CORE_PREDICATE_RULES: readonly [{
|
|
3
2
|
readonly pattern: RegExp;
|
|
4
3
|
readonly name: "guard";
|
|
4
|
+
readonly arity: 3;
|
|
5
5
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
6
6
|
readonly rationale: "Disabled-until prose defines a condition that guards behavior availability and should be queryable as a predicate.";
|
|
7
7
|
}, {
|
|
8
8
|
readonly pattern: RegExp;
|
|
9
9
|
readonly name: "temporal_order";
|
|
10
|
+
readonly arity: 3;
|
|
10
11
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
11
12
|
readonly rationale: "Before/after ordering is relational temporal logic and should be modeled as a predicate.";
|
|
12
13
|
}, {
|
|
13
14
|
readonly pattern: RegExp;
|
|
14
15
|
readonly name: "conditional_behavior";
|
|
16
|
+
readonly arity: 3;
|
|
15
17
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
16
18
|
readonly rationale: "If/then requirement prose is conditional behavior and should be queryable as a predicate.";
|
|
17
19
|
}, {
|
|
18
20
|
readonly pattern: RegExp;
|
|
19
21
|
readonly name: "conditional_behavior";
|
|
22
|
+
readonly arity: 3;
|
|
20
23
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
21
24
|
readonly rationale: "When/must prose is conditional behavior and should be queryable as a predicate.";
|
|
22
25
|
}, {
|
|
23
26
|
readonly pattern: RegExp;
|
|
24
27
|
readonly name: "exception_rule";
|
|
28
|
+
readonly arity: 3;
|
|
25
29
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
26
30
|
readonly rationale: "Unless/except prose defines an explicit exception to required behavior and should be queryable as a predicate.";
|
|
27
31
|
}, {
|
|
28
32
|
readonly pattern: RegExp;
|
|
29
33
|
readonly name: "mutual_exclusion";
|
|
34
|
+
readonly arity: 2;
|
|
30
35
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
31
36
|
readonly rationale: "Mutual-exclusion prose is a relational constraint and should be queryable as a predicate.";
|
|
32
37
|
}, {
|
|
33
38
|
readonly pattern: RegExp;
|
|
34
39
|
readonly name: "dependency_rule";
|
|
40
|
+
readonly arity: 3;
|
|
35
41
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
36
42
|
readonly rationale: "Requires-before prose defines a prerequisite relationship and should be queryable as a predicate.";
|
|
37
43
|
}, {
|
|
38
44
|
readonly pattern: RegExp;
|
|
39
45
|
readonly name: "ownership_rule";
|
|
46
|
+
readonly arity: 2;
|
|
40
47
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
41
48
|
readonly rationale: "Ownership prose assigns responsibility for a resource or behavior and should be queryable as a predicate.";
|
|
42
49
|
}, {
|
|
43
50
|
readonly pattern: RegExp;
|
|
44
51
|
readonly name: "merge_policy";
|
|
52
|
+
readonly arity: 3;
|
|
45
53
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
46
54
|
readonly rationale: "Same-slot merge prose defines ontology-lite merge behavior and should be queryable as a predicate.";
|
|
47
55
|
}, {
|
|
48
56
|
readonly pattern: RegExp;
|
|
49
57
|
readonly name: "retry_policy";
|
|
58
|
+
readonly arity: 3;
|
|
50
59
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
51
60
|
readonly rationale: "Retry prose defines bounded recovery behavior and should be queryable as a predicate.";
|
|
52
61
|
}, {
|
|
53
62
|
readonly pattern: RegExp;
|
|
54
63
|
readonly name: "escalation_rule";
|
|
64
|
+
readonly arity: 4;
|
|
55
65
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
56
66
|
readonly rationale: "Escalation prose defines delayed handoff behavior and should be queryable as a predicate.";
|
|
57
67
|
}, {
|
|
58
68
|
readonly pattern: RegExp;
|
|
59
69
|
readonly name: "availability_sla";
|
|
70
|
+
readonly arity: 4;
|
|
60
71
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
61
72
|
readonly rationale: "Availability SLA prose defines a service target over a window and should be queryable as a predicate.";
|
|
62
73
|
}, {
|
|
63
74
|
readonly pattern: RegExp;
|
|
64
75
|
readonly name: "notification_route";
|
|
76
|
+
readonly arity: 3;
|
|
65
77
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
66
78
|
readonly rationale: "Notification routing prose defines a recipient and channel and should be queryable as a predicate.";
|
|
67
79
|
}, {
|
|
68
80
|
readonly pattern: RegExp;
|
|
69
81
|
readonly name: "idempotency_rule";
|
|
82
|
+
readonly arity: 2;
|
|
70
83
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
71
84
|
readonly rationale: "Idempotency prose defines deduplication behavior and should be queryable as a predicate.";
|
|
72
85
|
}, {
|
|
73
86
|
readonly pattern: RegExp;
|
|
74
87
|
readonly name: "idempotency_rule";
|
|
88
|
+
readonly arity: 2;
|
|
75
89
|
readonly args: (g: Readonly<Record<string, string | undefined>>) => string[];
|
|
76
90
|
readonly rationale: "Deduplication prose defines idempotent handling of repeated or concurrent operations and should be queryable as a predicate.";
|
|
77
91
|
}];
|
|
78
|
-
export { commaList };
|
|
79
92
|
//# sourceMappingURL=predicate-rules-core.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"predicate-rules-core.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/predicate-rules-core.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"predicate-rules-core.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/predicate-rules-core.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA+B,CAAC"}
|