progmune-runtime 2.1.6 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -461
- package/dist/ablation-study.js +144 -0
- package/dist/ablation-study.test.js +18 -0
- package/dist/action-runtime.js +3 -1
- package/dist/active-learning.js +211 -0
- package/dist/analytics.js +139 -0
- package/dist/asset-factory.js +309 -0
- package/dist/asset-growth.js +244 -0
- package/dist/asset-promotion.js +382 -0
- package/dist/asset-quality.js +550 -0
- package/dist/audit/business-translator.js +285 -0
- package/dist/audit/cli.js +66 -0
- package/dist/audit/formatters/html.js +379 -0
- package/dist/audit/formatters/json.js +11 -0
- package/dist/audit/formatters/markdown.js +192 -0
- package/dist/audit/formatters/terminal.js +189 -0
- package/dist/audit/index.js +25 -0
- package/dist/audit/report-builder.js +318 -0
- package/dist/audit/types.js +8 -0
- package/dist/audit.js +3 -3
- package/dist/auto-benchmark-generator.js +137 -0
- package/dist/auto-benchmark-generator.test.js +45 -0
- package/dist/auto-protocol-synthesizer.js +362 -0
- package/dist/auto-protocol-synthesizer.test.js +82 -0
- package/dist/autonomous-patch.js +175 -0
- package/dist/autonomous-patch.test.js +128 -0
- package/dist/badge/badge-server.js +98 -0
- package/dist/behavior-miner.js +442 -0
- package/dist/belief-layer.js +475 -0
- package/dist/benchmark-count.js +5 -0
- package/dist/benchmark-generator.js +211 -0
- package/dist/benchmark-harness.js +201 -0
- package/dist/benchmark-pass-rate.js +7 -0
- package/dist/benchmark-report.js +8 -3
- package/dist/benchmark-save.js +14 -1
- package/dist/bootstrap-validation.js +197 -0
- package/dist/bootstrap-validation.test.js +51 -0
- package/dist/branch-ledger.js +1 -1
- package/dist/capability-gap.js +130 -0
- package/dist/certify-html.js +351 -0
- package/dist/certify.js +326 -0
- package/dist/check.js +4 -4
- package/dist/compliance-miner.js +447 -0
- package/dist/continuous-benchmark.js +194 -0
- package/dist/continuous-benchmark.test.js +116 -0
- package/dist/corpus-stats.js +173 -0
- package/dist/counterfactual-engine.js +288 -0
- package/dist/coverage-dashboard.js +109 -0
- package/dist/coverage-system.test.js +205 -0
- package/dist/cross-repo-precision.js +352 -0
- package/dist/cve-benchmark.js +180 -0
- package/dist/cve-benchmark.test.js +28 -0
- package/dist/cve-collector.js +73 -0
- package/dist/data-quality.js +141 -0
- package/dist/decision-engine.js +388 -0
- package/dist/derive-metadata.js +250 -0
- package/dist/difficulty-active.test.js +198 -0
- package/dist/difficulty-map.js +244 -0
- package/dist/discovery-analytics.js +125 -0
- package/dist/discovery-model.js +149 -0
- package/dist/discovery-optimize.test.js +199 -0
- package/dist/discovery-trace.js +276 -0
- package/dist/discovery-trace.test.js +97 -0
- package/dist/emitter.js +83 -1
- package/dist/enterprise-dashboard.js +405 -0
- package/dist/eval-hardening.js +297 -0
- package/dist/eval-hardening.test.js +85 -0
- package/dist/evaluation-campaign.js +359 -0
- package/dist/evaluation-campaign.test.js +181 -0
- package/dist/evidence-growth.js +143 -0
- package/dist/evidence-repository.js +209 -0
- package/dist/evidence-system.js +441 -0
- package/dist/execute.js +15 -7
- package/dist/experimental/software-physics.js +291 -0
- package/dist/experimental/state-inference.js +516 -0
- package/dist/experimental/unsupervised-physics.js +230 -0
- package/dist/extract-ir-python.js +54 -7
- package/dist/extract-ir.js +376 -12
- package/dist/failure-collector.js +2 -2
- package/dist/failure-corpus.js +322 -9
- package/dist/feedback.js +16 -5
- package/dist/feedback.test.js +49 -0
- package/dist/file-lock.js +1 -1
- package/dist/flywheel-batch.js +292 -0
- package/dist/frameworks/express-cli.js +237 -0
- package/dist/frameworks/express-detector.js +445 -0
- package/dist/frameworks/express-detector.test.js +206 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/nestjs-detector.js +302 -0
- package/dist/frameworks/trpc-detector.js +161 -0
- package/dist/frameworks/version-awareness.js +179 -0
- package/dist/function-synonyms.js +164 -0
- package/dist/function-synonyms.test.js +68 -0
- package/dist/generalization.test.js +352 -0
- package/dist/goal-annotator.js +113 -0
- package/dist/goal-planner.js +563 -0
- package/dist/gold-cve.js +164 -0
- package/dist/gold-cve.test.js +104 -0
- package/dist/gold-quality.js +206 -0
- package/dist/gold-tiers.js +241 -0
- package/dist/governance-dashboard.js +327 -0
- package/dist/graph-viz.js +240 -0
- package/dist/guided-frontier.js +195 -0
- package/dist/hierarchical-planner.js +148 -0
- package/dist/identifier-parser.js +260 -0
- package/dist/immune-metrics.js +93 -0
- package/dist/immune-receiver.js +158 -0
- package/dist/immune-reporter.js +1 -1
- package/dist/improvement-orchestrator.js +206 -0
- package/dist/inject-p0-vocabulary.js +300 -0
- package/dist/intent-parser.js +218 -0
- package/dist/invariant-algebra.js +476 -0
- package/dist/invariant-calculus.js +533 -0
- package/dist/ir-utils.js +70 -0
- package/dist/ir-utils.test.js +50 -0
- package/dist/knowledge-api.js +312 -0
- package/dist/knowledge-evolution.js +452 -0
- package/dist/knowledge-explorer.js +506 -0
- package/dist/knowledge-flywheel.js +274 -0
- package/dist/knowledge-governance.js +338 -0
- package/dist/knowledge-governance.test.js +150 -0
- package/dist/knowledge-graph.js +181 -0
- package/dist/knowledge-guided-synth.js +246 -0
- package/dist/knowledge-loop.test.js +77 -0
- package/dist/knowledge-object.js +316 -0
- package/dist/knowledge-package.js +98 -0
- package/dist/kpi-dashboard.js +561 -0
- package/dist/l3-cross-function.js +280 -0
- package/dist/learning-ranker.js +148 -0
- package/dist/learning-ranker.test.js +291 -0
- package/dist/ledger/accountability.js +322 -0
- package/dist/ledger/chain-builder.js +185 -0
- package/dist/ledger/cli.js +222 -0
- package/dist/ledger/index.js +13 -0
- package/dist/ledger/signatures.js +193 -0
- package/dist/ledger/types.js +9 -0
- package/dist/llm.js +74 -3
- package/dist/load-benchmarks.js +8 -3
- package/dist/logger.js +66 -0
- package/dist/logger.test.js +37 -0
- package/dist/logistic-reward.js +339 -0
- package/dist/logistic-reward.test.js +180 -0
- package/dist/macro-graph.js +193 -0
- package/dist/macro-repair.js +183 -0
- package/dist/mcp-server.mjs +1202 -483
- package/dist/memory-layer.js +42 -5
- package/dist/multi-repo-precision.js +422 -0
- package/dist/name-free-protocol.js +425 -0
- package/dist/name-free-protocol.test.js +170 -0
- package/dist/name-scrambling.js +138 -0
- package/dist/name-scrambling.test.js +16 -0
- package/dist/p3-observability.test.js +281 -0
- package/dist/p5-orchestrator.test.js +225 -0
- package/dist/pairwise-preference.js +294 -0
- package/dist/pairwise-preference.test.js +140 -0
- package/dist/planner-constraints.js +104 -0
- package/dist/planner-prompts.js +155 -0
- package/dist/planner-telemetry.js +415 -0
- package/dist/planner-trace.js +214 -0
- package/dist/planner.js +162 -167
- package/dist/plsb/artifact.js +116 -0
- package/dist/plsb/cli.js +71 -0
- package/dist/plsb/index.js +19 -0
- package/dist/plsb/leaderboard.js +249 -0
- package/dist/plsb/report-md.js +156 -0
- package/dist/plsb/schema.js +179 -0
- package/dist/plsb-benchmark.js +284 -0
- package/dist/plsb-benchmark.test.js +119 -0
- package/dist/policy/cli.js +134 -0
- package/dist/policy/engine.js +333 -0
- package/dist/policy/index.js +12 -0
- package/dist/policy/types.js +59 -0
- package/dist/policy-miner.js +505 -0
- package/dist/precision-analyze.js +229 -0
- package/dist/precision-benchmark.js +147 -0
- package/dist/precision-label-c.js +134 -0
- package/dist/precision-label.js +193 -0
- package/dist/precision-report-c.js +149 -0
- package/dist/precision-report.js +246 -0
- package/dist/progmune-status.js +108 -0
- package/dist/proof-engine.js +479 -0
- package/dist/proof-provenance.js +315 -0
- package/dist/protocol-coverage.js +294 -0
- package/dist/protocol-detector.js +1189 -0
- package/dist/protocol-embedding-expanded.js +297 -0
- package/dist/protocol-embedding-expanded.test.js +97 -0
- package/dist/protocol-embedding.js +195 -0
- package/dist/protocol-embedding.test.js +82 -0
- package/dist/protocol-extractor-v2.js +354 -0
- package/dist/protocol-extractor-v2.test.js +140 -0
- package/dist/protocol-extractor.js +310 -0
- package/dist/protocol-extractor.test.js +113 -0
- package/dist/protocol-foundation.js +322 -0
- package/dist/protocol-foundation.test.js +163 -0
- package/dist/protocol-frontier.js +243 -0
- package/dist/protocol-frontier.test.js +92 -0
- package/dist/protocol-gap-analyzer.js +228 -0
- package/dist/protocol-gap-analyzer.test.js +49 -0
- package/dist/protocol-invariants.js +276 -0
- package/dist/protocol-invariants.test.js +111 -0
- package/dist/protocol-knowledge.js +464 -0
- package/dist/protocol-miner.js +343 -0
- package/dist/protocol-mining.js +207 -0
- package/dist/protocol-mining.test.js +37 -0
- package/dist/protocol-registry.js +1 -1
- package/dist/protocol-security-benchmark.js +222 -0
- package/dist/protocol-vulnerability.js +257 -0
- package/dist/protocol-vulnerability.test.js +60 -0
- package/dist/python-benchmark.js +120 -0
- package/dist/python-emitter.js +163 -45
- package/dist/python-protocol-extractor.js +187 -0
- package/dist/python-protocol-extractor.test.js +116 -0
- package/dist/realworld-benchmark.js +646 -0
- package/dist/realworld-benchmark.test.js +36 -0
- package/dist/repair-arch.test.js +411 -0
- package/dist/repair-evolution.test.js +454 -0
- package/dist/repair-executor.js +719 -0
- package/dist/repair-proposal.js +4 -4
- package/dist/repair-ranker.js +141 -0
- package/dist/repair-strategies.js +419 -0
- package/dist/repair-taxonomy.js +234 -0
- package/dist/repair-types.js +12 -0
- package/dist/repo-evaluator.js +250 -0
- package/dist/repo-evaluator.test.js +128 -0
- package/dist/resource-abstraction.js +242 -0
- package/dist/resource-detector.js +211 -0
- package/dist/result.test.js +43 -0
- package/dist/reward-system.js +411 -0
- package/dist/reward-system.test.js +175 -0
- package/dist/risk-model.js +215 -0
- package/dist/rule-miner.js +234 -7
- package/dist/rule-specificity.js +254 -0
- package/dist/runtime-types.js +27 -0
- package/dist/scaffold.js +208 -0
- package/dist/scale-collector.test.js +101 -0
- package/dist/scale-trajectory-collector.js +128 -0
- package/dist/sdk.js +250 -0
- package/dist/search-planner.js +4 -41
- package/dist/semantic-snapshot.js +1 -1
- package/dist/semantic-topology.js +121 -0
- package/dist/semantic-trace.js +310 -317
- package/dist/sequence-extractor.js +343 -0
- package/dist/skill-library.js +245 -0
- package/dist/skill-planner.test.js +189 -0
- package/dist/software-physics.js +291 -0
- package/dist/software-physics.test.js +81 -0
- package/dist/ssg-precision.js +478 -0
- package/dist/ssg-validator.js +71 -21
- package/dist/state-inference-doubleblind.test.js +160 -0
- package/dist/state-inference.js +516 -0
- package/dist/state-inference.test.js +115 -0
- package/dist/state-machine-fingerprint.js +345 -0
- package/dist/state-machine-fingerprint.test.js +120 -0
- package/dist/state-miner.js +386 -0
- package/dist/state-name-inference.js +213 -0
- package/dist/state-name-inference.test.js +69 -0
- package/dist/strategy-planner.js +262 -96
- package/dist/strategy-planner.test.js +135 -0
- package/dist/telemetry-analytics.test.js +402 -0
- package/dist/terminal-format.js +68 -0
- package/dist/terminal-format.test.js +83 -0
- package/dist/topology-factory.js +196 -0
- package/dist/topology-representation.js +242 -0
- package/dist/topology-representation.test.js +27 -0
- package/dist/trajectory-augmentation.js +254 -0
- package/dist/trajectory-augmentation.test.js +63 -0
- package/dist/trajectory-corpus.js +440 -0
- package/dist/trajectory-corpus.test.js +32 -0
- package/dist/trajectory-feedback.test.js +116 -0
- package/dist/transition-synthesizer.js +286 -0
- package/dist/transition-synthesizer.test.js +123 -0
- package/dist/trust/api-semantic-mapper.js +809 -0
- package/dist/trust/call-graph-propagator.js +225 -0
- package/dist/trust/cli.js +122 -0
- package/dist/trust/compliance-scorer.js +283 -0
- package/dist/trust/confidence-calculator.js +261 -0
- package/dist/trust/engine.js +1145 -0
- package/dist/trust/explainability.js +85 -0
- package/dist/trust/formatters/ci.js +42 -0
- package/dist/trust/formatters/json.js +11 -0
- package/dist/trust/formatters/terminal.js +152 -0
- package/dist/trust/index.js +39 -0
- package/dist/trust/phase1-verify.js +171 -0
- package/dist/trust/protocol-domain-validator.js +697 -0
- package/dist/trust/score-calculator.js +282 -0
- package/dist/trust/ssg-bridge.js +641 -0
- package/dist/trust/ssg-bridge.test.js +269 -0
- package/dist/trust/types.js +67 -0
- package/dist/trust/violation-trace.js +335 -0
- package/dist/trust-api.js +179 -0
- package/dist/trust-calibration.js +279 -0
- package/dist/unknown-protocol-discovery.js +339 -0
- package/dist/unknown-protocol-discovery.test.js +102 -0
- package/dist/unsupervised-physics.js +230 -0
- package/dist/unsupervised-physics.test.js +95 -0
- package/dist/utils.test.js +37 -0
- package/dist/validator.js +187 -10
- package/dist/verification-intelligence.js +475 -0
- package/dist/verify-api.js +432 -0
- package/dist/vi-impact-report.js +293 -0
- package/dist/wl-fingerprint.js +162 -0
- package/dist/wl-fingerprint.test.js +130 -0
- package/dist/zeroshot-strategy.js +139 -0
- package/docs/Progmune_/346/212/225/350/265/204/344/272/272/347/231/275/347/232/256/344/271/246_v2.0.html +576 -0
- package/docs/Progmune_/351/241/271/347/233/256/345/205/250/350/247/243.html +721 -0
- package/package.json +73 -6
- package/protocols.json +1956 -50
- package/.dockerignore +0 -14
- package/.mcp.json +0 -11
- package/.progmune_allowlist +0 -50
- package/.test_report/test_report.md +0 -87
- package/Dockerfile +0 -9
- package/FAQ.md +0 -167
- package/WHITEPAPER.md +0 -540
- package/demo-project/auth.ts +0 -55
- package/demo-project/tsconfig.json +0 -8
- package/dist/acl-breakdown.js +0 -13
- package/dist/all-sessions.js +0 -11
- package/dist/antibody-stats.js +0 -11
- package/dist/branch-tree-count.js +0 -14
- package/dist/common-fixpath.js +0 -12
- package/dist/constraint-types.js +0 -12
- package/dist/exec-metrics.js +0 -11
- package/dist/failure-report.js +0 -11
- package/dist/fast-path-hits.js +0 -13
- package/dist/fingerprint-list.js +0 -15
- package/dist/gen-history-log.js +0 -13
- package/dist/heatmap-data.js +0 -11
- package/dist/recent-session.js +0 -12
- package/dist/svl-distribution.js +0 -11
- package/dist/terminal-status.js +0 -11
- package/dist/token-savings.js +0 -11
- package/dist/total-repairs.js +0 -12
- package/dist/unresolved-count.js +0 -12
- package/dist/valid-fingerprints.js +0 -13
- package/dist/verify-ledgers.js +0 -11
- package/docs/whitepaper-style.css +0 -77
- package/docs/whitepaper-v2.1.md +0 -609
- package/docs/whitepaper-v2.2.md +0 -1064
- package/docs/whitepaper-v2.2.pdf +0 -0
- package/fly.toml +0 -31
- package/public/dashboard.html +0 -119
- package/server/hub.js +0 -116
- package/test/replay-golden/sess_1780063202050_mgeld.json +0 -9
- package/test/replay-golden/sess_1780064032560_gocld.json +0 -354
- package/test/replay-golden/sess_1780064413331_s2709.json +0 -606
- package/test/replay-golden/sess_1780064792710_y3avo.json +0 -614
- package/test/replay-golden.ts +0 -84
- package/test_benchmark.js +0 -165
- package/test_comprehensive.mjs +0 -638
- package/test_concurrency.js +0 -129
- package/test_ir_robustness.js +0 -85
- package/test_semantic_contracts.js +0 -269
- package/test_ssg_stress.js +0 -156
- package/test_svl3.js +0 -58
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Progmune V10 — Knowledge Explorer
|
|
4
|
+
* ==================================
|
|
5
|
+
* 系统不再只验证已有命题,而是主动生成待验证的命题。
|
|
6
|
+
*
|
|
7
|
+
* Question 来源(从最自然的开始):
|
|
8
|
+
* 1. Graph Topology Gaps — 知识图上的孤立节点和缺失边
|
|
9
|
+
* 2. Knowledge Conflicts — CONFLICTING 状态触发追问
|
|
10
|
+
* 3. Structural Incompleteness — 状态机中的传递缺口
|
|
11
|
+
*
|
|
12
|
+
* 流程:
|
|
13
|
+
* Question → Hypothesis Generator → Candidate Claims
|
|
14
|
+
* → Proof (verify) → Counterexample (refute) → Belief (evaluate)
|
|
15
|
+
* → Retain or Discard → Knowledge Completion
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.explore = explore;
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
// ══════════════════════════════════════════════
|
|
55
|
+
// L1: QUESTION GENERATOR — From Graph Topology
|
|
56
|
+
// ══════════════════════════════════════════════
|
|
57
|
+
/**
|
|
58
|
+
* Generate questions from the Knowledge Graph's structural properties.
|
|
59
|
+
*
|
|
60
|
+
* Source 1: GRAPH_GAP
|
|
61
|
+
* For every pair of states (A, B) in a state machine,
|
|
62
|
+
* if there is no claim about their relationship (neither A⇒B nor B⇒A nor ¬(A→B)),
|
|
63
|
+
* generate: "What is the relationship between A and B?"
|
|
64
|
+
*
|
|
65
|
+
* Source 2: CONFLICT
|
|
66
|
+
* For every CONFLICTING claim, generate: "Why is this claim conflicting?
|
|
67
|
+
* Is the invariant wrong, or is the state machine incomplete?"
|
|
68
|
+
*
|
|
69
|
+
* Source 3: TRANSITIVE_HOLE
|
|
70
|
+
* If we have A⇒B and B⇒C as claims, but no claim for A⇒C,
|
|
71
|
+
* generate: "Does A require C?" (this is the transitive closure question)
|
|
72
|
+
*/
|
|
73
|
+
function generateQuestions(claims, stateMachines) {
|
|
74
|
+
const questions = [];
|
|
75
|
+
let qId = 1;
|
|
76
|
+
// ── Source 1: GRAPH_GAP ──
|
|
77
|
+
// For each state machine, find pairs of states with no defined relationship
|
|
78
|
+
for (const machine of stateMachines) {
|
|
79
|
+
const states = (machine.states || []).map((s) => s.toLowerCase());
|
|
80
|
+
const domain = machine.entity;
|
|
81
|
+
// Get all existing claims about this domain's states
|
|
82
|
+
const domainClaims = claims.filter((c) => c.domain === domain ||
|
|
83
|
+
domain.includes(c.domain) ||
|
|
84
|
+
c.domain.includes(domain));
|
|
85
|
+
// Build the set of known relationships
|
|
86
|
+
const knownRelations = new Set();
|
|
87
|
+
for (const c of domainClaims) {
|
|
88
|
+
const pred = (c.claim || c.predicate || "").toLowerCase();
|
|
89
|
+
const implies = pred.match(/(\w+)\s*⇒\s*(\w+)/);
|
|
90
|
+
const forbids = pred.match(/¬\((\w+)\s*→\s*(\w+)\)/);
|
|
91
|
+
if (implies) {
|
|
92
|
+
knownRelations.add(`${implies[1]}⇒${implies[2]}`);
|
|
93
|
+
}
|
|
94
|
+
if (forbids) {
|
|
95
|
+
knownRelations.add(`¬(${forbids[1]}→${forbids[2]})`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// For every pair of states, check if relationship is known
|
|
99
|
+
for (let i = 0; i < states.length; i++) {
|
|
100
|
+
for (let j = i + 1; j < states.length; j++) {
|
|
101
|
+
const a = states[i];
|
|
102
|
+
const b = states[j];
|
|
103
|
+
const hasAB = knownRelations.has(`${a}⇒${b}`) || knownRelations.has(`¬(${a}→${b})`);
|
|
104
|
+
const hasBA = knownRelations.has(`${b}⇒${a}`) || knownRelations.has(`¬(${b}→${a})`);
|
|
105
|
+
if (!hasAB && !hasBA && a !== b) {
|
|
106
|
+
questions.push({
|
|
107
|
+
id: `Q-${qId++}`,
|
|
108
|
+
text: `${a} 和 ${b} 之间是否存在先决关系?`,
|
|
109
|
+
source: "GRAPH_GAP",
|
|
110
|
+
domain,
|
|
111
|
+
entities: [a, b],
|
|
112
|
+
generatedAt: new Date().toISOString(),
|
|
113
|
+
priority: "MEDIUM",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// ── Source 2: CONFLICT ──
|
|
120
|
+
const conflicting = claims.filter((c) => c.status === "CONFLICTING");
|
|
121
|
+
for (const c of conflicting) {
|
|
122
|
+
questions.push({
|
|
123
|
+
id: `Q-${qId++}`,
|
|
124
|
+
text: `为什么 "${c.claim || c.predicate}" 存在证据冲突?不变量错了还是状态机有缺口?`,
|
|
125
|
+
source: "CONFLICT",
|
|
126
|
+
domain: c.domain,
|
|
127
|
+
entities: [],
|
|
128
|
+
generatedAt: new Date().toISOString(),
|
|
129
|
+
priority: "HIGH",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// ── Source 3: TRANSITIVE_HOLE ──
|
|
133
|
+
const requiresClaims = claims.filter((c) => (c.logicType === "REQUIRES" || (c.claim || c.predicate || "").includes("⇒")) &&
|
|
134
|
+
c.status !== "REFUTED" && c.status !== "CONFLICTING");
|
|
135
|
+
for (const c1 of requiresClaims) {
|
|
136
|
+
const m1 = (c1.claim || c1.predicate || "").match(/(\w+)\s*⇒\s*(\w+)/);
|
|
137
|
+
if (!m1)
|
|
138
|
+
continue;
|
|
139
|
+
const a = m1[1].toLowerCase();
|
|
140
|
+
const b = m1[2].toLowerCase();
|
|
141
|
+
for (const c2 of requiresClaims) {
|
|
142
|
+
if (c1.id === c2.id)
|
|
143
|
+
continue;
|
|
144
|
+
const m2 = (c2.claim || c2.predicate || "").match(/(\w+)\s*⇒\s*(\w+)/);
|
|
145
|
+
if (!m2)
|
|
146
|
+
continue;
|
|
147
|
+
const b2 = m2[1].toLowerCase();
|
|
148
|
+
const c = m2[2].toLowerCase();
|
|
149
|
+
// Chain: A⇒B and B⇒C → check if A⇒C exists
|
|
150
|
+
if (b === b2 && a !== c) {
|
|
151
|
+
const alreadyExists = requiresClaims.some((existing) => {
|
|
152
|
+
const m = (existing.claim || existing.predicate || "").match(/(\w+)\s*⇒\s*(\w+)/);
|
|
153
|
+
return m && m[1].toLowerCase() === a && m[2].toLowerCase() === c;
|
|
154
|
+
});
|
|
155
|
+
if (!alreadyExists) {
|
|
156
|
+
// Don't duplicate
|
|
157
|
+
const alreadyAsked = questions.some(q => q.source === "TRANSITIVE_HOLE" &&
|
|
158
|
+
q.entities.includes(a) && q.entities.includes(c));
|
|
159
|
+
if (!alreadyAsked) {
|
|
160
|
+
questions.push({
|
|
161
|
+
id: `Q-${qId++}`,
|
|
162
|
+
text: `${a} 是否要求 ${c}?(通过 ${b} 传递)`,
|
|
163
|
+
source: "TRANSITIVE_HOLE",
|
|
164
|
+
domain: c1.domain,
|
|
165
|
+
entities: [a, b, c],
|
|
166
|
+
generatedAt: new Date().toISOString(),
|
|
167
|
+
priority: "HIGH",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return questions;
|
|
175
|
+
}
|
|
176
|
+
// ══════════════════════════════════════════════
|
|
177
|
+
// L2: HYPOTHESIS GENERATOR
|
|
178
|
+
// ══════════════════════════════════════════════
|
|
179
|
+
/**
|
|
180
|
+
* For each question, generate candidate hypotheses.
|
|
181
|
+
*
|
|
182
|
+
* TRANSITIVE_HOLE: "Does A require C (via B)?"
|
|
183
|
+
* → Hypothesis: "A ⇒ C" (derived from A⇒B and B⇒C via transitivity)
|
|
184
|
+
*
|
|
185
|
+
* GRAPH_GAP: "What is the relationship between A and B?"
|
|
186
|
+
* → Hypothesis 1: "A ⇒ B"
|
|
187
|
+
* → Hypothesis 2: "B ⇒ A"
|
|
188
|
+
* → For each, check if it's plausible based on graph neighborhood
|
|
189
|
+
*
|
|
190
|
+
* CONFLICT: "Why is this conflicting?"
|
|
191
|
+
* → Hypothesis: re-state the existing claim for re-verification
|
|
192
|
+
*/
|
|
193
|
+
function generateHypotheses(questions, claims) {
|
|
194
|
+
const hypotheses = [];
|
|
195
|
+
let hId = 1;
|
|
196
|
+
// Index existing claims by their atoms for quick lookup
|
|
197
|
+
const claimIndex = new Map();
|
|
198
|
+
for (const c of claims) {
|
|
199
|
+
const pred = (c.claim || c.predicate || "").toLowerCase();
|
|
200
|
+
const m = pred.match(/(\w+)\s*⇒\s*(\w+)/);
|
|
201
|
+
if (m) {
|
|
202
|
+
const key = `${m[1]}⇒${m[2]}`;
|
|
203
|
+
if (!claimIndex.has(key))
|
|
204
|
+
claimIndex.set(key, []);
|
|
205
|
+
claimIndex.get(key).push(c);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
for (const q of questions) {
|
|
209
|
+
switch (q.source) {
|
|
210
|
+
case "TRANSITIVE_HOLE": {
|
|
211
|
+
// "Does A require C (via B)?"
|
|
212
|
+
const [a, b, c] = q.entities;
|
|
213
|
+
// Hypothesis: A ⇒ C
|
|
214
|
+
hypotheses.push({
|
|
215
|
+
id: `H-${hId++}`,
|
|
216
|
+
questionId: q.id,
|
|
217
|
+
claim: `${a} ⇒ ${c}`,
|
|
218
|
+
reasoning: `传递链: ${a}⇒${b} 且 ${b}⇒${c},因此 ${a}⇒${c} 可能成立`,
|
|
219
|
+
source: "TRANSITIVE_CHAIN",
|
|
220
|
+
confidence: 60, // Prior: transitivity is sound but premises might be noisy
|
|
221
|
+
});
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
case "GRAPH_GAP": {
|
|
225
|
+
// "What is the relationship between A and B?"
|
|
226
|
+
const [a, b] = q.entities;
|
|
227
|
+
// Hypothesis 1: A ⇒ B
|
|
228
|
+
// Check if A has other outgoing prerequisites (suggests A is "later" in lifecycle)
|
|
229
|
+
const aOutgoing = claims.filter((c) => {
|
|
230
|
+
const m = (c.claim || c.predicate || "").match(/(\w+)\s*⇒\s*(\w+)/);
|
|
231
|
+
return m && m[1].toLowerCase() === a;
|
|
232
|
+
});
|
|
233
|
+
const bOutgoing = claims.filter((c) => {
|
|
234
|
+
const m = (c.claim || c.predicate || "").match(/(\w+)\s*⇒\s*(\w+)/);
|
|
235
|
+
return m && m[1].toLowerCase() === b;
|
|
236
|
+
});
|
|
237
|
+
// Heuristic: the state with MORE outgoing prerequisites is "later" (more dependent)
|
|
238
|
+
// The later state should require the earlier state
|
|
239
|
+
if (bOutgoing.length > aOutgoing.length) {
|
|
240
|
+
hypotheses.push({
|
|
241
|
+
id: `H-${hId++}`,
|
|
242
|
+
questionId: q.id,
|
|
243
|
+
claim: `${b} ⇒ ${a}`,
|
|
244
|
+
reasoning: `${b} 有 ${bOutgoing.length} 个先决条件,${a} 有 ${aOutgoing.length} 个。${b} 更可能是后置状态。`,
|
|
245
|
+
source: "GRAPH_NEIGHBOR",
|
|
246
|
+
confidence: 40,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
else if (aOutgoing.length > bOutgoing.length) {
|
|
250
|
+
hypotheses.push({
|
|
251
|
+
id: `H-${hId++}`,
|
|
252
|
+
questionId: q.id,
|
|
253
|
+
claim: `${a} ⇒ ${b}`,
|
|
254
|
+
reasoning: `${a} 有 ${aOutgoing.length} 个先决条件,${b} 有 ${bOutgoing.length} 个。${a} 更可能是后置状态。`,
|
|
255
|
+
source: "GRAPH_NEIGHBOR",
|
|
256
|
+
confidence: 40,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// Both have similar outgoing counts — try both directions
|
|
261
|
+
hypotheses.push({
|
|
262
|
+
id: `H-${hId++}`,
|
|
263
|
+
questionId: q.id,
|
|
264
|
+
claim: `${a} ⇒ ${b}`,
|
|
265
|
+
reasoning: `图结构无法确定方向,试探性假设 ${a}⇒${b}`,
|
|
266
|
+
source: "GRAPH_NEIGHBOR",
|
|
267
|
+
confidence: 25,
|
|
268
|
+
});
|
|
269
|
+
hypotheses.push({
|
|
270
|
+
id: `H-${hId++}`,
|
|
271
|
+
questionId: q.id,
|
|
272
|
+
claim: `${b} ⇒ ${a}`,
|
|
273
|
+
reasoning: `图结构无法确定方向,试探性假设 ${b}⇒${a}`,
|
|
274
|
+
source: "GRAPH_NEIGHBOR",
|
|
275
|
+
confidence: 25,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case "CONFLICT": {
|
|
281
|
+
// Re-state the conflicting claim as a hypothesis for re-verification
|
|
282
|
+
const conflictingClaim = claims.find((c) => c.status === "CONFLICTING" && c.domain === q.domain);
|
|
283
|
+
if (conflictingClaim) {
|
|
284
|
+
hypotheses.push({
|
|
285
|
+
id: `H-${hId++}`,
|
|
286
|
+
questionId: q.id,
|
|
287
|
+
claim: conflictingClaim.claim || conflictingClaim.predicate,
|
|
288
|
+
reasoning: "重新验证冲突的不变量——检查是否有新的证据改变结论",
|
|
289
|
+
source: "DOMAIN_TEMPLATE",
|
|
290
|
+
confidence: 50,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return hypotheses;
|
|
298
|
+
}
|
|
299
|
+
// ══════════════════════════════════════════════
|
|
300
|
+
// L3: VERIFICATION — Proof + Counterexample + Belief
|
|
301
|
+
// ══════════════════════════════════════════════
|
|
302
|
+
/**
|
|
303
|
+
* Verify each hypothesis against the existing knowledge.
|
|
304
|
+
*
|
|
305
|
+
* For a hypothesis "A ⇒ C":
|
|
306
|
+
* 1. Proof: can we derive it from existing claims via transitivity?
|
|
307
|
+
* 2. Counterexample: does the state machine allow A without C?
|
|
308
|
+
* 3. Belief: what do the independent evidence sources say?
|
|
309
|
+
*/
|
|
310
|
+
function verifyHypotheses(hypotheses, claims, stateMachines) {
|
|
311
|
+
const results = [];
|
|
312
|
+
// Index valid transitions for counterexample checking
|
|
313
|
+
const machineTransitions = new Map();
|
|
314
|
+
for (const machine of stateMachines) {
|
|
315
|
+
const validSet = new Set();
|
|
316
|
+
for (const vt of machine.validTransitions || []) {
|
|
317
|
+
validSet.add(vt.toLowerCase());
|
|
318
|
+
}
|
|
319
|
+
machineTransitions.set(machine.entity, validSet);
|
|
320
|
+
}
|
|
321
|
+
for (const hyp of hypotheses) {
|
|
322
|
+
const m = hyp.claim.match(/(\w+)\s*⇒\s*(\w+)/);
|
|
323
|
+
if (!m)
|
|
324
|
+
continue;
|
|
325
|
+
const a = m[1].toLowerCase();
|
|
326
|
+
const c = m[2].toLowerCase();
|
|
327
|
+
// ── Proof check: can we derive A⇒C from existing claims? ──
|
|
328
|
+
const existingAB = claims.filter((cl) => {
|
|
329
|
+
const pred = (cl.claim || cl.predicate || "").toLowerCase();
|
|
330
|
+
const pm = pred.match(/(\w+)\s*⇒\s*(\w+)/);
|
|
331
|
+
return pm && pm[2].toLowerCase() === c;
|
|
332
|
+
});
|
|
333
|
+
const existingBC = claims.filter((cl) => {
|
|
334
|
+
const pred = (cl.claim || cl.predicate || "").toLowerCase();
|
|
335
|
+
const pm = pred.match(/(\w+)\s*⇒\s*(\w+)/);
|
|
336
|
+
return pm && pm[1].toLowerCase() === a;
|
|
337
|
+
});
|
|
338
|
+
const proofExists = existingAB.length > 0 || existingBC.length > 0;
|
|
339
|
+
const proofChain = [
|
|
340
|
+
...existingAB.map((cl) => cl.id),
|
|
341
|
+
...existingBC.map((cl) => cl.id),
|
|
342
|
+
];
|
|
343
|
+
// ── Counterexample check: does any state machine allow A without C? ──
|
|
344
|
+
let hasCounterexample = false;
|
|
345
|
+
let cePath;
|
|
346
|
+
for (const [domain, validTransitions] of machineTransitions) {
|
|
347
|
+
// Simple check: is there a direct transition to A from any state that is NOT C?
|
|
348
|
+
for (const vt of validTransitions) {
|
|
349
|
+
const [from, to] = vt.split("→");
|
|
350
|
+
if (to === a && from !== c) {
|
|
351
|
+
hasCounterexample = true;
|
|
352
|
+
cePath = [from, to];
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (hasCounterexample)
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
// ── Belief assessment (simplified) ──
|
|
360
|
+
let beliefScore;
|
|
361
|
+
let beliefLevel;
|
|
362
|
+
if (hasCounterexample) {
|
|
363
|
+
beliefScore = 15;
|
|
364
|
+
beliefLevel = "LOW";
|
|
365
|
+
}
|
|
366
|
+
else if (proofExists) {
|
|
367
|
+
beliefScore = hyp.confidence + 20;
|
|
368
|
+
beliefLevel = beliefScore >= 75 ? "HIGH" : "MEDIUM";
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
beliefScore = hyp.confidence;
|
|
372
|
+
beliefLevel = beliefScore >= 50 ? "MEDIUM" : "LOW";
|
|
373
|
+
}
|
|
374
|
+
// ── Verdict ──
|
|
375
|
+
let verdict;
|
|
376
|
+
let explanation;
|
|
377
|
+
if (hasCounterexample) {
|
|
378
|
+
verdict = "DISCARD";
|
|
379
|
+
explanation = `存在反例:${cePath?.join(" → ")} 绕过了 ${c}。假设不成立。`;
|
|
380
|
+
}
|
|
381
|
+
else if (proofExists && beliefScore >= 60) {
|
|
382
|
+
verdict = "RETAIN";
|
|
383
|
+
explanation = `可通过 ${proofChain.join(", ")} 推导。置信度 ${beliefScore}%。`;
|
|
384
|
+
}
|
|
385
|
+
else if (proofExists) {
|
|
386
|
+
verdict = "NEEDS_HUMAN";
|
|
387
|
+
explanation = `推导成立但置信度不足(${beliefScore}%)。建议人工确认。`;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
verdict = "NEEDS_HUMAN";
|
|
391
|
+
explanation = `无法从现有知识推导。需要额外证据。`;
|
|
392
|
+
}
|
|
393
|
+
results.push({
|
|
394
|
+
hypothesisId: hyp.id,
|
|
395
|
+
claim: hyp.claim,
|
|
396
|
+
proofExists,
|
|
397
|
+
proofChain,
|
|
398
|
+
hasCounterexample,
|
|
399
|
+
counterexamplePath: cePath,
|
|
400
|
+
beliefScore,
|
|
401
|
+
beliefLevel,
|
|
402
|
+
verdict,
|
|
403
|
+
explanation,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
return results;
|
|
407
|
+
}
|
|
408
|
+
// ══════════════════════════════════════════════
|
|
409
|
+
// MAIN
|
|
410
|
+
// ══════════════════════════════════════════════
|
|
411
|
+
function explore(projectPath) {
|
|
412
|
+
console.log("🔬 Progmune Knowledge Explorer — V10");
|
|
413
|
+
console.log(" Project:", projectPath);
|
|
414
|
+
const kbPath = path.join(projectPath, ".progmune_knowledge.json");
|
|
415
|
+
const statePath = path.join(projectPath, ".progmune_state_machines.json");
|
|
416
|
+
if (!fs.existsSync(kbPath)) {
|
|
417
|
+
console.log(" Run V9 first: npx ts-node src/knowledge-object.ts");
|
|
418
|
+
return { timestamp: new Date().toISOString(), questions: [], hypotheses: [], verifications: [], summary: { questionsGenerated: 0, hypothesesGenerated: 0, retained: 0, discarded: 0, needsHuman: 0 } };
|
|
419
|
+
}
|
|
420
|
+
const kb = JSON.parse(fs.readFileSync(kbPath, "utf-8"));
|
|
421
|
+
const states = fs.existsSync(statePath) ? JSON.parse(fs.readFileSync(statePath, "utf-8")) : { machines: [] };
|
|
422
|
+
const claims = kb.claims || [];
|
|
423
|
+
const machines = states.machines || [];
|
|
424
|
+
console.log(` Loaded ${claims.length} claims, ${machines.length} state machines`);
|
|
425
|
+
// ── L1: Generate Questions ──
|
|
426
|
+
console.log("\nL1 · Generating Questions...");
|
|
427
|
+
const questions = generateQuestions(claims, machines);
|
|
428
|
+
const bySource = {};
|
|
429
|
+
for (const q of questions) {
|
|
430
|
+
bySource[q.source] = (bySource[q.source] || 0) + 1;
|
|
431
|
+
}
|
|
432
|
+
console.log(` ${questions.length} questions generated`);
|
|
433
|
+
for (const [src, count] of Object.entries(bySource)) {
|
|
434
|
+
console.log(` ${src}: ${count}`);
|
|
435
|
+
}
|
|
436
|
+
// Show top questions
|
|
437
|
+
for (const q of questions.filter(q => q.priority === "HIGH").slice(0, 3)) {
|
|
438
|
+
console.log(` ❓ ${q.id}: ${q.text}`);
|
|
439
|
+
}
|
|
440
|
+
// ── L2: Generate Hypotheses ──
|
|
441
|
+
console.log("\nL2 · Generating Hypotheses...");
|
|
442
|
+
const hypotheses = generateHypotheses(questions, claims);
|
|
443
|
+
console.log(` ${hypotheses.length} hypotheses generated`);
|
|
444
|
+
// ── L3: Verify ──
|
|
445
|
+
console.log("\nL3 · Verifying Hypotheses...");
|
|
446
|
+
const verifications = verifyHypotheses(hypotheses, claims, machines);
|
|
447
|
+
const retained = verifications.filter(v => v.verdict === "RETAIN");
|
|
448
|
+
const discarded = verifications.filter(v => v.verdict === "DISCARD");
|
|
449
|
+
const needsHuman = verifications.filter(v => v.verdict === "NEEDS_HUMAN");
|
|
450
|
+
console.log(` ${retained.length} RETAINED · ${discarded.length} DISCARDED · ${needsHuman.length} NEEDS HUMAN`);
|
|
451
|
+
// Show retained
|
|
452
|
+
console.log("\n ── RETAINED (new knowledge) ──");
|
|
453
|
+
for (const v of retained.slice(0, 5)) {
|
|
454
|
+
console.log(` ✅ ${v.claim}`);
|
|
455
|
+
console.log(` ${v.explanation}`);
|
|
456
|
+
}
|
|
457
|
+
// Show discarded
|
|
458
|
+
console.log("\n ── DISCARDED (refuted) ──");
|
|
459
|
+
for (const v of discarded.slice(0, 3)) {
|
|
460
|
+
console.log(` ❌ ${v.claim}`);
|
|
461
|
+
console.log(` ${v.explanation}`);
|
|
462
|
+
}
|
|
463
|
+
// Show needs human
|
|
464
|
+
if (needsHuman.length > 0) {
|
|
465
|
+
console.log("\n ── NEEDS HUMAN REVIEW ──");
|
|
466
|
+
for (const v of needsHuman.slice(0, 3)) {
|
|
467
|
+
console.log(` ⚠️ ${v.claim}`);
|
|
468
|
+
console.log(` ${v.explanation}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const report = {
|
|
472
|
+
timestamp: new Date().toISOString(),
|
|
473
|
+
questions,
|
|
474
|
+
hypotheses,
|
|
475
|
+
verifications,
|
|
476
|
+
summary: {
|
|
477
|
+
questionsGenerated: questions.length,
|
|
478
|
+
hypothesesGenerated: hypotheses.length,
|
|
479
|
+
retained: retained.length,
|
|
480
|
+
discarded: discarded.length,
|
|
481
|
+
needsHuman: needsHuman.length,
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
return report;
|
|
485
|
+
}
|
|
486
|
+
// ══════════════════════════════════════════════
|
|
487
|
+
// CLI
|
|
488
|
+
// ══════════════════════════════════════════════
|
|
489
|
+
if (require.main === module) {
|
|
490
|
+
const targetProject = process.argv[2];
|
|
491
|
+
if (!targetProject) {
|
|
492
|
+
console.error("Usage: npx ts-node src/knowledge-explorer.ts <project-path>");
|
|
493
|
+
process.exit(1);
|
|
494
|
+
}
|
|
495
|
+
const report = explore(targetProject);
|
|
496
|
+
const outputPath = path.join(targetProject, ".progmune_exploration.json");
|
|
497
|
+
fs.writeFileSync(outputPath, JSON.stringify(report, null, 2));
|
|
498
|
+
console.log(`\n✅ Exploration report saved to: ${outputPath}`);
|
|
499
|
+
console.log("\n═══ Knowledge Explorer Summary ═══");
|
|
500
|
+
console.log(` Questions: ${report.summary.questionsGenerated}`);
|
|
501
|
+
console.log(` Hypotheses: ${report.summary.hypothesesGenerated}`);
|
|
502
|
+
console.log(` Retained: ${report.summary.retained} ← new knowledge`);
|
|
503
|
+
console.log(` Discarded: ${report.summary.discarded} ← refuted`);
|
|
504
|
+
console.log(` Needs human: ${report.summary.needsHuman} ← requires review`);
|
|
505
|
+
console.log();
|
|
506
|
+
}
|