insika 0.9.0 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +237 -2
- data/README.md +5 -3
- data/bin/insika +3 -0
- data/bin/insika-router +0 -1
- data/docs/AGENTS.md +90 -7
- data/docs/ARCHITECTURE.md +12 -7
- data/docs/ARTIFACTS.md +33 -3
- data/docs/BENCHMARK.md +50 -7
- data/docs/CONTEXT.md +63 -0
- data/docs/CROSS-HARNESS-BENCH.md +157 -0
- data/docs/DEPLOY.md +57 -11
- data/docs/EMBEDDING.md +10 -0
- data/docs/EVALS.md +33 -4
- data/docs/FACTS.md +8 -6
- data/docs/HARVEST.md +8 -3
- data/docs/KNOWLEDGE.md +7 -0
- data/docs/MEDIA.md +6 -2
- data/docs/OBSERVABILITY.md +143 -22
- data/docs/PERFORMANCE.md +110 -0
- data/docs/PLUGINS.md +4 -4
- data/docs/POLICY.md +22 -13
- data/docs/RELEASING.md +5 -0
- data/docs/ROUTER.md +7 -0
- data/docs/RUBYLLM_2_MIGRATION.md +307 -0
- data/docs/SECURITY.md +7 -0
- data/docs/SHARED-CONVERSATIONS.md +40 -0
- data/docs/SKILLS.md +5 -3
- data/docs/STUDIO.md +122 -0
- data/docs/TOOLS.md +111 -9
- data/docs/operate.md +3 -0
- data/lib/insika/agent_defaults.rb +140 -0
- data/lib/insika/agent_file_store.rb +12 -0
- data/lib/insika/agent_profile.rb +78 -8
- data/lib/insika/artifact_signing.rb +11 -0
- data/lib/insika/chat_builder.rb +79 -25
- data/lib/insika/checkpoint.rb +6 -1
- data/lib/insika/checkpoint_store.rb +59 -7
- data/lib/insika/commands/agent_payload.rb +3 -3
- data/lib/insika/commands/delete_kit.rb +26 -0
- data/lib/insika/commands/gate_harvest.rb +3 -0
- data/lib/insika/commands/review_guardrail_flag.rb +38 -0
- data/lib/insika/commands/run_harvest.rb +17 -1
- data/lib/insika/commands/seed_session.rb +11 -1
- data/lib/insika/commands/send_message.rb +9 -3
- data/lib/insika/commands/update_settings.rb +23 -1
- data/lib/insika/commands/write_kit.rb +27 -0
- data/lib/insika/compaction.rb +33 -7
- data/lib/insika/config_store.rb +87 -7
- data/lib/insika/context/priority.rb +3 -0
- data/lib/insika/context/provider.rb +2 -2
- data/lib/insika/context/providers/knowledge.rb +25 -2
- data/lib/insika/context/providers/memory.rb +53 -4
- data/lib/insika/context/providers/pending_confirmation.rb +52 -0
- data/lib/insika/context/providers/prompt.rb +17 -4
- data/lib/insika/context/providers/session.rb +39 -3
- data/lib/insika/context/reranker.rb +75 -0
- data/lib/insika/distill.rb +22 -13
- data/lib/insika/doctor.rb +34 -4
- data/lib/insika/dsl/runtime.rb +36 -4
- data/lib/insika/dsl/server_boot.rb +4 -12
- data/lib/insika/dsl.rb +13 -5
- data/lib/insika/edge_limiter.rb +23 -15
- data/lib/insika/egress_guard.rb +11 -3
- data/lib/insika/env_schema.rb +33 -73
- data/lib/insika/evals/assertions.rb +171 -16
- data/lib/insika/evals/golden.rb +141 -4
- data/lib/insika/evals/judge.rb +19 -10
- data/lib/insika/evals/moderator_compare.rb +47 -0
- data/lib/insika/evals/report.rb +61 -9
- data/lib/insika/evals/runner.rb +44 -4
- data/lib/insika/evals/simulator.rb +10 -1
- data/lib/insika/evals/transport.rb +200 -0
- data/lib/insika/event_stream.rb +63 -7
- data/lib/insika/evidence.rb +29 -8
- data/lib/insika/executor.rb +668 -141
- data/lib/insika/harvest.rb +26 -14
- data/lib/insika/harvest_engine.rb +1 -0
- data/lib/insika/harvest_store.rb +12 -0
- data/lib/insika/http_client.rb +87 -4
- data/lib/insika/kits.rb +54 -0
- data/lib/insika/knowledge.rb +34 -20
- data/lib/insika/llm_configurator.rb +46 -4
- data/lib/insika/llm_provider_store.rb +1 -3
- data/lib/insika/llm_trace_store.rb +54 -0
- data/lib/insika/mcp_client.rb +27 -63
- data/lib/insika/mcp_live_tool.rb +55 -9
- data/lib/insika/mcp_store.rb +32 -1
- data/lib/insika/mcp_tool_registry.rb +69 -26
- data/lib/insika/media.rb +38 -25
- data/lib/insika/memory_store.rb +4 -3
- data/lib/insika/memory_trim.rb +60 -0
- data/lib/insika/middleware.rb +12 -3
- data/lib/insika/model_metrics_store.rb +285 -0
- data/lib/insika/model_registry.rb +110 -0
- data/lib/insika/model_resolver.rb +1 -1
- data/lib/insika/model_selection.rb +14 -21
- data/lib/insika/model_visible.rb +17 -6
- data/lib/insika/onboarding.rb +1 -0
- data/lib/insika/outcome_store.rb +1 -1
- data/lib/insika/overlay_tool_registry.rb +25 -2
- data/lib/insika/pack_importer.rb +54 -9
- data/lib/insika/packaging.rb +14 -5
- data/lib/insika/pending_action_store.rb +45 -18
- data/lib/insika/plugin/loader.rb +4 -2
- data/lib/insika/profile_source.rb +4 -0
- data/lib/insika/provider_error_classifier.rb +18 -3
- data/lib/insika/queue_policy.rb +7 -2
- data/lib/insika/refinement/proposer.rb +42 -17
- data/lib/insika/reliability.rb +12 -3
- data/lib/insika/router/app.rb +14 -5
- data/lib/insika/router/session_key.rb +35 -5
- data/lib/insika/safety/config.rb +20 -3
- data/lib/insika/safety/corpus.rb +10 -3
- data/lib/insika/safety/factory.rb +39 -1
- data/lib/insika/safety/input_guardrail.rb +21 -1
- data/lib/insika/safety/judge_moderator.rb +70 -0
- data/lib/insika/safety/output_validator.rb +29 -5
- data/lib/insika/safety/prompt_echo.rb +89 -0
- data/lib/insika/schema_guard.rb +53 -76
- data/lib/insika/server/app.rb +17 -8
- data/lib/insika/server/responses.rb +19 -1
- data/lib/insika/session_actor.rb +58 -28
- data/lib/insika/session_store.rb +159 -10
- data/lib/insika/settings_store.rb +47 -1
- data/lib/insika/shared_conversations.rb +305 -0
- data/lib/insika/skill_catalog.rb +28 -2
- data/lib/insika/skill_store.rb +6 -0
- data/lib/insika/soak/runner.rb +1 -1
- data/lib/insika/steer_injector.rb +7 -48
- data/lib/insika/store.rb +29 -1
- data/lib/insika/stores/json_value.rb +37 -0
- data/lib/insika/stores/memory.rb +22 -35
- data/lib/insika/stores/postgres.rb +295 -0
- data/lib/insika/stores/sqlite.rb +257 -43
- data/lib/insika/stores/turn_counter.rb +25 -0
- data/lib/insika/studio/app.rb +1453 -161
- data/lib/insika/studio/assets/dist/application.css +1 -1
- data/lib/insika/studio/assets/dist/application.js +25 -25
- data/lib/insika/studio/assets/dist/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- data/lib/insika/studio/assets/dist/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- data/lib/insika/studio/assets/dist/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
- data/lib/insika/studio/assets/dist/fonts/ibm-plex-sans-latin-wght-normal.woff2 +0 -0
- data/lib/insika/studio/forms.rb +171 -9
- data/lib/insika/studio/nav_icons.rb +3 -0
- data/lib/insika/studio/views/_agent_config_retrieval.erb +73 -0
- data/lib/insika/studio/views/_agent_picker.erb +15 -0
- data/lib/insika/studio/views/_agent_tab_config.erb +78 -242
- data/lib/insika/studio/views/_agent_tab_history.erb +56 -1
- data/lib/insika/studio/views/_agent_tab_performance.erb +34 -0
- data/lib/insika/studio/views/_agent_tab_prompts.erb +7 -9
- data/lib/insika/studio/views/_agent_tab_skills.erb +16 -7
- data/lib/insika/studio/views/_agent_tab_tools.erb +90 -0
- data/lib/insika/studio/views/_agents_master.erb +8 -12
- data/lib/insika/studio/views/_cfg_alerts.erb +4 -0
- data/lib/insika/studio/views/_cfg_budget.erb +20 -0
- data/lib/insika/studio/views/_cfg_distill.erb +26 -0
- data/lib/insika/studio/views/_cfg_followup.erb +30 -0
- data/lib/insika/studio/views/_cfg_grounding.erb +21 -0
- data/lib/insika/studio/views/_cfg_guardrails.erb +47 -0
- data/lib/insika/studio/views/_cfg_harvest.erb +35 -0
- data/lib/insika/studio/views/_cfg_reliability.erb +32 -0
- data/lib/insika/studio/views/_chats_master.erb +66 -0
- data/lib/insika/studio/views/_customers_master.erb +37 -0
- data/lib/insika/studio/views/_empty.erb +15 -0
- data/lib/insika/studio/views/_facts_master.erb +42 -0
- data/lib/insika/studio/views/_harvest_candidate_detail.erb +14 -0
- data/lib/insika/studio/views/_identity.erb +16 -0
- data/lib/insika/studio/views/_kpi.erb +17 -0
- data/lib/insika/studio/views/_message.erb +40 -2
- data/lib/insika/studio/views/_model_chart.erb +82 -0
- data/lib/insika/studio/views/_refinement_master.erb +38 -0
- data/lib/insika/studio/views/{tool_edit.erb → _tool_edit.erb} +32 -9
- data/lib/insika/studio/views/_tool_users.erb +17 -0
- data/lib/insika/studio/views/_why.erb +53 -0
- data/lib/insika/studio/views/agent_detail.erb +17 -10
- data/lib/insika/studio/views/agents.erb +28 -16
- data/lib/insika/studio/views/approvals.erb +16 -11
- data/lib/insika/studio/views/artifact.erb +20 -7
- data/lib/insika/studio/views/artifacts.erb +19 -14
- data/lib/insika/studio/views/chats.erb +22 -29
- data/lib/insika/studio/views/customer.erb +88 -43
- data/lib/insika/studio/views/customers.erb +16 -26
- data/lib/insika/studio/views/evals.erb +49 -31
- data/lib/insika/studio/views/fact_detail.erb +114 -0
- data/lib/insika/studio/views/facts.erb +15 -116
- data/lib/insika/studio/views/followups.erb +63 -52
- data/lib/insika/studio/views/funnel.erb +104 -43
- data/lib/insika/studio/views/harvest.erb +85 -59
- data/lib/insika/studio/views/home.erb +138 -23
- data/lib/insika/studio/views/inbox.erb +36 -0
- data/lib/insika/studio/views/kits.erb +176 -0
- data/lib/insika/studio/views/knowledge.erb +92 -80
- data/lib/insika/studio/views/layout.erb +42 -11
- data/lib/insika/studio/views/live.erb +45 -0
- data/lib/insika/studio/views/mcp.erb +43 -13
- data/lib/insika/studio/views/models.erb +187 -0
- data/lib/insika/studio/views/parity.erb +38 -32
- data/lib/insika/studio/views/playground.erb +13 -9
- data/lib/insika/studio/views/refinement.erb +15 -224
- data/lib/insika/studio/views/refinement_detail.erb +243 -0
- data/lib/insika/studio/views/session.erb +152 -117
- data/lib/insika/studio/views/settings.erb +86 -22
- data/lib/insika/studio/views/skills.erb +53 -19
- data/lib/insika/studio/views/system_files.erb +7 -7
- data/lib/insika/studio/views/task.erb +45 -15
- data/lib/insika/studio/views/tasks.erb +35 -18
- data/lib/insika/studio/views/tools.erb +62 -73
- data/lib/insika/task_actor.rb +4 -1
- data/lib/insika/task_store.rb +67 -1
- data/lib/insika/telemetry/pricing.rb +16 -14
- data/lib/insika/telemetry/recorder.rb +103 -9
- data/lib/insika/telemetry/ruby_llm_instrumenter.rb +110 -0
- data/lib/insika/telemetry.rb +12 -2
- data/lib/insika/templates/daily-digest/README.md +13 -0
- data/lib/insika/templates/daily-digest/agent.rb +22 -11
- data/lib/insika/testing/store_contract.rb +65 -0
- data/lib/insika/text_tool_call.rb +66 -0
- data/lib/insika/tick.rb +7 -1
- data/lib/insika/token_store.rb +5 -0
- data/lib/insika/tool_batch.rb +1 -1
- data/lib/insika/tool_catalog.rb +12 -2
- data/lib/insika/tool_definition.rb +10 -4
- data/lib/insika/tool_envelope.rb +165 -28
- data/lib/insika/tool_manifest.rb +1 -0
- data/lib/insika/tool_store.rb +3 -0
- data/lib/insika/tool_trace_store.rb +15 -8
- data/lib/insika/tool_usage_report.rb +2 -2
- data/lib/insika/tools/a2a_remote.rb +1 -1
- data/lib/insika/tools/agent_enum.rb +1 -1
- data/lib/insika/tools/confirm_pending.rb +112 -0
- data/lib/insika/tools/data_defined_tool.rb +23 -15
- data/lib/insika/tools/generate_image.rb +2 -2
- data/lib/insika/tools/load_knowledge.rb +7 -3
- data/lib/insika/tools/load_skill.rb +3 -3
- data/lib/insika/tools/present.rb +1 -8
- data/lib/insika/tools/remember.rb +7 -2
- data/lib/insika/tools/run_persona_eval.rb +3 -5
- data/lib/insika/tools/save_artifact.rb +16 -6
- data/lib/insika/tools/schedule_followup.rb +3 -3
- data/lib/insika/tools/stuck_signal.rb +3 -3
- data/lib/insika/tools/subagent.rb +5 -5
- data/lib/insika/tools/subagents.rb +3 -3
- data/lib/insika/tools/tool_search.rb +7 -5
- data/lib/insika/tools/tts.rb +2 -2
- data/lib/insika/tools/update_briefing.rb +4 -4
- data/lib/insika/turn_output.rb +2 -2
- data/lib/insika/turn_state.rb +18 -0
- data/lib/insika/turn_timing.rb +51 -14
- data/lib/insika/version.rb +1 -1
- data/lib/insika/wiring/graph.rb +81 -16
- data/lib/insika/workflow.rb +25 -64
- data/lib/insika.rb +19 -3
- metadata +69 -13
- data/lib/insika/commands/import_mcp_tools.rb +0 -48
- data/lib/insika/mcp_http_client.rb +0 -67
- data/lib/insika/mcp_tool_ingestor.rb +0 -165
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: insika
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Guilherme Lages Santos
|
|
@@ -24,33 +24,33 @@ dependencies:
|
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '2.0'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
27
|
+
name: json_schemer
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '2.5'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '2.5'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name: ruby_llm
|
|
41
|
+
name: ruby_llm
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '1
|
|
46
|
+
version: '2.1'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '1
|
|
53
|
+
version: '2.1'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: falcon
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -173,6 +173,7 @@ files:
|
|
|
173
173
|
- docs/BENCHMARK.md
|
|
174
174
|
- docs/CHANNELS.md
|
|
175
175
|
- docs/CONTEXT.md
|
|
176
|
+
- docs/CROSS-HARNESS-BENCH.md
|
|
176
177
|
- docs/DEMO.md
|
|
177
178
|
- docs/DEPLOY.md
|
|
178
179
|
- docs/EMBEDDING.md
|
|
@@ -184,17 +185,21 @@ files:
|
|
|
184
185
|
- docs/MEDIA.md
|
|
185
186
|
- docs/OBSERVABILITY.md
|
|
186
187
|
- docs/OUTCOMES.md
|
|
188
|
+
- docs/PERFORMANCE.md
|
|
187
189
|
- docs/PLUGINS.md
|
|
188
190
|
- docs/POLICY.md
|
|
189
191
|
- docs/REFINEMENT.md
|
|
190
192
|
- docs/RELEASING.md
|
|
191
193
|
- docs/ROUTER.md
|
|
194
|
+
- docs/RUBYLLM_2_MIGRATION.md
|
|
192
195
|
- docs/RUNNING-LOCAL.md
|
|
193
196
|
- docs/SANDBOX.md
|
|
194
197
|
- docs/SCHEDULING.md
|
|
195
198
|
- docs/SECURITY.md
|
|
199
|
+
- docs/SHARED-CONVERSATIONS.md
|
|
196
200
|
- docs/SKILLS.md
|
|
197
201
|
- docs/SOAK.md
|
|
202
|
+
- docs/STUDIO.md
|
|
198
203
|
- docs/TEMPLATES.md
|
|
199
204
|
- docs/TOOLS.md
|
|
200
205
|
- docs/WHY.md
|
|
@@ -221,6 +226,7 @@ files:
|
|
|
221
226
|
- docs/ship.md
|
|
222
227
|
- docs/start-here.md
|
|
223
228
|
- lib/insika.rb
|
|
229
|
+
- lib/insika/agent_defaults.rb
|
|
224
230
|
- lib/insika/agent_file_store.rb
|
|
225
231
|
- lib/insika/agent_profile.rb
|
|
226
232
|
- lib/insika/alert_dispatcher.rb
|
|
@@ -258,6 +264,7 @@ files:
|
|
|
258
264
|
- lib/insika/commands/delete_artifact.rb
|
|
259
265
|
- lib/insika/commands/delete_concept.rb
|
|
260
266
|
- lib/insika/commands/delete_data_tool.rb
|
|
267
|
+
- lib/insika/commands/delete_kit.rb
|
|
261
268
|
- lib/insika/commands/delete_llm_provider.rb
|
|
262
269
|
- lib/insika/commands/delete_mcp.rb
|
|
263
270
|
- lib/insika/commands/delete_skill.rb
|
|
@@ -268,7 +275,6 @@ files:
|
|
|
268
275
|
- lib/insika/commands/freeze_funnel_baseline.rb
|
|
269
276
|
- lib/insika/commands/gate_harvest.rb
|
|
270
277
|
- lib/insika/commands/gate_refinement.rb
|
|
271
|
-
- lib/insika/commands/import_mcp_tools.rb
|
|
272
278
|
- lib/insika/commands/import_tools.rb
|
|
273
279
|
- lib/insika/commands/issue_tenant_token.rb
|
|
274
280
|
- lib/insika/commands/judge_shadow_pairs.rb
|
|
@@ -288,6 +294,7 @@ files:
|
|
|
288
294
|
- lib/insika/commands/restore_data_tool.rb
|
|
289
295
|
- lib/insika/commands/restore_system_file.rb
|
|
290
296
|
- lib/insika/commands/resume_task.rb
|
|
297
|
+
- lib/insika/commands/review_guardrail_flag.rb
|
|
291
298
|
- lib/insika/commands/revoke_contact.rb
|
|
292
299
|
- lib/insika/commands/revoke_token.rb
|
|
293
300
|
- lib/insika/commands/rollback_harvest.rb
|
|
@@ -310,6 +317,7 @@ files:
|
|
|
310
317
|
- lib/insika/commands/write_concept.rb
|
|
311
318
|
- lib/insika/commands/write_data_tool.rb
|
|
312
319
|
- lib/insika/commands/write_golden.rb
|
|
320
|
+
- lib/insika/commands/write_kit.rb
|
|
313
321
|
- lib/insika/commands/write_skill.rb
|
|
314
322
|
- lib/insika/commands/write_system_file.rb
|
|
315
323
|
- lib/insika/compaction.rb
|
|
@@ -324,12 +332,14 @@ files:
|
|
|
324
332
|
- lib/insika/context/providers/fence_notice.rb
|
|
325
333
|
- lib/insika/context/providers/knowledge.rb
|
|
326
334
|
- lib/insika/context/providers/memory.rb
|
|
335
|
+
- lib/insika/context/providers/pending_confirmation.rb
|
|
327
336
|
- lib/insika/context/providers/prompt.rb
|
|
328
337
|
- lib/insika/context/providers/request.rb
|
|
329
338
|
- lib/insika/context/providers/session.rb
|
|
330
339
|
- lib/insika/context/providers/skill.rb
|
|
331
340
|
- lib/insika/context/providers/skill_trigger.rb
|
|
332
341
|
- lib/insika/context/providers/tool_search.rb
|
|
342
|
+
- lib/insika/context/reranker.rb
|
|
333
343
|
- lib/insika/context_trace_store.rb
|
|
334
344
|
- lib/insika/cron.rb
|
|
335
345
|
- lib/insika/delegation_store.rb
|
|
@@ -353,6 +363,7 @@ files:
|
|
|
353
363
|
- lib/insika/evals/baseline.rb
|
|
354
364
|
- lib/insika/evals/golden.rb
|
|
355
365
|
- lib/insika/evals/judge.rb
|
|
366
|
+
- lib/insika/evals/moderator_compare.rb
|
|
356
367
|
- lib/insika/evals/pairwise.rb
|
|
357
368
|
- lib/insika/evals/persona.rb
|
|
358
369
|
- lib/insika/evals/report.rb
|
|
@@ -384,24 +395,27 @@ files:
|
|
|
384
395
|
- lib/insika/hooks.rb
|
|
385
396
|
- lib/insika/http_client.rb
|
|
386
397
|
- lib/insika/inbound_log.rb
|
|
398
|
+
- lib/insika/kits.rb
|
|
387
399
|
- lib/insika/knowledge.rb
|
|
388
400
|
- lib/insika/knowledge_store.rb
|
|
389
401
|
- lib/insika/llm_configurator.rb
|
|
390
402
|
- lib/insika/llm_provider_store.rb
|
|
403
|
+
- lib/insika/llm_trace_store.rb
|
|
391
404
|
- lib/insika/loop_detector.rb
|
|
392
405
|
- lib/insika/mcp_client.rb
|
|
393
|
-
- lib/insika/mcp_http_client.rb
|
|
394
406
|
- lib/insika/mcp_json.rb
|
|
395
407
|
- lib/insika/mcp_live_tool.rb
|
|
396
408
|
- lib/insika/mcp_store.rb
|
|
397
|
-
- lib/insika/mcp_tool_ingestor.rb
|
|
398
409
|
- lib/insika/mcp_tool_registry.rb
|
|
399
410
|
- lib/insika/media.rb
|
|
400
411
|
- lib/insika/memory_audit_store.rb
|
|
401
412
|
- lib/insika/memory_store.rb
|
|
413
|
+
- lib/insika/memory_trim.rb
|
|
402
414
|
- lib/insika/message_origin.rb
|
|
403
415
|
- lib/insika/middleware.rb
|
|
416
|
+
- lib/insika/model_metrics_store.rb
|
|
404
417
|
- lib/insika/model_policy.rb
|
|
418
|
+
- lib/insika/model_registry.rb
|
|
405
419
|
- lib/insika/model_resolver.rb
|
|
406
420
|
- lib/insika/model_selection.rb
|
|
407
421
|
- lib/insika/model_visible.rb
|
|
@@ -451,9 +465,11 @@ files:
|
|
|
451
465
|
- lib/insika/safety/grounding_enforcer.rb
|
|
452
466
|
- lib/insika/safety/grounding_validator.rb
|
|
453
467
|
- lib/insika/safety/input_guardrail.rb
|
|
468
|
+
- lib/insika/safety/judge_moderator.rb
|
|
454
469
|
- lib/insika/safety/moderator.rb
|
|
455
470
|
- lib/insika/safety/output_filter.rb
|
|
456
471
|
- lib/insika/safety/output_validator.rb
|
|
472
|
+
- lib/insika/safety/prompt_echo.rb
|
|
457
473
|
- lib/insika/safety/safe_responses.rb
|
|
458
474
|
- lib/insika/sandbox.rb
|
|
459
475
|
- lib/insika/sandbox/boundary.rb
|
|
@@ -484,6 +500,7 @@ files:
|
|
|
484
500
|
- lib/insika/session_store.rb
|
|
485
501
|
- lib/insika/settings_store.rb
|
|
486
502
|
- lib/insika/shadow_pair_store.rb
|
|
503
|
+
- lib/insika/shared_conversations.rb
|
|
487
504
|
- lib/insika/shutdown.rb
|
|
488
505
|
- lib/insika/skill_catalog.rb
|
|
489
506
|
- lib/insika/skill_store.rb
|
|
@@ -493,24 +510,55 @@ files:
|
|
|
493
510
|
- lib/insika/spoken_transcript.rb
|
|
494
511
|
- lib/insika/steer_injector.rb
|
|
495
512
|
- lib/insika/store.rb
|
|
513
|
+
- lib/insika/stores/json_value.rb
|
|
496
514
|
- lib/insika/stores/memory.rb
|
|
515
|
+
- lib/insika/stores/postgres.rb
|
|
497
516
|
- lib/insika/stores/sqlite.rb
|
|
517
|
+
- lib/insika/stores/turn_counter.rb
|
|
498
518
|
- lib/insika/studio/app.rb
|
|
499
519
|
- lib/insika/studio/assets/dist/application.css
|
|
500
520
|
- lib/insika/studio/assets/dist/application.js
|
|
501
521
|
- lib/insika/studio/assets/dist/favicon.svg
|
|
522
|
+
- lib/insika/studio/assets/dist/fonts/ibm-plex-mono-latin-400-normal.woff2
|
|
523
|
+
- lib/insika/studio/assets/dist/fonts/ibm-plex-mono-latin-500-normal.woff2
|
|
524
|
+
- lib/insika/studio/assets/dist/fonts/ibm-plex-mono-latin-600-normal.woff2
|
|
525
|
+
- lib/insika/studio/assets/dist/fonts/ibm-plex-sans-latin-wght-normal.woff2
|
|
502
526
|
- lib/insika/studio/forms.rb
|
|
503
527
|
- lib/insika/studio/nav_icons.rb
|
|
528
|
+
- lib/insika/studio/views/_agent_config_retrieval.erb
|
|
529
|
+
- lib/insika/studio/views/_agent_picker.erb
|
|
504
530
|
- lib/insika/studio/views/_agent_tab_cache.erb
|
|
505
531
|
- lib/insika/studio/views/_agent_tab_config.erb
|
|
506
532
|
- lib/insika/studio/views/_agent_tab_history.erb
|
|
507
533
|
- lib/insika/studio/views/_agent_tab_loops.erb
|
|
508
534
|
- lib/insika/studio/views/_agent_tab_memory.erb
|
|
509
535
|
- lib/insika/studio/views/_agent_tab_outcomes.erb
|
|
536
|
+
- lib/insika/studio/views/_agent_tab_performance.erb
|
|
510
537
|
- lib/insika/studio/views/_agent_tab_prompts.erb
|
|
511
538
|
- lib/insika/studio/views/_agent_tab_skills.erb
|
|
539
|
+
- lib/insika/studio/views/_agent_tab_tools.erb
|
|
512
540
|
- lib/insika/studio/views/_agents_master.erb
|
|
541
|
+
- lib/insika/studio/views/_cfg_alerts.erb
|
|
542
|
+
- lib/insika/studio/views/_cfg_budget.erb
|
|
543
|
+
- lib/insika/studio/views/_cfg_distill.erb
|
|
544
|
+
- lib/insika/studio/views/_cfg_followup.erb
|
|
545
|
+
- lib/insika/studio/views/_cfg_grounding.erb
|
|
546
|
+
- lib/insika/studio/views/_cfg_guardrails.erb
|
|
547
|
+
- lib/insika/studio/views/_cfg_harvest.erb
|
|
548
|
+
- lib/insika/studio/views/_cfg_reliability.erb
|
|
549
|
+
- lib/insika/studio/views/_chats_master.erb
|
|
550
|
+
- lib/insika/studio/views/_customers_master.erb
|
|
551
|
+
- lib/insika/studio/views/_empty.erb
|
|
552
|
+
- lib/insika/studio/views/_facts_master.erb
|
|
553
|
+
- lib/insika/studio/views/_harvest_candidate_detail.erb
|
|
554
|
+
- lib/insika/studio/views/_identity.erb
|
|
555
|
+
- lib/insika/studio/views/_kpi.erb
|
|
513
556
|
- lib/insika/studio/views/_message.erb
|
|
557
|
+
- lib/insika/studio/views/_model_chart.erb
|
|
558
|
+
- lib/insika/studio/views/_refinement_master.erb
|
|
559
|
+
- lib/insika/studio/views/_tool_edit.erb
|
|
560
|
+
- lib/insika/studio/views/_tool_users.erb
|
|
561
|
+
- lib/insika/studio/views/_why.erb
|
|
514
562
|
- lib/insika/studio/views/agent_detail.erb
|
|
515
563
|
- lib/insika/studio/views/agents.erb
|
|
516
564
|
- lib/insika/studio/views/approvals.erb
|
|
@@ -520,26 +568,31 @@ files:
|
|
|
520
568
|
- lib/insika/studio/views/customer.erb
|
|
521
569
|
- lib/insika/studio/views/customers.erb
|
|
522
570
|
- lib/insika/studio/views/evals.erb
|
|
571
|
+
- lib/insika/studio/views/fact_detail.erb
|
|
523
572
|
- lib/insika/studio/views/facts.erb
|
|
524
573
|
- lib/insika/studio/views/followups.erb
|
|
525
574
|
- lib/insika/studio/views/funnel.erb
|
|
526
575
|
- lib/insika/studio/views/harvest.erb
|
|
527
576
|
- lib/insika/studio/views/home.erb
|
|
577
|
+
- lib/insika/studio/views/inbox.erb
|
|
578
|
+
- lib/insika/studio/views/kits.erb
|
|
528
579
|
- lib/insika/studio/views/knowledge.erb
|
|
529
580
|
- lib/insika/studio/views/layout.erb
|
|
581
|
+
- lib/insika/studio/views/live.erb
|
|
530
582
|
- lib/insika/studio/views/login.erb
|
|
531
583
|
- lib/insika/studio/views/mcp.erb
|
|
584
|
+
- lib/insika/studio/views/models.erb
|
|
532
585
|
- lib/insika/studio/views/not_found.erb
|
|
533
586
|
- lib/insika/studio/views/parity.erb
|
|
534
587
|
- lib/insika/studio/views/playground.erb
|
|
535
588
|
- lib/insika/studio/views/refinement.erb
|
|
589
|
+
- lib/insika/studio/views/refinement_detail.erb
|
|
536
590
|
- lib/insika/studio/views/session.erb
|
|
537
591
|
- lib/insika/studio/views/settings.erb
|
|
538
592
|
- lib/insika/studio/views/skills.erb
|
|
539
593
|
- lib/insika/studio/views/system_files.erb
|
|
540
594
|
- lib/insika/studio/views/task.erb
|
|
541
595
|
- lib/insika/studio/views/tasks.erb
|
|
542
|
-
- lib/insika/studio/views/tool_edit.erb
|
|
543
596
|
- lib/insika/studio/views/tools.erb
|
|
544
597
|
- lib/insika/subagent_graph.rb
|
|
545
598
|
- lib/insika/system_file_store.rb
|
|
@@ -548,6 +601,7 @@ files:
|
|
|
548
601
|
- lib/insika/telemetry.rb
|
|
549
602
|
- lib/insika/telemetry/pricing.rb
|
|
550
603
|
- lib/insika/telemetry/recorder.rb
|
|
604
|
+
- lib/insika/telemetry/ruby_llm_instrumenter.rb
|
|
551
605
|
- lib/insika/templates.rb
|
|
552
606
|
- lib/insika/templates/browser-agent/README.md
|
|
553
607
|
- lib/insika/templates/browser-agent/agent.rb
|
|
@@ -562,6 +616,7 @@ files:
|
|
|
562
616
|
- lib/insika/templates/travel-planner/README.md
|
|
563
617
|
- lib/insika/templates/travel-planner/agent.rb
|
|
564
618
|
- lib/insika/testing/store_contract.rb
|
|
619
|
+
- lib/insika/text_tool_call.rb
|
|
565
620
|
- lib/insika/tick.rb
|
|
566
621
|
- lib/insika/timezone.rb
|
|
567
622
|
- lib/insika/token_estimator.rb
|
|
@@ -580,6 +635,7 @@ files:
|
|
|
580
635
|
- lib/insika/tools/a2a_remote.rb
|
|
581
636
|
- lib/insika/tools/agent_enum.rb
|
|
582
637
|
- lib/insika/tools/concurrency.rb
|
|
638
|
+
- lib/insika/tools/confirm_pending.rb
|
|
583
639
|
- lib/insika/tools/data_defined_tool.rb
|
|
584
640
|
- lib/insika/tools/generate_image.rb
|
|
585
641
|
- lib/insika/tools/load_knowledge.rb
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "time"
|
|
4
|
-
|
|
5
|
-
module Insika
|
|
6
|
-
module Commands
|
|
7
|
-
# Control command: LIVE MCP ingestion. Receives the NAME of
|
|
8
|
-
# an MCP instance, delegates to McpToolIngestor (discovers via an injectable client
|
|
9
|
-
# -> builds the manifest -> reuses :import_tools: batch upsert + hot reload)
|
|
10
|
-
# and returns the per-tool report in the shape of :import_tools, plus
|
|
11
|
-
# `instance:`. Idempotent (re-ingesting reconciles). A per-tool failure is isolated
|
|
12
|
-
# in `errors[]` (R4); an absent/disabled/url-less instance raises.
|
|
13
|
-
# -> { instance, version, created: [names], updated: [names], errors: [{tool,error}] }
|
|
14
|
-
class ImportMcpTools
|
|
15
|
-
def initialize(ingestor:, event_stream:)
|
|
16
|
-
@ingestor = ingestor
|
|
17
|
-
@event_stream = event_stream
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def call(command)
|
|
21
|
-
p = symbolize(command.payload)
|
|
22
|
-
name = Insika::Coercion.presence(p[:name]) ||
|
|
23
|
-
(raise Insika::ValidationError, "import_mcp_tools: 'name' (MCP instance) is required")
|
|
24
|
-
|
|
25
|
-
report = @ingestor.ingest(name)
|
|
26
|
-
emit(report)
|
|
27
|
-
report
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
# Emits only COUNTS + the instance name (never headers/env/secrets): 0 leakage.
|
|
33
|
-
def emit(report)
|
|
34
|
-
@event_stream.emit(Insika::Event.new(
|
|
35
|
-
type: :mcp_tools_imported,
|
|
36
|
-
data: { instance: report[:instance],
|
|
37
|
-
created: report[:created].size, updated: report[:updated].size,
|
|
38
|
-
errors: report[:errors].size },
|
|
39
|
-
meta: { at: Time.now.utc.iso8601 }
|
|
40
|
-
))
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def symbolize(payload)
|
|
44
|
-
(payload || {}).each_with_object({}) { |(k, v), acc| acc[k.to_sym] = v }
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
|
-
module Insika
|
|
6
|
-
# MINIMAL MCP client over HTTP JSON-RPC. Discovers the tools
|
|
7
|
-
# of an MCP instance with HTTP transport by making a JSON-RPC 2.0 `tools/list`
|
|
8
|
-
# POST to the instance endpoint, behind the EgressGuard (SSRF — the url comes
|
|
9
|
-
# from editable config). It is the DEFAULT client injected into the
|
|
10
|
-
# McpToolIngestor; tests pass a Fake (duck-typed) in its place.
|
|
11
|
-
#
|
|
12
|
-
# Contract (MCP client duck-type): `#list_tools -> [{name, description,
|
|
13
|
-
# inputSchema}]` — the same MCP envelope that the ToolManifest adapter normalizes.
|
|
14
|
-
#
|
|
15
|
-
# SCOPE (bounded): only the minimal handshake of ONE stateless `tools/list`
|
|
16
|
-
# POST. Does NOT implement the full MCP session lifecycle (initialize/protocol
|
|
17
|
-
# negotiation/session-id/notifications) nor the stdio transport — that is the
|
|
18
|
-
# "real MCP transport", later work (out-of-scope, see spec). It serves
|
|
19
|
-
# simple HTTP MCP servers (direct JSON-RPC) and proves the ingestion seam.
|
|
20
|
-
class McpHttpClient
|
|
21
|
-
JSONRPC_VERSION = "2.0"
|
|
22
|
-
|
|
23
|
-
def initialize(url:, http: Insika::HttpClient.new, egress: Insika::EgressGuard,
|
|
24
|
-
egress_options: {}, headers: {}, timeout: nil)
|
|
25
|
-
@url = url.to_s
|
|
26
|
-
@http = http
|
|
27
|
-
@egress = egress
|
|
28
|
-
@egress_options = egress_options
|
|
29
|
-
@headers = { "Content-Type" => "application/json", "Accept" => "application/json" }.merge(headers || {})
|
|
30
|
-
@timeout = timeout
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# -> [ { "name", "description", "inputSchema" } ]. Raises Insika::Error on
|
|
34
|
-
# blocked egress, HTTP != 2xx, invalid JSON or a JSON-RPC error.
|
|
35
|
-
def list_tools
|
|
36
|
-
reason = @egress.violation(@url, **@egress_options)
|
|
37
|
-
raise Insika::Error, "MCP target blocked: #{reason}" if reason
|
|
38
|
-
|
|
39
|
-
result = @http.request(method: "POST", url: @url, headers: @headers,
|
|
40
|
-
body: request_body("tools/list", {}), timeout: @timeout)
|
|
41
|
-
Array(rpc_result(result).fetch("tools", []))
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
def request_body(method, params)
|
|
47
|
-
JSON.generate(jsonrpc: JSONRPC_VERSION, id: 1, method: method, params: params)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Validates the JSON-RPC envelope and returns `result` (Hash). HTTP/parse/error -> Error.
|
|
51
|
-
def rpc_result(result)
|
|
52
|
-
status = result[:status].to_i
|
|
53
|
-
raise Insika::Error, "MCP HTTP #{status}: #{result[:body].to_s[0, 200]}" if status >= 400
|
|
54
|
-
|
|
55
|
-
parsed = begin
|
|
56
|
-
JSON.parse(result[:body].to_s)
|
|
57
|
-
rescue JSON::ParserError => e
|
|
58
|
-
raise Insika::Error, "MCP response is not JSON: #{e.message}"
|
|
59
|
-
end
|
|
60
|
-
if (err = parsed["error"])
|
|
61
|
-
raise Insika::Error, "MCP JSON-RPC error: #{err["message"] || err.inspect}"
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
parsed["result"] || {}
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
|
-
module Insika
|
|
6
|
-
# LIVE MCP ingestion (/ spec): discovers the tools of an
|
|
7
|
-
# MCP instance at RUNTIME (no hand-written manifest) and ingests them as
|
|
8
|
-
# data-tools. Given an McpStore instance + an INJECTABLE MCP client
|
|
9
|
-
# (duck-typed: `#list_tools -> [{name, description, inputSchema}]`), it builds a
|
|
10
|
-
# ToolManifest and REUSES the ingestion path (the:import_tools Command:
|
|
11
|
-
# batch upsert into the ToolStore + hot reload + per-tool report + partial-
|
|
12
|
-
# failure isolation R4). The ToolManifest MCP adapter (`inputSchema`) is reused
|
|
13
|
-
# — no schema parsing here.
|
|
14
|
-
#
|
|
15
|
-
# GENERIC: nothing here mentions a consumer/gateway. The MCP instance is DATA in the store.
|
|
16
|
-
#
|
|
17
|
-
# BINDING STRATEGY (this stage's choice, bounded):
|
|
18
|
-
# Each discovered tool becomes an HTTP data-tool that makes a JSON-RPC 2.0
|
|
19
|
-
# `tools/call` POST to the instance endpoint (url). The tool name is resolved
|
|
20
|
-
# at INGESTION (literal in the body); the model's arguments come in as `{{param}}`
|
|
21
|
-
# per TOP-level property of the inputSchema (with quoting by type — strings
|
|
22
|
-
# quoted, others raw, via the DataDefinedTool :body encode). This way the tool
|
|
23
|
-
# runs through the SAME HTTP path as the other data-tools (egress guard, secret
|
|
24
|
-
# headers, hot reload) — no new execution code.
|
|
25
|
-
#
|
|
26
|
-
# Each tool gets `group: "mcp:<instance>"` so the per-group gating
|
|
27
|
-
# (tools_allow_groups) works for free.
|
|
28
|
-
#
|
|
29
|
-
# DEFERRED / OUT-OF-SCOPE (documented — spec):
|
|
30
|
-
# - Real MCP transport: only instances with a `url` (http transport) are ingestible;
|
|
31
|
-
# stdio has no HTTP endpoint -> raises a clear error (later work).
|
|
32
|
-
# - MCP session lifecycle (initialize/negotiation/session-id/notifications) and the
|
|
33
|
-
# UNWRAP of the `tools/call` response (`{content:[{type,text}]}`) — the binding
|
|
34
|
-
# makes a stateless POST and returns the raw body (extract body_raw). A server
|
|
35
|
-
# that requires the handshake before answering `tools/list`/`tools/call` is not
|
|
36
|
-
# reachable yet — that IS the real transport, not this minimal client.
|
|
37
|
-
# - Tools whose name/top-level property is outside the ToolDefinition NAME_RE
|
|
38
|
-
# (uppercase/hyphens) are ISOLATED into `errors[]` by the import (R4).
|
|
39
|
-
#
|
|
40
|
-
# CREDENTIAL INJECTION: the instance `env` (Hash) is sent verbatim as HTTP
|
|
41
|
-
# headers, on BOTH the discovery request (`tools/list`, this file) and every
|
|
42
|
-
# ingested tool's `tools/call` binding (`build_manifest`'s `defaults.headers`)
|
|
43
|
-
# — literal header-name -> value, the same convention as a data-tool's own
|
|
44
|
-
# `secret_headers` (the operator types the full value, "Bearer xxx" included;
|
|
45
|
-
# no magic prefixing). An instance with no `env` sends the bare
|
|
46
|
-
# `Content-Type` header only, byte-for-byte what shipped before this.
|
|
47
|
-
class McpToolIngestor
|
|
48
|
-
def initialize(mcp_store:, import_tools:, client_factory: nil)
|
|
49
|
-
@mcp_store = mcp_store
|
|
50
|
-
@import_tools = import_tools
|
|
51
|
-
# Per-instance client factory (default: minimal JSON-RPC HTTP client).
|
|
52
|
-
# Injectable for tests (Fake) and to swap for a real transport later.
|
|
53
|
-
@client_factory = client_factory || method(:default_client)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Discovers + ingests the tools of instance `name`. `client` is injectable
|
|
57
|
-
# (Fake in tests); absent -> the factory builds one from the record. -> the
|
|
58
|
-
# import_tools report + `instance:` ({ instance:, version:, created:, updated:, errors: }).
|
|
59
|
-
def ingest(name, client: nil)
|
|
60
|
-
manifest = manifest_for(name, client: client)
|
|
61
|
-
report = @import_tools.call(Insika::Command.build(:import_tools, manifest, transport: :internal))
|
|
62
|
-
report.merge(instance: name.to_s)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Discovers the tools and builds the manifest Hash (without ingesting) — isolable for testing.
|
|
66
|
-
def manifest_for(name, client: nil)
|
|
67
|
-
record = @mcp_store.get_raw(name.to_s)
|
|
68
|
-
raise Insika::NotFoundError, "MCP instance '#{name}' not found" if record.nil?
|
|
69
|
-
raise Insika::ValidationError, "MCP instance '#{name}' is disabled" unless record["enabled"]
|
|
70
|
-
|
|
71
|
-
url = presence(record["url"])
|
|
72
|
-
if url.nil?
|
|
73
|
-
raise Insika::ValidationError,
|
|
74
|
-
"MCP instance '#{name}' has no url: live ingestion requires HTTP transport " \
|
|
75
|
-
"(stdio is later work)"
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
tools = Array((client || @client_factory.call(record)).list_tools)
|
|
79
|
-
build_manifest(name.to_s, url, tools, env_headers(record))
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
private
|
|
83
|
-
|
|
84
|
-
# `headers` (an http/sse instance's credentials — `env` reads
|
|
85
|
-
# as `headers` for these transports via McpStore's migration-on-read, see
|
|
86
|
-
# McpStore#migrate_legacy_headers) -> literal HTTP headers (header-NAME ->
|
|
87
|
-
# value; the value carries its own scheme, e.g. "Bearer xxx" — no magic
|
|
88
|
-
# prefixing). NOT `secret_headers`: ToolManifest's own rule for a secret
|
|
89
|
-
# header requires a `{{secret.*}}` reference, which is the opposite of what
|
|
90
|
-
# a literal credential is — so an MCP instance's headers are NOT masked in
|
|
91
|
-
# a tool's edit page in /studio/tools, unlike a hand-authored data-tool's
|
|
92
|
-
# Authorization header. Acceptable for now (Studio is already
|
|
93
|
-
# operator-gated); a real fix routes this through the deployment's own
|
|
94
|
-
# {{secret.*}}/{{env.*}} resolution instead — later work.
|
|
95
|
-
def env_headers(record)
|
|
96
|
-
(record["headers"] || {}).each_with_object({}) { |(k, v), acc| acc[k.to_s] = v.to_s }
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def build_manifest(name, url, tools, headers)
|
|
100
|
-
{
|
|
101
|
-
"version" => 1,
|
|
102
|
-
"defaults" => {
|
|
103
|
-
"method" => "POST",
|
|
104
|
-
"headers" => { "Content-Type" => "application/json" }.merge(headers),
|
|
105
|
-
"response" => { "extract" => "body_raw" },
|
|
106
|
-
"group" => "mcp:#{name}"
|
|
107
|
-
},
|
|
108
|
-
"tools" => tools.map { |raw| tool_entry(name, url, stringify(raw)) }
|
|
109
|
-
}
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Raw MCP entry -> manifest entry (MCP `inputSchema` envelope + JSON-RPC
|
|
113
|
-
# binding). ToolManifest normalizes the `inputSchema` (MCP adapter) and
|
|
114
|
-
# inherits the defaults; `group` falls through from the defaults.
|
|
115
|
-
def tool_entry(name, url, raw)
|
|
116
|
-
tool_name = raw["name"]
|
|
117
|
-
input_schema = raw["inputSchema"] || {}
|
|
118
|
-
{
|
|
119
|
-
"name" => tool_name,
|
|
120
|
-
"description" => presence(raw["description"]) || "Tool '#{tool_name}' from MCP server '#{name}'.",
|
|
121
|
-
"inputSchema" => input_schema,
|
|
122
|
-
"url" => url,
|
|
123
|
-
"side_effect" => true, # a tools/call is a side effect (checkpoint/skip-on-resume)
|
|
124
|
-
"body" => jsonrpc_call_body(tool_name, input_schema)
|
|
125
|
-
}
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# JSON-RPC 2.0 `tools/call` body. `name` literal (resolved at ingestion);
|
|
129
|
-
# `arguments` per TOP-level property of the inputSchema, with `{{param}}` that
|
|
130
|
-
# the DataDefinedTool interpolates at TURN time.
|
|
131
|
-
def jsonrpc_call_body(tool_name, input_schema)
|
|
132
|
-
%({"jsonrpc":"2.0","id":1,"method":"tools/call",) +
|
|
133
|
-
%("params":{"name":#{JSON.generate(tool_name)},"arguments":#{arguments_fragment(input_schema)}}})
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# -> "{...}" JSON with one placeholder per top-level property. Strings are
|
|
137
|
-
# quoted (the DataDefinedTool :body encode returns the escaped content WITHOUT
|
|
138
|
-
# quotes); other types raw (the encode returns value.to_json).
|
|
139
|
-
def arguments_fragment(input_schema)
|
|
140
|
-
props = (input_schema["properties"] || input_schema[:properties] || {})
|
|
141
|
-
return "{}" if props.nil? || props.empty?
|
|
142
|
-
|
|
143
|
-
pairs = props.map do |key, spec|
|
|
144
|
-
type = stringify(spec)["type"].to_s
|
|
145
|
-
placeholder = type == "string" ? %("{{#{key}}}") : "{{#{key}}}"
|
|
146
|
-
%(#{JSON.generate(key.to_s)}:#{placeholder})
|
|
147
|
-
end
|
|
148
|
-
"{#{pairs.join(',')}}"
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def default_client(record)
|
|
152
|
-
Insika::McpHttpClient.new(url: record["url"], headers: env_headers(record))
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def presence(str) = Insika::Coercion.presence(str)
|
|
156
|
-
|
|
157
|
-
def stringify(obj)
|
|
158
|
-
case obj
|
|
159
|
-
when Hash then obj.each_with_object({}) { |(k, v), acc| acc[k.to_s] = stringify(v) }
|
|
160
|
-
when Array then obj.map { |v| stringify(v) }
|
|
161
|
-
else obj
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
end
|