kairos-chain 3.9.4 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -0
- data/lib/kairos_mcp/safety.rb +6 -0
- data/lib/kairos_mcp/tools/skills_promote.rb +30 -4
- data/lib/kairos_mcp/tools/system_upgrade.rb +33 -0
- data/lib/kairos_mcp/version.rb +1 -1
- data/templates/knowledge/multi_llm_review_workflow/multi_llm_review_workflow.md +60 -1
- data/templates/skillsets/agent/tools/agent_start.rb +3 -1
- data/templates/skillsets/dream/lib/dream/scanner.rb +102 -7
- data/templates/skillsets/dream/tools/dream_scan.rb +9 -2
- data/templates/skillsets/introspection/config/introspection.yml +4 -0
- data/templates/skillsets/introspection/knowledge/introspection_guide/introspection_guide.md +90 -0
- data/templates/skillsets/introspection/lib/introspection/health_scorer.rb +136 -0
- data/templates/skillsets/introspection/lib/introspection/safety_inspector.rb +85 -0
- data/templates/skillsets/introspection/lib/introspection.rb +7 -0
- data/templates/skillsets/introspection/skillset.json +17 -0
- data/templates/skillsets/introspection/tools/introspection_check.rb +207 -0
- data/templates/skillsets/introspection/tools/introspection_health.rb +80 -0
- data/templates/skillsets/introspection/tools/introspection_safety.rb +46 -0
- data/templates/skillsets/llm_client/lib/llm_client/adapter.rb +1 -1
- data/templates/skillsets/llm_client/lib/llm_client/anthropic_adapter.rb +7 -1
- data/templates/skillsets/llm_client/lib/llm_client/bedrock_adapter.rb +8 -1
- data/templates/skillsets/llm_client/lib/llm_client/claude_code_adapter.rb +10 -3
- data/templates/skillsets/llm_client/lib/llm_client/openai_adapter.rb +11 -1
- data/templates/skillsets/llm_client/lib/llm_client/schema_converter.rb +12 -1
- data/templates/skillsets/llm_client/tools/llm_call.rb +9 -1
- metadata +12 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0acca30b533051c884363e6c9215394d308f501985a6dc647f92c513e875ce6b
|
|
4
|
+
data.tar.gz: 862c366ebcb1f88a66eb5c30b48086e454145609d57f1fbb8ba6aa62aeb89652
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9066154fcf88d0e39457fca4c4f4ebac74e7104c9d5d2bf047783e2f058ddfc9b03e38be965f492ac914b81eb611da50cff7e63264e6a16caadf0e23d8d707ef
|
|
7
|
+
data.tar.gz: b7324da5f140698a74d11e94dedee3a3bdc2d778a9f0f2ca12417861bb5a5b23ad96eb6f4ad94077c1b6f72cbc2f2e0c3651c62d08242452451d7f0ada75d330
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,58 @@ All notable changes to the `kairos-chain` gem will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
This project follows [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [3.11.0] - 2026-04-01
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`llm_call` `output_schema` parameter** — Enables structured JSON output from LLMs.
|
|
12
|
+
When provided, the LLM is constrained to return JSON matching the given JSON Schema.
|
|
13
|
+
- Anthropic/Bedrock: system prompt injection with tools-aware qualifier
|
|
14
|
+
("When you are NOT using a tool, respond with ONLY valid JSON...")
|
|
15
|
+
- OpenAI: native `response_format: { type: "json_schema", strict: true }`
|
|
16
|
+
- Claude Code CLI: prompt text injection
|
|
17
|
+
- Backward compatible: `nil` default preserves all existing behavior
|
|
18
|
+
- **`SchemaConverter.normalize_for_openai` `required` auto-population** — When an
|
|
19
|
+
object schema has no `required` key, all property names are auto-populated into
|
|
20
|
+
`required` to satisfy OpenAI strict mode constraints. Existing `required` arrays
|
|
21
|
+
are preserved unchanged.
|
|
22
|
+
- **Multi-LLM review XML block prompts** (`multi_llm_review_workflow` v3.2) — Five
|
|
23
|
+
XML blocks for structured review prompts:
|
|
24
|
+
`<task>`, `<structured_output_contract>`, `<grounding_rules>`,
|
|
25
|
+
`<verification_loop>`, `<default_follow_through_policy>`.
|
|
26
|
+
LLM-agnostic; works with Claude, GPT, and Composer models.
|
|
27
|
+
|
|
28
|
+
### Design Process
|
|
29
|
+
|
|
30
|
+
- Inspired by codex-plugin-cc (Claude Code Codex plugin) analysis — 4-agent team review
|
|
31
|
+
- Implementation reviewed: 2 rounds x 3 LLMs (Claude Opus 4.6, Codex GPT-5.4, Cursor Composer-2)
|
|
32
|
+
- R1: 3/3 APPROVE WITH CHANGES — 2 must-fix findings (tools+schema conflict, OpenAI required)
|
|
33
|
+
- R2: 2/3 APPROVE, 1/3 AWC, 0 FAIL/HIGH — converged
|
|
34
|
+
- Tests: 32 → 47 (all PASS)
|
|
35
|
+
|
|
36
|
+
## [3.10.0] - 2026-03-31
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- **introspection SkillSet** (v0.1.0) — New self-inspection SkillSet with 3 tools:
|
|
41
|
+
- `introspection_check`: Full inspection (L1 health + blockchain integrity + safety mechanisms + recommendations)
|
|
42
|
+
- `introspection_health`: L1 knowledge health scores using Synoptis TrustScorer (optional, falls back to staleness-only)
|
|
43
|
+
- `introspection_safety`: 4-layer safety mechanism visibility (L0 approval workflow, RBAC policies, agent safety gates, blockchain recording)
|
|
44
|
+
- **Dream SkillSet L1 dedup + confidence scoring** (v0.2.1) — `dream_scan` now checks promotion candidates against existing L1 knowledge (name similarity + tag Jaccard overlap) and scores candidates with 3-dimension confidence (recurrence, tag consistency, session diversity). New `include_l1_dedup` parameter.
|
|
45
|
+
- **`skills_promote` attestation integration** — Successful L2→L1 promotions now automatically issue Synoptis attestations (`claim: "promoted_from_l2"`, `actor_role: "automated"`). Graceful degradation when Synoptis is not loaded.
|
|
46
|
+
- **`Safety.registered_policy_names`** — New thread-safe public API for introspecting registered RBAC policies. Replaces `instance_variable_get(:@policies)` pattern.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- **`system_upgrade` SkillSet-only install** — When specific SkillSet names are provided via `names` parameter but L0 templates are already up-to-date, `system_upgrade apply` now correctly installs/upgrades the requested SkillSets instead of returning "No upgrade needed". Previously, the L0 upgrade check (`UpgradeAnalyzer.upgrade_needed?`) gated all operations including SkillSet installs.
|
|
51
|
+
|
|
52
|
+
### Design Process
|
|
53
|
+
|
|
54
|
+
- Design reviewed: 2 rounds x 3 LLMs (Claude Opus 4.6, Codex GPT-5.4, Cursor Composer-2)
|
|
55
|
+
- Implementation reviewed: 1 round x 3 LLMs per phase
|
|
56
|
+
- 8 P0/P1 bugs found and fixed during design review (before any code was written)
|
|
57
|
+
- Inspired by oh-my-claudecode analysis; independently designed with KairosChain philosophy
|
|
58
|
+
|
|
7
59
|
## [3.9.4] - 2026-03-30
|
|
8
60
|
|
|
9
61
|
### Added
|
data/lib/kairos_mcp/safety.rb
CHANGED
|
@@ -25,6 +25,12 @@ module KairosMcp
|
|
|
25
25
|
@policy_mutex.synchronize { @policies[name.to_sym] }
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# Thread-safe list of registered policy names.
|
|
29
|
+
# Used by introspection SkillSet for safety visibility.
|
|
30
|
+
def self.registered_policy_names
|
|
31
|
+
@policy_mutex.synchronize { @policies.keys.map(&:to_s) }
|
|
32
|
+
end
|
|
33
|
+
|
|
28
34
|
# For testing only
|
|
29
35
|
def self.clear_policies!
|
|
30
36
|
@policy_mutex.synchronize { @policies = {} }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'json'
|
|
3
4
|
require_relative 'base_tool'
|
|
4
5
|
require_relative '../knowledge_provider'
|
|
5
6
|
require_relative '../context_manager'
|
|
@@ -30,7 +31,8 @@ module KairosMcp
|
|
|
30
31
|
|
|
31
32
|
def description
|
|
32
33
|
'Promote knowledge between layers (L2→L1, L1→L0) with optional Persona Assembly for decision support. ' \
|
|
33
|
-
'Assembly generates a structured discussion from multiple perspectives before human decision.'
|
|
34
|
+
'Assembly generates a structured discussion from multiple perspectives before human decision. ' \
|
|
35
|
+
'For pattern detection and auto-scan, use dream_scan instead.'
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def category
|
|
@@ -63,7 +65,7 @@ module KairosMcp
|
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
def related_tools
|
|
66
|
-
%w[context_save knowledge_update skills_evolve skills_audit]
|
|
68
|
+
%w[context_save knowledge_update skills_evolve skills_audit attestation_issue]
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
def input_schema
|
|
@@ -72,7 +74,7 @@ module KairosMcp
|
|
|
72
74
|
properties: {
|
|
73
75
|
command: {
|
|
74
76
|
type: 'string',
|
|
75
|
-
description: 'Command: "analyze" (with assembly), "promote" (direct promotion), "status" (check requirements), or "suggest" (LLM suggests optimal personas
|
|
77
|
+
description: 'Command: "analyze" (with assembly), "promote" (direct promotion), "status" (check requirements), or "suggest" (LLM suggests optimal personas)',
|
|
76
78
|
enum: %w[analyze promote status suggest]
|
|
77
79
|
},
|
|
78
80
|
source_name: {
|
|
@@ -122,7 +124,7 @@ module KairosMcp
|
|
|
122
124
|
consensus_threshold: {
|
|
123
125
|
type: 'number',
|
|
124
126
|
description: 'Consensus threshold for early termination in discussion mode (default: 0.6 = 60%)'
|
|
125
|
-
}
|
|
127
|
+
},
|
|
126
128
|
},
|
|
127
129
|
required: %w[command]
|
|
128
130
|
}
|
|
@@ -576,6 +578,9 @@ module KairosMcp
|
|
|
576
578
|
# Track promotion event for state commit (this triggers auto-commit on promotion)
|
|
577
579
|
track_promotion_change(from_layer: 'L2', to_layer: 'L1', skill_id: target_name, reason: reason)
|
|
578
580
|
|
|
581
|
+
# Issue attestation AFTER L1 exists
|
|
582
|
+
issue_promotion_attestation(target_name, reason)
|
|
583
|
+
|
|
579
584
|
action = existing ? 'updated' : 'created'
|
|
580
585
|
output = "## Promotion Successful\n\n"
|
|
581
586
|
output += "**Target**: #{target_name} (L1)\n"
|
|
@@ -681,6 +686,27 @@ module KairosMcp
|
|
|
681
686
|
end
|
|
682
687
|
end
|
|
683
688
|
|
|
689
|
+
# Issue attestation after successful promotion
|
|
690
|
+
def issue_promotion_attestation(target_name, reason)
|
|
691
|
+
return unless synoptis_available?
|
|
692
|
+
|
|
693
|
+
invoke_tool('attestation_issue', {
|
|
694
|
+
'subject_ref' => "knowledge://#{target_name}",
|
|
695
|
+
'claim' => 'promoted_from_l2',
|
|
696
|
+
'evidence' => reason.to_s,
|
|
697
|
+
'actor_role' => 'automated'
|
|
698
|
+
})
|
|
699
|
+
rescue StandardError => e
|
|
700
|
+
# Synoptis may not be loaded — silently skip
|
|
701
|
+
warn "[SkillsPromote] Attestation skipped: #{e.message}"
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
def synoptis_available?
|
|
705
|
+
@registry && true
|
|
706
|
+
rescue StandardError
|
|
707
|
+
false
|
|
708
|
+
end
|
|
709
|
+
|
|
684
710
|
# Track promotion event for state commit auto-commit
|
|
685
711
|
def track_promotion_change(from_layer:, to_layer:, skill_id:, reason: nil)
|
|
686
712
|
return unless SkillsConfig.state_commit_enabled?
|
|
@@ -5,6 +5,7 @@ require 'fileutils'
|
|
|
5
5
|
require_relative 'base_tool'
|
|
6
6
|
require_relative '../upgrade_analyzer'
|
|
7
7
|
require_relative '../config_merger'
|
|
8
|
+
require_relative '../skillset_manager'
|
|
8
9
|
|
|
9
10
|
module KairosMcp
|
|
10
11
|
module Tools
|
|
@@ -240,6 +241,12 @@ module KairosMcp
|
|
|
240
241
|
analyzer = UpgradeAnalyzer.new
|
|
241
242
|
analyzer.analyze
|
|
242
243
|
|
|
244
|
+
# When specific SkillSet names are requested, skip L0 upgrade check
|
|
245
|
+
# and go directly to SkillSet install/upgrade
|
|
246
|
+
if names && !names.empty? && !analyzer.upgrade_needed?
|
|
247
|
+
return handle_skillset_only_install(names)
|
|
248
|
+
end
|
|
249
|
+
|
|
243
250
|
unless analyzer.upgrade_needed?
|
|
244
251
|
return text_content("No upgrade needed. Data directory is already up to date.")
|
|
245
252
|
end
|
|
@@ -437,6 +444,32 @@ module KairosMcp
|
|
|
437
444
|
# =====================================================================
|
|
438
445
|
# skillsets — List all available SkillSets with install status
|
|
439
446
|
# =====================================================================
|
|
447
|
+
# Install/upgrade specific SkillSets without requiring a full L0 upgrade.
|
|
448
|
+
# Called when names are specified but L0 templates are already up to date.
|
|
449
|
+
def handle_skillset_only_install(names)
|
|
450
|
+
ss_mgr = KairosMcp::SkillSetManager.new
|
|
451
|
+
ss_results = ss_mgr.upgrade_apply(names: names)
|
|
452
|
+
|
|
453
|
+
if ss_results.empty?
|
|
454
|
+
return text_content("No SkillSet changes needed for: #{names.join(', ')}")
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
output = "# SkillSet Install/Upgrade\n\n"
|
|
458
|
+
ss_results.each do |r|
|
|
459
|
+
if r[:action] == 'installed'
|
|
460
|
+
output += " [INSTALLED] #{r[:name]} v#{r[:to]}\n"
|
|
461
|
+
else
|
|
462
|
+
output += " [UPGRADED] #{r[:name]} v#{r[:from]} → v#{r[:to]} (#{r[:files_updated]} files)\n"
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
output += "\nNo L0 template changes needed.\n"
|
|
466
|
+
output += "Restart the MCP server to load new SkillSet tools.\n"
|
|
467
|
+
|
|
468
|
+
text_content(output)
|
|
469
|
+
rescue StandardError => e
|
|
470
|
+
text_content("SkillSet install failed: #{e.message}")
|
|
471
|
+
end
|
|
472
|
+
|
|
440
473
|
def handle_skillsets
|
|
441
474
|
ss_mgr = KairosMcp::SkillSetManager.new
|
|
442
475
|
available = ss_mgr.available_skillsets
|
data/lib/kairos_mcp/version.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: multi_llm_review_workflow
|
|
3
3
|
description: "Multi-LLM review methodology and execution — workflow pattern, CLI tooling, consensus analysis, Persona Assembly. Applicable to design, implementation, documentation, or any artifact."
|
|
4
|
-
version: "3.
|
|
4
|
+
version: "3.2"
|
|
5
5
|
tags:
|
|
6
6
|
- workflow
|
|
7
7
|
- review
|
|
@@ -243,6 +243,65 @@ Every review prompt MUST include these 7 items:
|
|
|
243
243
|
|
|
244
244
|
All prompt content MUST be in **English** for consistent parsing across LLM tools.
|
|
245
245
|
|
|
246
|
+
### XML Block Structure for Review Prompts
|
|
247
|
+
|
|
248
|
+
Review prompts SHOULD use XML blocks to give LLMs explicit structural contracts.
|
|
249
|
+
This reduces hallucination, enforces grounded findings, and standardizes output.
|
|
250
|
+
|
|
251
|
+
```xml
|
|
252
|
+
<task>
|
|
253
|
+
Review the provided artifact for [review type: design correctness / implementation bugs / ...].
|
|
254
|
+
Target: [artifact name and version]
|
|
255
|
+
Scope: [what changed since last round, or "initial review"]
|
|
256
|
+
</task>
|
|
257
|
+
|
|
258
|
+
<structured_output_contract>
|
|
259
|
+
Output a Markdown file with this structure:
|
|
260
|
+
- **Reviewer**: [tool_name]
|
|
261
|
+
- **Model**: [model_id]
|
|
262
|
+
- **Date**: [ISO date]
|
|
263
|
+
- **Overall Verdict**: APPROVE / APPROVE WITH CHANGES / REJECT
|
|
264
|
+
|
|
265
|
+
For each finding:
|
|
266
|
+
- **Severity**: FAIL / HIGH / MEDIUM / LOW
|
|
267
|
+
- **Confidence**: 0.0-1.0 (how certain you are this is a real issue)
|
|
268
|
+
- **Location**: file:line or section reference
|
|
269
|
+
- **What can go wrong**: concrete failure scenario
|
|
270
|
+
- **Why this is vulnerable**: code path or design gap
|
|
271
|
+
- **Likely impact**: data loss, security breach, silent corruption, etc.
|
|
272
|
+
- **Recommended fix**: specific change (not "consider improving")
|
|
273
|
+
</structured_output_contract>
|
|
274
|
+
|
|
275
|
+
<grounding_rules>
|
|
276
|
+
Ground every finding in the provided artifact text or referenced source files.
|
|
277
|
+
If a claim is an inference (not directly visible in the artifact), label it:
|
|
278
|
+
"[INFERRED] Based on X, this likely means Y."
|
|
279
|
+
Do not invent files, methods, or runtime behavior not shown in the artifact.
|
|
280
|
+
Keep confidence scores honest — 0.5 if uncertain, 0.9+ only if directly evidenced.
|
|
281
|
+
</grounding_rules>
|
|
282
|
+
|
|
283
|
+
<verification_loop>
|
|
284
|
+
Before finalizing your review:
|
|
285
|
+
1. Re-read each FAIL/HIGH finding. Is the failure scenario concrete and reproducible?
|
|
286
|
+
2. Check for second-order failures: empty-state, retry, stale state, rollback risk.
|
|
287
|
+
3. Verify file paths and line numbers are accurate.
|
|
288
|
+
4. If you found zero issues, state that explicitly — do not manufacture findings.
|
|
289
|
+
</verification_loop>
|
|
290
|
+
|
|
291
|
+
<default_follow_through_policy>
|
|
292
|
+
Complete the full review in one pass. Do not ask clarifying questions.
|
|
293
|
+
If context is missing, note it as a finding with severity LOW and confidence 0.3.
|
|
294
|
+
</default_follow_through_policy>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Usage**: Include these XML blocks in the prompt body (rule #3 "Review instructions").
|
|
298
|
+
They replace or supplement free-form review instructions. The blocks are
|
|
299
|
+
LLM-agnostic and work with Claude, GPT, and Composer models.
|
|
300
|
+
|
|
301
|
+
**When to use full XML blocks vs. lightweight**:
|
|
302
|
+
- **Full** (all 5 blocks): Design review, implementation review, security-critical
|
|
303
|
+
- **Lightweight** (`<task>` + `<structured_output_contract>` only): Fix plan review, document review
|
|
304
|
+
|
|
246
305
|
### Output Directive in Prompt Body
|
|
247
306
|
|
|
248
307
|
The prompt body itself (what the LLM sees) must contain:
|
|
@@ -39,7 +39,9 @@ module KairosMcp
|
|
|
39
39
|
properties: {
|
|
40
40
|
goal_name: {
|
|
41
41
|
type: 'string',
|
|
42
|
-
description: 'Name of the goal
|
|
42
|
+
description: 'Name of the goal. Create it with context_save (L2) first. ' \
|
|
43
|
+
'L1 knowledge is only for reusable goal templates. ' \
|
|
44
|
+
'The agent searches L2 contexts first, then falls back to L1.'
|
|
43
45
|
},
|
|
44
46
|
max_cycles: {
|
|
45
47
|
type: 'integer',
|
|
@@ -25,8 +25,9 @@ module KairosMcp
|
|
|
25
25
|
# @param scope [String] 'l2', 'l1', or 'all'
|
|
26
26
|
# @param since_session [String, nil] Only scan sessions after this ID
|
|
27
27
|
# @param include_archive_candidates [Boolean] Whether to detect stale L2
|
|
28
|
+
# @param include_l1_dedup [Boolean] Check promotion candidates against existing L1
|
|
28
29
|
# @return [Hash] Structured scan result
|
|
29
|
-
def scan(scope: 'l2', since_session: nil, include_archive_candidates: true)
|
|
30
|
+
def scan(scope: 'l2', since_session: nil, include_archive_candidates: true, include_l1_dedup: true)
|
|
30
31
|
result = {
|
|
31
32
|
scope: scope,
|
|
32
33
|
scanned_at: Time.now.iso8601,
|
|
@@ -38,7 +39,8 @@ module KairosMcp
|
|
|
38
39
|
|
|
39
40
|
if %w[l2 all].include?(scope)
|
|
40
41
|
scan_l2(result, since_session: since_session,
|
|
41
|
-
include_archive_candidates: include_archive_candidates
|
|
42
|
+
include_archive_candidates: include_archive_candidates,
|
|
43
|
+
include_l1_dedup: include_l1_dedup)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
if %w[l1 all].include?(scope)
|
|
@@ -54,7 +56,7 @@ module KairosMcp
|
|
|
54
56
|
# L2 scanning
|
|
55
57
|
# ---------------------------------------------------------------
|
|
56
58
|
|
|
57
|
-
def scan_l2(result, since_session: nil, include_archive_candidates: true)
|
|
59
|
+
def scan_l2(result, since_session: nil, include_archive_candidates: true, include_l1_dedup: true)
|
|
58
60
|
all_contexts = load_all_l2_contexts(since_session: since_session)
|
|
59
61
|
|
|
60
62
|
# Partition: live contexts vs archived stubs
|
|
@@ -62,7 +64,20 @@ module KairosMcp
|
|
|
62
64
|
|
|
63
65
|
# Promotion candidates: tag co-occurrence across sessions
|
|
64
66
|
sessions_tags = build_sessions_tags(live)
|
|
65
|
-
|
|
67
|
+
candidates = detect_promotion_candidates(sessions_tags, all_sessions_tags: sessions_tags)
|
|
68
|
+
|
|
69
|
+
# L1 dedup: mark candidates that already exist as L1 knowledge
|
|
70
|
+
if include_l1_dedup
|
|
71
|
+
kp = knowledge_provider
|
|
72
|
+
existing_l1 = kp ? kp.list : []
|
|
73
|
+
candidates.each do |candidate|
|
|
74
|
+
match = find_l1_match(candidate[:tag], Array(candidate[:tag]), existing_l1)
|
|
75
|
+
candidate[:already_in_l1] = !match.nil?
|
|
76
|
+
candidate[:l1_match] = match if match
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
result[:promotion_candidates] = candidates
|
|
66
81
|
|
|
67
82
|
# Consolidation candidates: name token overlap
|
|
68
83
|
result[:consolidation_candidates] = detect_consolidation_candidates(live)
|
|
@@ -132,8 +147,11 @@ module KairosMcp
|
|
|
132
147
|
# Detect tags that recur across min_recurrence+ distinct sessions.
|
|
133
148
|
#
|
|
134
149
|
# @param sessions_tags [Hash] { session_id => { context_name => [tags] } }
|
|
135
|
-
# @
|
|
136
|
-
|
|
150
|
+
# @param all_sessions_tags [Hash] same as sessions_tags (for scoring)
|
|
151
|
+
# @return [Array<Hash>] promotion candidate hashes with confidence scoring
|
|
152
|
+
def detect_promotion_candidates(sessions_tags, all_sessions_tags: nil)
|
|
153
|
+
all_sessions_tags ||= sessions_tags
|
|
154
|
+
|
|
137
155
|
tag_sessions = Hash.new { |h, k| h[k] = Set.new }
|
|
138
156
|
|
|
139
157
|
sessions_tags.each do |session_id, contexts|
|
|
@@ -148,12 +166,15 @@ module KairosMcp
|
|
|
148
166
|
.sort_by { |_tag, sids| -sids.size }
|
|
149
167
|
.first(@max_candidates)
|
|
150
168
|
.map do |tag, sids|
|
|
169
|
+
confidence = score_promotion_candidate(tag, sids, all_sessions_tags)
|
|
151
170
|
{
|
|
152
171
|
tag: tag,
|
|
153
172
|
session_count: sids.size,
|
|
154
173
|
sessions: sids.to_a,
|
|
155
174
|
signal: 'tag_recurrence',
|
|
156
|
-
strength: sids.size.to_f / @min_recurrence
|
|
175
|
+
strength: sids.size.to_f / @min_recurrence,
|
|
176
|
+
confidence: confidence,
|
|
177
|
+
already_in_l1: false
|
|
157
178
|
}
|
|
158
179
|
end
|
|
159
180
|
|
|
@@ -274,6 +295,80 @@ module KairosMcp
|
|
|
274
295
|
tags
|
|
275
296
|
end
|
|
276
297
|
|
|
298
|
+
# ---------------------------------------------------------------
|
|
299
|
+
# L1 dedup and confidence scoring (migrated from skills_promote auto_scan)
|
|
300
|
+
# ---------------------------------------------------------------
|
|
301
|
+
|
|
302
|
+
# Find an existing L1 entry that matches a candidate by name or tag overlap.
|
|
303
|
+
#
|
|
304
|
+
# @param candidate_name [String] suggested/tag name of the candidate
|
|
305
|
+
# @param candidate_tags [Array<String>] tags for the candidate
|
|
306
|
+
# @param existing_l1 [Array<Hash>] list from KnowledgeProvider#list
|
|
307
|
+
# @return [String, nil] matching L1 entry name or nil
|
|
308
|
+
def find_l1_match(candidate_name, candidate_tags, existing_l1)
|
|
309
|
+
existing_l1.each do |entry|
|
|
310
|
+
# Name match (normalized substring ratio)
|
|
311
|
+
name_sim = name_similarity(candidate_name, entry[:name])
|
|
312
|
+
return entry[:name] if name_sim > 0.8
|
|
313
|
+
|
|
314
|
+
# Tag overlap (Jaccard on name tokens vs entry tags)
|
|
315
|
+
entry_tags = Array(entry[:tags]).to_set
|
|
316
|
+
candidate_set = candidate_tags.to_set
|
|
317
|
+
sim = candidate_set.empty? && entry_tags.empty? ? 0.0 : (candidate_set & entry_tags).size.to_f / (candidate_set | entry_tags).size.to_f
|
|
318
|
+
return entry[:name] if sim > 0.8
|
|
319
|
+
end
|
|
320
|
+
nil
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Compute name similarity using token-level Jaccard.
|
|
324
|
+
#
|
|
325
|
+
# @param a [String]
|
|
326
|
+
# @param b [String]
|
|
327
|
+
# @return [Float] 0.0..1.0
|
|
328
|
+
def name_similarity(a, b)
|
|
329
|
+
a_parts = a.to_s.split('_').to_set
|
|
330
|
+
b_parts = b.to_s.split('_').to_set
|
|
331
|
+
return 0.0 if a_parts.empty? && b_parts.empty?
|
|
332
|
+
(a_parts & b_parts).size.to_f / [a_parts.size, b_parts.size].max
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Score a promotion candidate across 3 dimensions:
|
|
336
|
+
# - recurrence: how many sessions the tag appears in (max 40)
|
|
337
|
+
# - tag_consistency: co-occurrence consistency across sessions (max 30)
|
|
338
|
+
# - session_diversity: distinct sessions (max 30)
|
|
339
|
+
#
|
|
340
|
+
# @param tag [String] the recurring tag
|
|
341
|
+
# @param sessions [Set<String>] session IDs where this tag appears
|
|
342
|
+
# @param all_sessions_tags [Hash] { session_id => { context_name => [tags] } }
|
|
343
|
+
# @return [Integer] 0..100
|
|
344
|
+
def score_promotion_candidate(tag, sessions, all_sessions_tags)
|
|
345
|
+
# Recurrence: 3=10, 4=20, 5=30, 6+=40 (max 40 points)
|
|
346
|
+
recurrence_score = [[sessions.size - 2, 4].min * 10, 40].min
|
|
347
|
+
recurrence_score = [recurrence_score, 0].max
|
|
348
|
+
|
|
349
|
+
# Tag consistency: what fraction of sessions containing this tag
|
|
350
|
+
# also share the same co-occurring tags?
|
|
351
|
+
co_tags_per_session = sessions.map do |sid|
|
|
352
|
+
contexts = all_sessions_tags[sid] || {}
|
|
353
|
+
contexts.values.flatten.uniq.reject { |t| t == tag }
|
|
354
|
+
end.reject(&:empty?)
|
|
355
|
+
|
|
356
|
+
if co_tags_per_session.size >= 2
|
|
357
|
+
co_sets = co_tags_per_session.map(&:to_set)
|
|
358
|
+
intersection = co_sets.reduce(:&)
|
|
359
|
+
union = co_sets.reduce(:|)
|
|
360
|
+
tag_consistency = union.empty? ? 0.0 : intersection.size.to_f / union.size
|
|
361
|
+
else
|
|
362
|
+
tag_consistency = 0.0
|
|
363
|
+
end
|
|
364
|
+
tag_score = (tag_consistency * 30).round
|
|
365
|
+
|
|
366
|
+
# Session diversity: distinct sessions (max 30 points)
|
|
367
|
+
session_score = [[sessions.size - 1, 3].min * 10, 30].min
|
|
368
|
+
|
|
369
|
+
[recurrence_score + tag_score + session_score, 100].min
|
|
370
|
+
end
|
|
371
|
+
|
|
277
372
|
# ---------------------------------------------------------------
|
|
278
373
|
# Helpers
|
|
279
374
|
# ---------------------------------------------------------------
|
|
@@ -42,6 +42,10 @@ module KairosMcp
|
|
|
42
42
|
include_archive_candidates: {
|
|
43
43
|
type: 'boolean',
|
|
44
44
|
description: 'Whether to detect stale L2 contexts for archival. Default: true'
|
|
45
|
+
},
|
|
46
|
+
include_l1_dedup: {
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
description: 'Check promotion candidates against existing L1 knowledge to mark duplicates. Default: true'
|
|
45
49
|
}
|
|
46
50
|
},
|
|
47
51
|
required: []
|
|
@@ -55,7 +59,8 @@ module KairosMcp
|
|
|
55
59
|
scan_result = scanner.scan(
|
|
56
60
|
scope: arguments['scope'] || config.dig('scan', 'default_scope') || 'l2',
|
|
57
61
|
since_session: arguments['since_session'],
|
|
58
|
-
include_archive_candidates: arguments.fetch('include_archive_candidates', true)
|
|
62
|
+
include_archive_candidates: arguments.fetch('include_archive_candidates', true),
|
|
63
|
+
include_l1_dedup: arguments.fetch('include_l1_dedup', true)
|
|
59
64
|
)
|
|
60
65
|
|
|
61
66
|
# Record findings on blockchain if non-empty
|
|
@@ -131,7 +136,9 @@ module KairosMcp
|
|
|
131
136
|
lines << "_No recurring patterns detected._"
|
|
132
137
|
else
|
|
133
138
|
promo.each do |c|
|
|
134
|
-
|
|
139
|
+
dedup_marker = c[:already_in_l1] ? " [already in L1: #{c[:l1_match]}]" : ''
|
|
140
|
+
confidence_str = c[:confidence] ? " (confidence: #{c[:confidence]})" : ''
|
|
141
|
+
lines << "- **#{c[:tag]}**: #{c[:session_count]} sessions (strength: #{c[:strength].round(2)})#{confidence_str}#{dedup_marker}"
|
|
135
142
|
end
|
|
136
143
|
end
|
|
137
144
|
lines << ""
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Introspection Guide
|
|
3
|
+
description: Usage guide for the introspection SkillSet — self-inspection, health scoring, and safety visibility
|
|
4
|
+
version: "0.1.0"
|
|
5
|
+
tags:
|
|
6
|
+
- introspection
|
|
7
|
+
- health
|
|
8
|
+
- safety
|
|
9
|
+
- blockchain
|
|
10
|
+
- maintenance
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Introspection Guide
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
The introspection SkillSet provides self-inspection capabilities for KairosChain.
|
|
18
|
+
It examines knowledge health, blockchain integrity, and safety mechanisms to produce
|
|
19
|
+
actionable reports and recommendations.
|
|
20
|
+
|
|
21
|
+
## Tools
|
|
22
|
+
|
|
23
|
+
### introspection_check
|
|
24
|
+
|
|
25
|
+
Full self-inspection combining all domains. Returns a consolidated report with
|
|
26
|
+
recommendations.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
introspection_check() # All domains, markdown format
|
|
30
|
+
introspection_check(format: "json") # JSON output
|
|
31
|
+
introspection_check(domains: ["health"]) # Health only
|
|
32
|
+
introspection_check(domains: ["blockchain"]) # Blockchain only
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### introspection_health
|
|
36
|
+
|
|
37
|
+
Focused L1 knowledge health scoring.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
introspection_health() # All entries
|
|
41
|
+
introspection_health(name: "my_knowledge") # Single entry
|
|
42
|
+
introspection_health(below_threshold: 0.5) # Only unhealthy entries
|
|
43
|
+
introspection_health(sort_by: "name") # Sort alphabetically
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### introspection_safety
|
|
47
|
+
|
|
48
|
+
Safety mechanism visibility across all layers.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
introspection_safety() # Returns 4-layer safety report
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Health Scoring
|
|
55
|
+
|
|
56
|
+
Health scores range from 0.0 (unhealthy) to 1.0 (healthy).
|
|
57
|
+
|
|
58
|
+
When Synoptis TrustScorer is available:
|
|
59
|
+
- **Trust score** (70% weight): Based on attestation count and quality
|
|
60
|
+
- **Staleness score** (30% weight): Based on file modification time
|
|
61
|
+
|
|
62
|
+
When TrustScorer is not available:
|
|
63
|
+
- **Staleness score only** (100%): Linear decay over configurable threshold
|
|
64
|
+
|
|
65
|
+
### Staleness Threshold
|
|
66
|
+
|
|
67
|
+
Default: 180 days. Configure in `config/introspection.yml`:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
introspection:
|
|
71
|
+
health:
|
|
72
|
+
staleness_days: 90 # More aggressive freshness requirement
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Safety Layers
|
|
76
|
+
|
|
77
|
+
The safety report covers four layers:
|
|
78
|
+
|
|
79
|
+
1. **L0 Approval Workflow**: Whether Kairos DSL approval_workflow skill is loaded
|
|
80
|
+
2. **Runtime RBAC**: Registered Safety policies (can_modify_l0, etc.)
|
|
81
|
+
3. **Agent Safety Gates**: Autonomous mode limits from agent.yml
|
|
82
|
+
4. **Blockchain Recording**: Chain integrity and block count
|
|
83
|
+
|
|
84
|
+
## Recommendations
|
|
85
|
+
|
|
86
|
+
The full check generates prioritized recommendations:
|
|
87
|
+
|
|
88
|
+
- **CRITICAL**: Blockchain integrity failure
|
|
89
|
+
- **HIGH**: Missing L0 approval workflow
|
|
90
|
+
- **MEDIUM**: Low health scores on individual knowledge entries
|