predicate-skill 2.0.3 → 2.0.5
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/.claude-plugin/plugin.json +2 -2
- package/LICENSE +92 -201
- package/README.md +334 -56
- package/catalog/catalog.json +1 -1
- package/package.json +2 -2
- package/server.bundle.mjs +112 -32
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "predicate",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Local reasoning knowledge graph (RDF/OWL) for AI agents — 9 kg_* MCP tools + cross-platform Stop-hook turn extraction (Claude Code + Gemini CLI + OpenCode) + reasoning bridge for action data.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nordic Agents Research",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"homepage": "https://github.com/NordicAgents/predicate#readme",
|
|
10
10
|
"repository": "https://github.com/NordicAgents/predicate",
|
|
11
|
-
"license": "
|
|
11
|
+
"license": "Elastic-2.0",
|
|
12
12
|
"keywords": ["mcp", "knowledge-graph", "rdf", "owl", "sparql", "reasoning"],
|
|
13
13
|
"mcpServers": {
|
|
14
14
|
"predicate": {
|
package/LICENSE
CHANGED
|
@@ -1,202 +1,93 @@
|
|
|
1
|
+
Elastic License 2.0 (ELv2)
|
|
1
2
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
meet the following conditions:
|
|
94
|
-
|
|
95
|
-
(a) You must give any other recipients of the Work or
|
|
96
|
-
Derivative Works a copy of this License; and
|
|
97
|
-
|
|
98
|
-
(b) You must cause any modified files to carry prominent notices
|
|
99
|
-
stating that You changed the files; and
|
|
100
|
-
|
|
101
|
-
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
-
that You distribute, all copyright, patent, trademark, and
|
|
103
|
-
attribution notices from the Source form of the Work,
|
|
104
|
-
excluding those notices that do not pertain to any part of
|
|
105
|
-
the Derivative Works; and
|
|
106
|
-
|
|
107
|
-
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
-
distribution, then any Derivative Works that You distribute must
|
|
109
|
-
include a readable copy of the attribution notices contained
|
|
110
|
-
within such NOTICE file, excluding those notices that do not
|
|
111
|
-
pertain to any part of the Derivative Works, in at least one
|
|
112
|
-
of the following places: within a NOTICE text file distributed
|
|
113
|
-
as part of the Derivative Works; within the Source form or
|
|
114
|
-
documentation, if provided along with the Derivative Works; or,
|
|
115
|
-
within a display generated by the Derivative Works, if and
|
|
116
|
-
wherever such third-party notices normally appear. The contents
|
|
117
|
-
of the NOTICE file are for informational purposes only and
|
|
118
|
-
do not modify the License. You may add Your own attribution
|
|
119
|
-
notices within Derivative Works that You distribute, alongside
|
|
120
|
-
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
-
that such additional attribution notices cannot be construed
|
|
122
|
-
as modifying the License.
|
|
123
|
-
|
|
124
|
-
You may add Your own copyright statement to Your modifications and
|
|
125
|
-
may provide additional or different license terms and conditions
|
|
126
|
-
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
-
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
-
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
-
the conditions stated in this License.
|
|
130
|
-
|
|
131
|
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
-
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
-
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
-
this License, without any additional terms or conditions.
|
|
135
|
-
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
-
the terms of any separate license agreement you may have executed
|
|
137
|
-
with Licensor regarding such Contributions.
|
|
138
|
-
|
|
139
|
-
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
-
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
-
except as required for reasonable and customary use in describing the
|
|
142
|
-
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
-
|
|
144
|
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
-
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
-
implied, including, without limitation, any warranties or conditions
|
|
149
|
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
-
appropriateness of using or redistributing the Work and assume any
|
|
152
|
-
risks associated with Your exercise of permissions under this License.
|
|
153
|
-
|
|
154
|
-
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
-
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
-
unless required by applicable law (such as deliberate and grossly
|
|
157
|
-
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
-
liable to You for damages, including any direct, indirect, special,
|
|
159
|
-
incidental, or consequential damages of any character arising as a
|
|
160
|
-
result of this License or out of the use or inability to use the
|
|
161
|
-
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
-
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
-
other commercial damages or losses), even if such Contributor
|
|
164
|
-
has been advised of the possibility of such damages.
|
|
165
|
-
|
|
166
|
-
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
-
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
-
or other liability obligations and/or rights consistent with this
|
|
170
|
-
License. However, in accepting such obligations, You may act only
|
|
171
|
-
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
-
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
-
defend, and hold each Contributor harmless for any liability
|
|
174
|
-
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
-
of your accepting any such warranty or additional liability.
|
|
176
|
-
|
|
177
|
-
END OF TERMS AND CONDITIONS
|
|
178
|
-
|
|
179
|
-
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
-
|
|
181
|
-
To apply the Apache License to your work, attach the following
|
|
182
|
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
-
replaced with your own identifying information. (Don't include
|
|
184
|
-
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
-
comment syntax for the file format. We also recommend that a
|
|
186
|
-
file or class name and description of purpose be included on the
|
|
187
|
-
same "printed page" as the copyright notice for easier
|
|
188
|
-
identification within third-party archives.
|
|
189
|
-
|
|
190
|
-
Copyright [yyyy] [name of copyright owner]
|
|
191
|
-
|
|
192
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
-
you may not use this file except in compliance with the License.
|
|
194
|
-
You may obtain a copy of the License at
|
|
195
|
-
|
|
196
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
-
|
|
198
|
-
Unless required by applicable law or agreed to in writing, software
|
|
199
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
-
See the License for the specific language governing permissions and
|
|
202
|
-
limitations under the License.
|
|
3
|
+
URL: https://www.elastic.co/licensing/elastic-license
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
8
|
+
|
|
9
|
+
## Copyright License
|
|
10
|
+
|
|
11
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
12
|
+
non-sublicensable, non-transferable license to use, copy, distribute, make
|
|
13
|
+
available, and prepare derivative works of the software, in each case subject
|
|
14
|
+
to the limitations and conditions below.
|
|
15
|
+
|
|
16
|
+
## Limitations
|
|
17
|
+
|
|
18
|
+
You may not provide the software to third parties as a hosted or managed
|
|
19
|
+
service, where the service provides users with access to any substantial set of
|
|
20
|
+
the features or functionality of the software.
|
|
21
|
+
|
|
22
|
+
You may not move, change, disable, or circumvent the license key functionality
|
|
23
|
+
in the software, and you may not remove or obscure any functionality in the
|
|
24
|
+
software that is protected by the license key.
|
|
25
|
+
|
|
26
|
+
You may not alter, remove, or obscure any licensing, copyright, or other notices
|
|
27
|
+
of the licensor in the software. Any use of the licensor's trademarks is subject
|
|
28
|
+
to applicable law.
|
|
29
|
+
|
|
30
|
+
## Patents
|
|
31
|
+
|
|
32
|
+
The licensor grants you a license, under any patent claims the licensor can
|
|
33
|
+
license, or becomes able to license, to make, have made, use, sell, offer for
|
|
34
|
+
sale, import and have imported the software, in each case subject to the
|
|
35
|
+
limitations and conditions in this license. This license does not cover any
|
|
36
|
+
patent claims that you cause to be infringed by modifications or additions to
|
|
37
|
+
the software. If you or your company make any written claim that the software
|
|
38
|
+
infringes or contributes to infringement of any patent, your patent license for
|
|
39
|
+
the software granted under these terms ends immediately. If your company makes
|
|
40
|
+
such a claim, your patent license ends immediately for work on behalf of your
|
|
41
|
+
company.
|
|
42
|
+
|
|
43
|
+
## Notices
|
|
44
|
+
|
|
45
|
+
You must ensure that anyone who gets a copy of any part of the software from you
|
|
46
|
+
also gets a copy of these terms.
|
|
47
|
+
|
|
48
|
+
If you modify the software, you must include in any modified copies of the
|
|
49
|
+
software prominent notices stating that you have modified the software.
|
|
50
|
+
|
|
51
|
+
## No Other Rights
|
|
52
|
+
|
|
53
|
+
These terms do not imply any licenses other than those expressly granted in
|
|
54
|
+
these terms.
|
|
55
|
+
|
|
56
|
+
## Termination
|
|
57
|
+
|
|
58
|
+
If you use the software in violation of these terms, such use is not licensed,
|
|
59
|
+
and your licenses will automatically terminate. If the licensor provides you
|
|
60
|
+
with a notice of your violation, and you cease all violation of this license no
|
|
61
|
+
later than 30 days after you receive that notice, your licenses will be
|
|
62
|
+
reinstated retroactively. However, if you violate these terms after such
|
|
63
|
+
reinstatement, any additional violation of these terms will cause your licenses
|
|
64
|
+
to terminate automatically and permanently.
|
|
65
|
+
|
|
66
|
+
## No Liability
|
|
67
|
+
|
|
68
|
+
*As far as the law allows, the software comes as is, without any warranty or
|
|
69
|
+
condition, and the licensor will not be liable to you for any damages arising
|
|
70
|
+
out of these terms or the use or nature of the software, under any kind of
|
|
71
|
+
legal claim.*
|
|
72
|
+
|
|
73
|
+
## Definitions
|
|
74
|
+
|
|
75
|
+
The **licensor** is the entity offering these terms, and the **software** is the
|
|
76
|
+
software the licensor makes available under these terms, including any portion
|
|
77
|
+
of it.
|
|
78
|
+
|
|
79
|
+
**you** refers to the individual or entity agreeing to these terms.
|
|
80
|
+
|
|
81
|
+
**your company** is any legal entity, sole proprietorship, or other kind of
|
|
82
|
+
organization that you work for, plus all organizations that have control over,
|
|
83
|
+
are under the control of, or are under common control with that organization.
|
|
84
|
+
**control** means ownership of substantially all the assets of an entity, or
|
|
85
|
+
the power to direct its management and policies by vote, contract, or
|
|
86
|
+
otherwise. Control can be direct or indirect.
|
|
87
|
+
|
|
88
|
+
**your licenses** are all the licenses granted to you for the software under
|
|
89
|
+
these terms.
|
|
90
|
+
|
|
91
|
+
**use** means anything you do with the software requiring one of your licenses.
|
|
92
|
+
|
|
93
|
+
**trademark** means trademarks, service marks, and similar rights.
|
package/README.md
CHANGED
|
@@ -1,97 +1,375 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Predicate
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Claude Code plugin (SKILL.md + hooks + slash commands) + per-platform
|
|
5
|
-
hook adapters for Cursor, Gemini CLI, VS Code Copilot, OpenCode, and
|
|
6
|
-
Codex CLI. This is the install target for both the Claude Code
|
|
7
|
-
marketplace and the npm path.
|
|
3
|
+
**Reasoning memory for AI agents — a knowledge graph that compounds with use.**
|
|
8
4
|
|
|
9
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/predicate-skill)
|
|
6
|
+
[](https://github.com/NordicAgents/predicate)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://github.com/NordicAgents/predicate/stargazers)
|
|
9
|
+
[](https://github.com/NordicAgents/predicate/commits)
|
|
10
|
+
|
|
11
|
+
## The Problem
|
|
12
|
+
|
|
13
|
+
An AI coding or research agent loses most of its value to two failures: it
|
|
14
|
+
forgets across sessions, and it can only answer single-hop lookups. Ask
|
|
15
|
+
*"why did login break?"* and a RAG system returns documents containing the
|
|
16
|
+
word *login*. It cannot traverse
|
|
17
|
+
`auth.ts → validateToken → jwt.verify → JWT_SECRET → .env.production`,
|
|
18
|
+
cannot tell you the blast radius of a rename, and cannot say which of two
|
|
19
|
+
documents contradicts the other. The "agent memory" category mostly stores
|
|
20
|
+
text or vectors with light graph structure on top — it does not separate
|
|
21
|
+
schema from data, does not run a reasoner, does not track provenance per
|
|
22
|
+
fact, and grows without bound until the operator cleans up by hand.
|
|
23
|
+
|
|
24
|
+
### How Predicate solves it
|
|
25
|
+
|
|
26
|
+
Predicate is an MCP skill that gives the agent a real reasoning graph,
|
|
27
|
+
not a search index.
|
|
28
|
+
|
|
29
|
+
1. **RDF/OWL, not retrieval.** Facts are stored as triples in Apache Jena
|
|
30
|
+
Fuseki. A curated OWL 2 RL ruleset materializes entailments through
|
|
31
|
+
SPARQL `CONSTRUCT` rules; SHACL covers closed-world validation. The
|
|
32
|
+
model formulates SPARQL against a freshly read schema — pre-baked
|
|
33
|
+
queries are forbidden — and reads logically entailed answers, never
|
|
34
|
+
hand-derived ones.
|
|
35
|
+
2. **Provenance per triple.** Every fact carries source, time, confidence,
|
|
36
|
+
and extraction method via RDF-star. Low-confidence triples stay visible
|
|
37
|
+
to queries but are excluded from the inference closure so they cannot
|
|
38
|
+
poison entailment. `kg_explain` returns the backward-chained derivation
|
|
39
|
+
for any claim with citations.
|
|
40
|
+
3. **Schema as code, with a use-gated promotion loop.** The TBox lives in
|
|
41
|
+
git as Turtle. The agent never edits it directly — it proposes deltas
|
|
42
|
+
to `kg:tbox-staging`. The reasoner validates each proposal; only after
|
|
43
|
+
N successful queries inside a TTL is a delta promoted into `kg:tbox`.
|
|
44
|
+
Unused proposals expire quietly. The graph cannot thrash because the
|
|
45
|
+
gate, not the goal source, is the safety mechanism.
|
|
46
|
+
4. **Goal-conditioned growth.** Concepts enter because a goal needed
|
|
47
|
+
them, not because a document mentioned them. `kg_research_goal`
|
|
48
|
+
decomposes a goal, detects schema or data gaps, and runs research only
|
|
49
|
+
where the existing graph cannot answer. Periodic generalization and a
|
|
50
|
+
reaper sweep keep size bounded.
|
|
51
|
+
5. **Cross-session continuity.** A Stop hook on each platform extracts
|
|
52
|
+
typed triples from the turn — files modified, commands that succeeded
|
|
53
|
+
or failed, decisions reached — and asserts them into `kg:abox`. The
|
|
54
|
+
reasoner derives `Hotspot`, `FlakyCommand`, and `ActiveFile` from
|
|
55
|
+
action data so `kg_ask` can answer "what is unstable here?" without
|
|
56
|
+
re-reading the repo next session.
|
|
10
57
|
|
|
11
58
|
## Install
|
|
12
59
|
|
|
13
60
|
Prerequisites everywhere: **Docker** (for Fuseki) and **Node 20+**.
|
|
14
61
|
|
|
15
|
-
|
|
62
|
+
<details open>
|
|
63
|
+
<summary><strong>Claude Code</strong> — plugin marketplace, fully automatic</summary>
|
|
16
64
|
|
|
17
|
-
|
|
65
|
+
**Install:**
|
|
66
|
+
|
|
67
|
+
```bash
|
|
18
68
|
/plugin marketplace add NordicAgents/predicate
|
|
19
69
|
/plugin install predicate@predicate
|
|
20
70
|
```
|
|
21
71
|
|
|
22
|
-
|
|
72
|
+
Restart Claude Code (or `/reload-plugins`). Then start the graph:
|
|
23
73
|
|
|
24
74
|
```bash
|
|
25
|
-
npm install -g predicate-skill
|
|
26
75
|
predicate up
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Verify:**
|
|
79
|
+
|
|
80
|
+
```bash
|
|
27
81
|
predicate doctor
|
|
28
82
|
```
|
|
29
83
|
|
|
30
|
-
|
|
84
|
+
All checks should show OK. The doctor validates Docker, Fuseki, the named
|
|
85
|
+
graphs, the reasoner, and plugin registration.
|
|
86
|
+
|
|
87
|
+
**Slash commands:**
|
|
88
|
+
|
|
89
|
+
| Command | What it does |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `/predicate:up` | Start Fuseki and bootstrap the 9 named graphs. |
|
|
92
|
+
| `/predicate:down` | Stop Fuseki, keep the volume. |
|
|
93
|
+
| `/predicate:doctor` | Health check across Docker, Fuseki, TBox, tools. |
|
|
94
|
+
| `/predicate:stats` | Triples, ABox, inferred, TBox counts; inferred ratio; unused-concept ratio. |
|
|
95
|
+
| `/predicate:ask <question>` | Free-form question routed through `kg_ask`. |
|
|
96
|
+
|
|
97
|
+
**Routing:** Automatic. The SessionStart hook injects a one-line KG
|
|
98
|
+
status banner. PreToolUse, PostToolUse, and Stop hooks capture tool
|
|
99
|
+
calls and extract typed session triples — no file is written to your
|
|
100
|
+
project.
|
|
101
|
+
|
|
102
|
+
<details>
|
|
103
|
+
<summary>Alternative — MCP-only install (no hooks or slash commands)</summary>
|
|
31
104
|
|
|
32
105
|
```bash
|
|
33
106
|
claude mcp add predicate -- npx -y predicate-skill
|
|
34
107
|
```
|
|
35
108
|
|
|
36
|
-
|
|
109
|
+
This gives you all `kg_*` tools without lifecycle hooks. Good for a quick
|
|
110
|
+
trial before installing the full plugin.
|
|
37
111
|
|
|
38
|
-
|
|
112
|
+
</details>
|
|
39
113
|
|
|
40
|
-
|
|
41
|
-
|---|---|---|
|
|
42
|
-
| Claude Code | `hooks/` (root) | SessionStart, PreToolUse, PostToolUse, Stop |
|
|
43
|
-
| Gemini CLI | `hooks/gemini-cli/` | sessionStart, preCompress, stop |
|
|
44
|
-
| Cursor | `hooks/cursor/` | manual / cron only (no native events) |
|
|
45
|
-
| VS Code Copilot | `hooks/vscode-copilot/` | manual / VS Code tasks |
|
|
46
|
-
| OpenCode | `hooks/opencode/` | session.started, session.compacted, session.stopped |
|
|
47
|
-
| Codex CLI | `hooks/codex-cli/` | manual / shell alias / cron |
|
|
114
|
+
</details>
|
|
48
115
|
|
|
49
|
-
|
|
116
|
+
<details>
|
|
117
|
+
<summary><strong>Any MCP-capable client</strong> — npm install</summary>
|
|
50
118
|
|
|
119
|
+
```bash
|
|
120
|
+
npm install -g predicate-skill
|
|
121
|
+
predicate up
|
|
122
|
+
predicate doctor
|
|
51
123
|
```
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
predicate
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
124
|
+
|
|
125
|
+
The bundled MCP server lives at the package's `bin` entry. Point any
|
|
126
|
+
MCP-over-stdio client at `predicate-skill` (or
|
|
127
|
+
`node /path/to/predicate-skill/server.bundle.mjs`) with env
|
|
128
|
+
`FUSEKI_URL=http://localhost:3030` and `PREDICATE_DATASET=predicate`.
|
|
129
|
+
|
|
130
|
+
</details>
|
|
131
|
+
|
|
132
|
+
<details>
|
|
133
|
+
<summary><strong>Cursor</strong> — MCP + maintenance scripts</summary>
|
|
134
|
+
|
|
135
|
+
**Install:**
|
|
136
|
+
|
|
137
|
+
1. Install context-mode globally and start Fuseki:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm install -g predicate-skill
|
|
141
|
+
predicate up
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
2. Copy the MCP config template to `~/.cursor/mcp.json` and replace
|
|
145
|
+
`__PLUGIN_DIR__` with the absolute path to the installed
|
|
146
|
+
`predicate-skill` directory:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
cp $(npm root -g)/predicate-skill/hooks/cursor/mcp.json.template ~/.cursor/mcp.json
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
3. Reload MCP servers (Cmd-Shift-P → "Reload MCP servers").
|
|
153
|
+
|
|
154
|
+
**Verify:** Open Cursor Settings → MCP and confirm "predicate" is
|
|
155
|
+
connected. In agent chat, type `predicate stats`.
|
|
156
|
+
|
|
157
|
+
**Routing:** Cursor has no native SessionStart / Stop events. The
|
|
158
|
+
SessionStart, PreCompact, and Stop maintenance scripts under
|
|
159
|
+
`hooks/cursor/` can be wired via cron or a shell alias — see
|
|
160
|
+
`hooks/cursor/README.md`.
|
|
161
|
+
|
|
162
|
+
</details>
|
|
163
|
+
|
|
164
|
+
<details>
|
|
165
|
+
<summary><strong>Gemini CLI</strong> — MCP + sessionStart + preCompress + stop hooks</summary>
|
|
166
|
+
|
|
167
|
+
**Install:**
|
|
168
|
+
|
|
169
|
+
1. Install and start Fuseki:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npm install -g predicate-skill
|
|
173
|
+
predicate up
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
2. Merge the template into `~/.gemini/settings.json`, replacing
|
|
177
|
+
`__PLUGIN_DIR__` with the absolute path to the installed
|
|
178
|
+
`predicate-skill` directory:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
cat $(npm root -g)/predicate-skill/hooks/gemini-cli/settings.json.template
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
3. Restart Gemini CLI.
|
|
185
|
+
|
|
186
|
+
**Verify:** `/mcp list` should show `predicate: ... Connected`.
|
|
187
|
+
|
|
188
|
+
**Routing:** Automatic via the three hook events. The Stop hook pipes
|
|
189
|
+
the transcript through `predicate extract --from-stdin --platform gemini`
|
|
190
|
+
so session triples land in `kg:abox` after every turn.
|
|
191
|
+
|
|
192
|
+
</details>
|
|
193
|
+
|
|
194
|
+
<details>
|
|
195
|
+
<summary><strong>VS Code Copilot</strong> — MCP via settings.json</summary>
|
|
196
|
+
|
|
197
|
+
**Install:**
|
|
198
|
+
|
|
199
|
+
1. Install and start Fuseki:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npm install -g predicate-skill
|
|
203
|
+
predicate up
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
2. Merge `hooks/vscode-copilot/settings.json.template` into your VS Code
|
|
207
|
+
`settings.json`, replacing `__PLUGIN_DIR__`. Restart VS Code.
|
|
208
|
+
|
|
209
|
+
**Verify:** Open Copilot Chat and type `predicate stats`.
|
|
210
|
+
|
|
211
|
+
**Routing:** VS Code Copilot does not yet expose SessionStart / PreCompact
|
|
212
|
+
/ Stop events. See `hooks/vscode-copilot/README.md` for manual and
|
|
213
|
+
VS Code-task wiring of the maintenance scripts.
|
|
214
|
+
|
|
215
|
+
</details>
|
|
216
|
+
|
|
217
|
+
<details>
|
|
218
|
+
<summary><strong>OpenCode</strong> — MCP + session.started + session.compacted + session.stopped hooks</summary>
|
|
219
|
+
|
|
220
|
+
**Install:**
|
|
221
|
+
|
|
222
|
+
1. Install and start Fuseki:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
npm install -g predicate-skill
|
|
226
|
+
predicate up
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
2. Merge `hooks/opencode/opencode.json.template` into
|
|
230
|
+
`~/.config/opencode/opencode.json`, replacing `__PLUGIN_DIR__`.
|
|
231
|
+
|
|
232
|
+
3. Restart OpenCode.
|
|
233
|
+
|
|
234
|
+
**Verify:** In the OpenCode session, type `predicate stats`.
|
|
235
|
+
|
|
236
|
+
**Routing:** Automatic via the three lifecycle events. The
|
|
237
|
+
session.stopped hook pipes the transcript through
|
|
238
|
+
`predicate extract --from-stdin --platform opencode`.
|
|
239
|
+
|
|
240
|
+
</details>
|
|
241
|
+
|
|
242
|
+
<details>
|
|
243
|
+
<summary><strong>Codex CLI</strong> — MCP via config.toml</summary>
|
|
244
|
+
|
|
245
|
+
**Install:**
|
|
246
|
+
|
|
247
|
+
1. Install and start Fuseki:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
npm install -g predicate-skill
|
|
251
|
+
predicate up
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
2. Merge `hooks/codex-cli/config.toml.template` into
|
|
255
|
+
`~/.codex/config.toml`, replacing `__PLUGIN_DIR__`.
|
|
256
|
+
|
|
257
|
+
**Verify:** Launch `codex` and type `predicate stats`.
|
|
258
|
+
|
|
259
|
+
**Routing:** Codex CLI has no lifecycle events yet. See
|
|
260
|
+
`hooks/codex-cli/README.md` for shell-alias wiring of the maintenance
|
|
261
|
+
scripts.
|
|
262
|
+
|
|
263
|
+
</details>
|
|
264
|
+
|
|
265
|
+
<details>
|
|
266
|
+
<summary><strong>Continue.dev</strong> — MCP via config.yaml</summary>
|
|
267
|
+
|
|
268
|
+
In `~/.continue/config.yaml`:
|
|
269
|
+
|
|
270
|
+
```yaml
|
|
271
|
+
mcpServers:
|
|
272
|
+
- name: predicate
|
|
273
|
+
command: predicate-skill
|
|
274
|
+
env:
|
|
275
|
+
FUSEKI_URL: http://localhost:3030
|
|
276
|
+
PREDICATE_DATASET: predicate
|
|
63
277
|
```
|
|
64
278
|
|
|
279
|
+
Then `predicate up` and restart Continue.
|
|
280
|
+
|
|
281
|
+
</details>
|
|
282
|
+
|
|
283
|
+
## Bootstrap modes
|
|
284
|
+
|
|
285
|
+
On first `predicate up`, choose how to seed the schema:
|
|
286
|
+
|
|
287
|
+
- **Community ontology** — install a bundled vocabulary (`top`,
|
|
288
|
+
`codebase`, `foaf`, `schema-org-lite`, `fhir-core`) from the catalog
|
|
289
|
+
in `packages/predicate-ontology/catalog/`.
|
|
290
|
+
- **Bring your own** — upload a Turtle file as the initial TBox.
|
|
291
|
+
- **Empty** — start with no schema; let the agent grow vocabulary through
|
|
292
|
+
the propose → validate → 3-uses-in-7-days promotion gate.
|
|
293
|
+
|
|
294
|
+
Schema-learning is toggleable at runtime via `kg_config_set` /
|
|
295
|
+
`kg_config_get`.
|
|
296
|
+
|
|
65
297
|
## MCP tools
|
|
66
298
|
|
|
67
|
-
The bundled server exposes 11 tools over stdio:
|
|
68
|
-
`kg_ask`, `kg_assert`, `kg_explain`, `kg_propose_schema`,
|
|
69
|
-
`kg_research_goal`, `kg_stats`, `kg_maintain`, `kg_capture`,
|
|
70
|
-
`kg_config_get`, `kg_config_set`.
|
|
299
|
+
The bundled server exposes 11 tools over stdio:
|
|
71
300
|
|
|
72
|
-
|
|
301
|
+
| Tool | What it does |
|
|
302
|
+
|---|---|
|
|
303
|
+
| `kg_explore_schema` | Returns the TBox slice for a concept so the model uses real predicates. |
|
|
304
|
+
| `kg_ask` | Executes a caller-drafted SPARQL query against asserted + inferred graphs. Logs to `kg:usage`, truncates results, supports `includeRemote: true` for peer-federated queries. |
|
|
305
|
+
| `kg_assert` | Writes a triple to `kg:abox` with RDF-star provenance. Rejects undeclared predicates. |
|
|
306
|
+
| `kg_explain` | Returns the backward-chained derivation for a claim, with cited provenance. |
|
|
307
|
+
| `kg_propose_schema` | Stages a `SchemaDelta` proposal in `kg:tbox-staging`. |
|
|
308
|
+
| `kg_research_goal` | Decompose a goal → gap-detect → optionally execute research → return a plan. |
|
|
309
|
+
| `kg_stats` | Triples, ABox, inferred, TBox counts; inferred ratio; unused-concept ratio. |
|
|
310
|
+
| `kg_maintain` | Runs reaper, generalizer, and promotion sweeper, then re-materializes inferred. |
|
|
311
|
+
| `kg_capture` | Records a tool invocation (toolName, input, output, sessionId, phase) into `kg:usage`. Used by PreToolUse / PostToolUse hooks. |
|
|
312
|
+
| `kg_config_get` / `kg_config_set` | Read or update runtime config (e.g. schema-learning toggle). |
|
|
73
313
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
314
|
+
## CLI
|
|
315
|
+
|
|
316
|
+
```
|
|
317
|
+
predicate up # docker compose up + bootstrap the 9 named graphs
|
|
318
|
+
predicate init # initialize kg:tbox (community / upload / empty)
|
|
319
|
+
predicate down # stop Fuseki, keep the volume
|
|
320
|
+
predicate doctor # health checks (docker, fuseki, tbox, tools)
|
|
321
|
+
predicate stats # current kg_stats output
|
|
322
|
+
predicate sessionstart # one-line KG status banner (used by hook scripts)
|
|
323
|
+
predicate maintain # reaper + generalizer + promotion sweeper
|
|
324
|
+
predicate capture # record a tool call in kg:usage (opt-in: PREDICATE_RAW_CAPTURE=1)
|
|
325
|
+
predicate extract # read a Stop-hook payload and assert typed triples to kg:abox
|
|
326
|
+
predicate sessions # list recent extracted sessions (modifiedFiles / ok / fail)
|
|
327
|
+
predicate captures # list raw kg:usage ToolCall captures
|
|
328
|
+
predicate recall <query> # substring search over session history (files + commands)
|
|
329
|
+
predicate dashboard # serve a localhost web view of session history + reasoning output
|
|
330
|
+
|
|
331
|
+
predicate peer add <name> <sparql-endpoint> # register a teammate's Fuseki
|
|
332
|
+
predicate peer list | peer remove
|
|
333
|
+
predicate export-sessions [--since DATE] [--user NAME]
|
|
334
|
+
predicate import-sessions <file.trig>
|
|
335
|
+
|
|
336
|
+
predicate ld init # register DBpedia + Wikidata as external LD peers
|
|
337
|
+
predicate ld list
|
|
338
|
+
predicate ld ask <query> # one-shot SPARQL across all registered LD endpoints
|
|
339
|
+
|
|
340
|
+
predicate --version
|
|
341
|
+
predicate --help
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Environment
|
|
345
|
+
|
|
346
|
+
| Var | Default | What it controls |
|
|
347
|
+
|---|---|---|
|
|
348
|
+
| `FUSEKI_URL` | `http://localhost:3030` | Where the MCP server reaches Fuseki. |
|
|
349
|
+
| `PREDICATE_DATASET` | `predicate` | Fuseki dataset name. |
|
|
350
|
+
| `PREDICATE_CAPTURE_SKIP` | *(empty)* | Comma-separated tool names suppressed by `kg_capture`. |
|
|
351
|
+
| `PREDICATE_CAPTURE_TRUNCATE` | `500` | Max chars per captured input/output field. |
|
|
352
|
+
| `PREDICATE_RAW_CAPTURE` | unset | When `1`, raw PreToolUse/PostToolUse captures are persisted to `kg:usage`. |
|
|
353
|
+
| `ANTHROPIC_API_KEY` | unset | Enables the LLM-augmented decomposer fallback in `kg_research_goal`. |
|
|
78
354
|
|
|
79
355
|
## What's in this directory
|
|
80
356
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- `
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
357
|
+
| Path | Purpose |
|
|
358
|
+
|---|---|
|
|
359
|
+
| `.claude-plugin/plugin.json` | MCP + skills + hooks registration for the Claude Code marketplace. |
|
|
360
|
+
| `server.bundle.mjs` | Bundled MCP server — runs without `node_modules` at runtime. |
|
|
361
|
+
| `cli.bundle.mjs` | Bundled `predicate` CLI, surfaced via this package's `bin` entry. |
|
|
362
|
+
| `skills/predicate/SKILL.md` | Host-agent contract: triggers, workflow, HARD-GATE anti-patterns, worked examples. |
|
|
363
|
+
| `skills/predicate-doctor/`, `skills/predicate-stats/` | Operator skills. |
|
|
364
|
+
| `commands/{up,down,doctor,stats,ask}.md` | Slash-command definitions for `/predicate:*`. |
|
|
365
|
+
| `hooks/hooks.json` | Claude Code hook registration (SessionStart, PreToolUse, PostToolUse, Stop). |
|
|
366
|
+
| `hooks/{session-start,pre-tool-use,post-tool-use,stop}.sh` | Claude Code lifecycle hooks — each delegates to a `predicate` CLI subcommand. |
|
|
367
|
+
| `hooks/{cursor,gemini-cli,vscode-copilot,opencode,codex-cli}/` | Per-platform hook scripts + config templates + per-platform README. |
|
|
368
|
+
| `compose/docker-compose.yml`, `compose/fuseki/config.ttl` | Fuseki + TDB2 config launched by `predicate up`. |
|
|
91
369
|
|
|
92
370
|
## Rebuilding the bundles
|
|
93
371
|
|
|
94
|
-
|
|
372
|
+
The bundles are committed so the marketplace install path works without
|
|
95
373
|
`pnpm install`. To rebuild after a source change:
|
|
96
374
|
|
|
97
375
|
```bash
|
|
@@ -110,9 +388,9 @@ The full workspace test suite runs against a live Fuseki:
|
|
|
110
388
|
|
|
111
389
|
```bash
|
|
112
390
|
predicate up
|
|
113
|
-
pnpm test
|
|
391
|
+
pnpm test
|
|
114
392
|
```
|
|
115
393
|
|
|
116
394
|
## License
|
|
117
395
|
|
|
118
|
-
|
|
396
|
+
Elastic License 2.0 (ELv2) — source-available. See [`LICENSE`](LICENSE).
|
package/catalog/catalog.json
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "predicate-skill",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "Local reasoning knowledge graph (RDF/OWL) for AI agents — Claude Code plugin + MCP server + predicate CLI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nordic Agents Research",
|
|
7
7
|
"email": "midhunxavier@outlook.com"
|
|
8
8
|
},
|
|
9
|
-
"license": "
|
|
9
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
10
10
|
"homepage": "https://github.com/NordicAgents/predicate#readme",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
package/server.bundle.mjs
CHANGED
|
@@ -46295,8 +46295,37 @@ Anthropic.Beta = Beta;
|
|
|
46295
46295
|
var { HUMAN_PROMPT, AI_PROMPT } = Anthropic;
|
|
46296
46296
|
var sdk_default = Anthropic;
|
|
46297
46297
|
|
|
46298
|
+
// ../predicate-agent/src/completion-provider.ts
|
|
46299
|
+
var ANTHROPIC_MODEL = "claude-haiku-4-5-20251001";
|
|
46300
|
+
var AnthropicSdkProvider = class {
|
|
46301
|
+
kind = "anthropic-sdk";
|
|
46302
|
+
isAvailable() {
|
|
46303
|
+
return Boolean(process.env["ANTHROPIC_API_KEY"]);
|
|
46304
|
+
}
|
|
46305
|
+
async complete(req) {
|
|
46306
|
+
const client = new sdk_default();
|
|
46307
|
+
const response = await client.messages.create({
|
|
46308
|
+
model: ANTHROPIC_MODEL,
|
|
46309
|
+
max_tokens: req.maxTokens,
|
|
46310
|
+
system: [
|
|
46311
|
+
{ type: "text", text: req.systemPrompt },
|
|
46312
|
+
{
|
|
46313
|
+
type: "text",
|
|
46314
|
+
text: `<tbox-slice>
|
|
46315
|
+
${req.tboxSlice}
|
|
46316
|
+
</tbox-slice>`,
|
|
46317
|
+
cache_control: { type: "ephemeral" }
|
|
46318
|
+
}
|
|
46319
|
+
],
|
|
46320
|
+
messages: [{ role: "user", content: `Question: ${req.question}
|
|
46321
|
+
|
|
46322
|
+
Return the JSON.` }]
|
|
46323
|
+
});
|
|
46324
|
+
return response.content.filter((b2) => b2.type === "text").map((b2) => b2.text).join("\n");
|
|
46325
|
+
}
|
|
46326
|
+
};
|
|
46327
|
+
|
|
46298
46328
|
// ../predicate-agent/src/semantic-decomposer.ts
|
|
46299
|
-
var MODEL = "claude-haiku-4-5-20251001";
|
|
46300
46329
|
var VALID_INTENTS = [
|
|
46301
46330
|
"why-broken",
|
|
46302
46331
|
"find-callers",
|
|
@@ -46335,33 +46364,31 @@ Output strict JSON, no prose:
|
|
|
46335
46364
|
}`;
|
|
46336
46365
|
var SemanticDecomposer = class {
|
|
46337
46366
|
deterministic = new Decomposer();
|
|
46338
|
-
|
|
46367
|
+
providers;
|
|
46368
|
+
fallbackOnEmpty;
|
|
46369
|
+
/** Set to the provider actually used on the last decompose() call (for telemetry). */
|
|
46370
|
+
lastProviderUsed = null;
|
|
46339
46371
|
constructor(options = {}) {
|
|
46340
|
-
this.
|
|
46372
|
+
this.providers = options.providers ?? [new AnthropicSdkProvider()];
|
|
46373
|
+
this.fallbackOnEmpty = options.fallbackOnEmpty ?? true;
|
|
46341
46374
|
}
|
|
46342
46375
|
async decompose(question, tboxSlice = "") {
|
|
46376
|
+
this.lastProviderUsed = null;
|
|
46343
46377
|
const deterministicResult = this.deterministic.decompose(question);
|
|
46344
46378
|
const allUnknown = deterministicResult.every((sq) => sq.intent.kind === "unknown");
|
|
46345
46379
|
if (!allUnknown) return deterministicResult;
|
|
46346
|
-
|
|
46347
|
-
|
|
46380
|
+
const provider = this.providers.find((p2) => p2.isAvailable());
|
|
46381
|
+
if (!provider) {
|
|
46382
|
+
return this.fallbackOnEmpty ? deterministicResult : [];
|
|
46348
46383
|
}
|
|
46349
46384
|
try {
|
|
46350
|
-
const
|
|
46351
|
-
|
|
46352
|
-
|
|
46353
|
-
|
|
46354
|
-
|
|
46355
|
-
{ type: "text", text: SYSTEM_PROMPT },
|
|
46356
|
-
{ type: "text", text: `<tbox-slice>
|
|
46357
|
-
${tboxSlice}
|
|
46358
|
-
</tbox-slice>`, cache_control: { type: "ephemeral" } }
|
|
46359
|
-
],
|
|
46360
|
-
messages: [{ role: "user", content: `Question: ${question}
|
|
46361
|
-
|
|
46362
|
-
Return the JSON.` }]
|
|
46385
|
+
const text = await provider.complete({
|
|
46386
|
+
systemPrompt: SYSTEM_PROMPT,
|
|
46387
|
+
tboxSlice,
|
|
46388
|
+
question,
|
|
46389
|
+
maxTokens: 1024
|
|
46363
46390
|
});
|
|
46364
|
-
|
|
46391
|
+
this.lastProviderUsed = provider.kind;
|
|
46365
46392
|
const stripped = text.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim();
|
|
46366
46393
|
const parsed = JSON.parse(stripped);
|
|
46367
46394
|
if (!Array.isArray(parsed.subQuestions)) return deterministicResult;
|
|
@@ -46373,10 +46400,10 @@ Return the JSON.` }]
|
|
|
46373
46400
|
payload: sq.intent.payload ?? {}
|
|
46374
46401
|
}
|
|
46375
46402
|
}));
|
|
46376
|
-
if (validated.length === 0) return this.
|
|
46403
|
+
if (validated.length === 0) return this.fallbackOnEmpty ? deterministicResult : [];
|
|
46377
46404
|
return validated;
|
|
46378
46405
|
} catch {
|
|
46379
|
-
return this.
|
|
46406
|
+
return this.fallbackOnEmpty ? deterministicResult : [];
|
|
46380
46407
|
}
|
|
46381
46408
|
}
|
|
46382
46409
|
};
|
|
@@ -47214,18 +47241,22 @@ async function buildTBoxSlice(client) {
|
|
|
47214
47241
|
);
|
|
47215
47242
|
return r2.results.bindings.map((b2) => `${b2["p"].value} a ${b2["kind"].value} .`).join("\n");
|
|
47216
47243
|
}
|
|
47217
|
-
async function kgResearchGoal(client, input) {
|
|
47244
|
+
async function kgResearchGoal(client, input, deps = {}) {
|
|
47218
47245
|
const baseInput = {
|
|
47219
47246
|
goal: input.goal,
|
|
47220
47247
|
source: input.source ?? "user",
|
|
47221
47248
|
parentGoal: input.parentGoal
|
|
47222
47249
|
};
|
|
47223
|
-
const
|
|
47224
|
-
|
|
47250
|
+
const providers = [
|
|
47251
|
+
...deps.extraCompletionProviders ?? [],
|
|
47252
|
+
new AnthropicSdkProvider()
|
|
47253
|
+
];
|
|
47254
|
+
const hasAvailableLlmProvider = providers.some((p2) => p2.isAvailable());
|
|
47255
|
+
const useSemantic = Boolean(input.useLlmDecomposer) && hasAvailableLlmProvider;
|
|
47225
47256
|
if (useSemantic) {
|
|
47226
47257
|
const store = new GoalStore(client);
|
|
47227
47258
|
const detector = new GapDetector(client);
|
|
47228
|
-
const semantic = new SemanticDecomposer();
|
|
47259
|
+
const semantic = new SemanticDecomposer({ providers });
|
|
47229
47260
|
const goal = await store.create({
|
|
47230
47261
|
statement: baseInput.goal,
|
|
47231
47262
|
source: baseInput.source,
|
|
@@ -47235,8 +47266,9 @@ async function kgResearchGoal(client, input) {
|
|
|
47235
47266
|
const subQuestions = await semantic.decompose(baseInput.goal, tboxSlice);
|
|
47236
47267
|
const gaps = await Promise.all(subQuestions.map((sq) => detector.detect(sq)));
|
|
47237
47268
|
const plan2 = { goalId: goal.id, subQuestions, gaps };
|
|
47269
|
+
const decomposerProvider = semantic.lastProviderUsed ?? void 0;
|
|
47238
47270
|
if (!input.executeResearch) {
|
|
47239
|
-
return { ...plan2, decomposerKind };
|
|
47271
|
+
return { ...plan2, decomposerKind: "semantic", ...decomposerProvider && { decomposerProvider } };
|
|
47240
47272
|
}
|
|
47241
47273
|
if (!input.corpusRoot) {
|
|
47242
47274
|
throw new Error(
|
|
@@ -47260,7 +47292,7 @@ async function kgResearchGoal(client, input) {
|
|
|
47260
47292
|
}
|
|
47261
47293
|
if (!input.executeResearch) {
|
|
47262
47294
|
const plan2 = await researchGoal(client, baseInput);
|
|
47263
|
-
return { ...plan2, decomposerKind };
|
|
47295
|
+
return { ...plan2, decomposerKind: "deterministic" };
|
|
47264
47296
|
}
|
|
47265
47297
|
if (!input.corpusRoot) {
|
|
47266
47298
|
throw new Error(
|
|
@@ -47280,7 +47312,7 @@ async function kgResearchGoal(client, input) {
|
|
|
47280
47312
|
new EnvVarExtractor()
|
|
47281
47313
|
]
|
|
47282
47314
|
});
|
|
47283
|
-
return { ...plan, decomposerKind };
|
|
47315
|
+
return { ...plan, decomposerKind: "deterministic" };
|
|
47284
47316
|
}
|
|
47285
47317
|
|
|
47286
47318
|
// ../predicate-mcp/src/tools/kg-propose-schema.ts
|
|
@@ -47522,7 +47554,8 @@ var schemaDeltaSchema = external_exports.discriminatedUnion("kind", [
|
|
|
47522
47554
|
shapes: external_exports.array(deltaQuadSchema).optional()
|
|
47523
47555
|
})
|
|
47524
47556
|
]);
|
|
47525
|
-
function buildTools(client) {
|
|
47557
|
+
function buildTools(client, options = {}) {
|
|
47558
|
+
const extraCompletionProviders = options.extraCompletionProviders ?? [];
|
|
47526
47559
|
return [
|
|
47527
47560
|
{
|
|
47528
47561
|
name: "kg_explore_schema",
|
|
@@ -47606,7 +47639,7 @@ function buildTools(client) {
|
|
|
47606
47639
|
},
|
|
47607
47640
|
{
|
|
47608
47641
|
name: "kg_research_goal",
|
|
47609
|
-
description: "Decompose a goal and report which predicates the live TBox can/cannot answer. When executeResearch=true and corpusRoot is provided, also fetch artifacts from that directory, extract candidate triples, and assert them via kg_assert. Set useLlmDecomposer=true to enable
|
|
47642
|
+
description: "Decompose a goal and report which predicates the live TBox can/cannot answer. When executeResearch=true and corpusRoot is provided, also fetch artifacts from that directory, extract candidate triples, and assert them via kg_assert. Set useLlmDecomposer=true to enable LLM-augmented decomposition for questions that do not match a built-in pattern; the decomposer prefers MCP sampling (no API key needed) and falls back to ANTHROPIC_API_KEY, then to deterministic.",
|
|
47610
47643
|
inputSchema: external_exports.object({
|
|
47611
47644
|
goal: external_exports.string().min(1),
|
|
47612
47645
|
source: external_exports.enum(["user", "inferred"]).optional(),
|
|
@@ -47624,7 +47657,7 @@ function buildTools(client) {
|
|
|
47624
47657
|
corpusRoot: external_exports.string().optional(),
|
|
47625
47658
|
useLlmDecomposer: external_exports.boolean().optional()
|
|
47626
47659
|
}).parse(raw);
|
|
47627
|
-
return kgResearchGoal(client, args);
|
|
47660
|
+
return kgResearchGoal(client, args, { extraCompletionProviders });
|
|
47628
47661
|
}
|
|
47629
47662
|
},
|
|
47630
47663
|
{
|
|
@@ -47708,15 +47741,62 @@ function stubs() {
|
|
|
47708
47741
|
return [];
|
|
47709
47742
|
}
|
|
47710
47743
|
|
|
47744
|
+
// ../predicate-mcp/src/sampling-provider.ts
|
|
47745
|
+
var SAMPLING_MODEL_HINT = "claude-haiku";
|
|
47746
|
+
var SamplingProvider = class {
|
|
47747
|
+
constructor(server) {
|
|
47748
|
+
this.server = server;
|
|
47749
|
+
}
|
|
47750
|
+
server;
|
|
47751
|
+
kind = "mcp-sampling";
|
|
47752
|
+
isAvailable() {
|
|
47753
|
+
const caps = this.server.getClientCapabilities();
|
|
47754
|
+
return Boolean(caps?.sampling);
|
|
47755
|
+
}
|
|
47756
|
+
async complete(req) {
|
|
47757
|
+
const result = await this.server.createMessage({
|
|
47758
|
+
systemPrompt: `${req.systemPrompt}
|
|
47759
|
+
|
|
47760
|
+
<tbox-slice>
|
|
47761
|
+
${req.tboxSlice}
|
|
47762
|
+
</tbox-slice>`,
|
|
47763
|
+
maxTokens: req.maxTokens,
|
|
47764
|
+
modelPreferences: {
|
|
47765
|
+
hints: [{ name: SAMPLING_MODEL_HINT }],
|
|
47766
|
+
intelligencePriority: 0.3,
|
|
47767
|
+
speedPriority: 0.7
|
|
47768
|
+
},
|
|
47769
|
+
messages: [
|
|
47770
|
+
{
|
|
47771
|
+
role: "user",
|
|
47772
|
+
content: { type: "text", text: `Question: ${req.question}
|
|
47773
|
+
|
|
47774
|
+
Return the JSON.` }
|
|
47775
|
+
}
|
|
47776
|
+
]
|
|
47777
|
+
});
|
|
47778
|
+
const content = result.content;
|
|
47779
|
+
if (Array.isArray(content)) {
|
|
47780
|
+
return content.filter((b2) => b2.type === "text").map((b2) => b2.text).join("\n");
|
|
47781
|
+
}
|
|
47782
|
+
if (content && typeof content === "object" && "type" in content && content.type === "text") {
|
|
47783
|
+
return content.text;
|
|
47784
|
+
}
|
|
47785
|
+
return "";
|
|
47786
|
+
}
|
|
47787
|
+
};
|
|
47788
|
+
|
|
47711
47789
|
// ../predicate-mcp/src/index.ts
|
|
47712
47790
|
async function main() {
|
|
47713
47791
|
const config2 = loadConfig();
|
|
47714
47792
|
const client = new SparqlClient(config2);
|
|
47715
|
-
const tools = buildTools(client);
|
|
47716
47793
|
const server = new Server(
|
|
47717
47794
|
{ name: "predicate-mcp", version: "0.1.0" },
|
|
47718
47795
|
{ capabilities: { tools: {} } }
|
|
47719
47796
|
);
|
|
47797
|
+
const tools = buildTools(client, {
|
|
47798
|
+
extraCompletionProviders: [new SamplingProvider(server)]
|
|
47799
|
+
});
|
|
47720
47800
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
47721
47801
|
tools: tools.map((t2) => ({
|
|
47722
47802
|
name: t2.name,
|