predicate-skill 2.0.4 → 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.bundle.mjs +112 -32
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "predicate-skill",
3
- "version": "2.0.4",
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",
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
- options;
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.options = { fallbackOnEmpty: options.fallbackOnEmpty ?? true };
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
- if (!process.env["ANTHROPIC_API_KEY"]) {
46347
- return this.options.fallbackOnEmpty ? deterministicResult : [];
46380
+ const provider = this.providers.find((p2) => p2.isAvailable());
46381
+ if (!provider) {
46382
+ return this.fallbackOnEmpty ? deterministicResult : [];
46348
46383
  }
46349
46384
  try {
46350
- const client = new sdk_default();
46351
- const response = await client.messages.create({
46352
- model: MODEL,
46353
- max_tokens: 1024,
46354
- system: [
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
- const text = response.content.filter((b2) => b2.type === "text").map((b2) => b2.text).join("\n");
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.options.fallbackOnEmpty ? deterministicResult : [];
46403
+ if (validated.length === 0) return this.fallbackOnEmpty ? deterministicResult : [];
46377
46404
  return validated;
46378
46405
  } catch {
46379
- return this.options.fallbackOnEmpty ? deterministicResult : [];
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 useSemantic = Boolean(input.useLlmDecomposer) && Boolean(process.env["ANTHROPIC_API_KEY"]);
47224
- const decomposerKind = useSemantic ? "semantic" : "deterministic";
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 Claude-Haiku-backed decomposition for questions that do not match a built-in pattern (requires ANTHROPIC_API_KEY; falls back to deterministic otherwise).",
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,