insika 0.2.0 → 0.7.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 +325 -17
- data/README.md +52 -11
- data/bin/insika +895 -8
- data/bin/insika-router +87 -0
- data/docs/AGENTS.md +229 -163
- data/docs/API.md +56 -0
- data/docs/ARCHITECTURE.md +3 -2
- data/docs/ARTIFACTS.md +95 -0
- data/docs/BENCHMARK.md +2 -2
- data/docs/CHANNELS.md +111 -21
- data/docs/CONTEXT.md +132 -19
- data/docs/DEMO.md +80 -0
- data/docs/DEPLOY.md +75 -4
- data/docs/EMBEDDING.md +1 -1
- data/docs/EVALS.md +146 -2
- data/docs/FACTS.md +135 -0
- data/docs/HARVEST.md +116 -0
- data/docs/KNOWLEDGE.md +290 -0
- data/docs/LOADTEST.md +16 -10
- data/docs/MEDIA.md +128 -0
- data/docs/OBSERVABILITY.md +65 -6
- data/docs/OUTCOMES.md +137 -0
- data/docs/PLUGINS.md +51 -6
- data/docs/POLICY.md +216 -0
- data/docs/REFINEMENT.md +17 -12
- data/docs/RELEASING.md +27 -0
- data/docs/ROUTER.md +213 -0
- data/docs/RUNNING-LOCAL.md +6 -6
- data/docs/SCHEDULING.md +121 -0
- data/docs/SECURITY.md +79 -2
- data/docs/SKILLS.md +13 -4
- data/docs/SOAK.md +127 -0
- data/docs/TEMPLATES.md +134 -0
- data/docs/TOOLS.md +220 -27
- data/docs/WHY.md +1 -1
- data/docs/WORKFLOWS.md +2 -2
- data/docs/_includes/head_custom.html +5 -0
- data/docs/_includes/title.html +13 -0
- data/docs/_sass/color_schemes/insika.scss +32 -0
- data/docs/_sass/custom/custom.scss +199 -0
- data/docs/_sass/custom/setup.scss +26 -0
- data/docs/assets/img/favicon.svg +7 -0
- data/docs/assets/img/insika-mark.svg +7 -0
- data/docs/core-concepts.md +21 -0
- data/docs/domain.md +115 -0
- data/docs/improve.md +20 -0
- data/docs/index.md +9 -6
- data/docs/integrate.md +20 -0
- data/docs/operate.md +13 -6
- data/docs/prompts/ADD-TOOL.md +118 -0
- data/docs/prompts/DIAGNOSE-TURN.md +65 -0
- data/docs/prompts/GO-LIVE.md +138 -0
- data/docs/prompts/RUN-EXAMPLES.md +70 -0
- data/docs/reference.md +19 -0
- data/docs/ship.md +10 -2
- data/docs/start-here.md +18 -0
- data/lib/insika/agent_profile.rb +202 -10
- data/lib/insika/artifact_signing.rb +82 -0
- data/lib/insika/artifact_store.rb +160 -0
- data/lib/insika/balloon_splitter.rb +102 -0
- data/lib/insika/budget_ledger.rb +34 -3
- data/lib/insika/cache_series_store.rb +49 -0
- data/lib/insika/channel_delivery.rb +119 -14
- data/lib/insika/channels/relay.rb +77 -3
- data/lib/insika/channels/web.rb +2 -2
- data/lib/insika/chat_builder.rb +105 -2
- data/lib/insika/checkpoint_store.rb +16 -0
- data/lib/insika/commands/agent_payload.rb +3 -3
- data/lib/insika/commands/backfill_knowledge.rb +145 -0
- data/lib/insika/commands/cancel_followup.rb +49 -0
- data/lib/insika/commands/delete_artifact.rb +35 -0
- data/lib/insika/commands/delete_concept.rb +34 -0
- data/lib/insika/commands/delete_mcp.rb +6 -2
- data/lib/insika/commands/delete_tenant_data.rb +107 -0
- data/lib/insika/commands/export_customer_memory.rb +48 -0
- data/lib/insika/commands/forget_customer.rb +117 -0
- data/lib/insika/commands/freeze_funnel_baseline.rb +113 -0
- data/lib/insika/commands/gate_harvest.rb +138 -0
- data/lib/insika/commands/gate_refinement.rb +1 -1
- data/lib/insika/commands/judge_shadow_pairs.rb +124 -0
- data/lib/insika/commands/memory_forget_fact.rb +20 -4
- data/lib/insika/commands/memory_put_fact.rb +23 -4
- data/lib/insika/commands/promote_harvest.rb +130 -0
- data/lib/insika/commands/record_outcome.rb +46 -0
- data/lib/insika/commands/record_shadow_reply.rb +68 -0
- data/lib/insika/commands/refresh_mcp_tools.rb +47 -0
- data/lib/insika/commands/reject_harvest.rb +38 -0
- data/lib/insika/commands/resolve_proposal.rb +108 -0
- data/lib/insika/commands/restore_concept.rb +34 -0
- data/lib/insika/commands/revoke_contact.rb +49 -0
- data/lib/insika/commands/rollback_harvest.rb +86 -0
- data/lib/insika/commands/run_distillation.rb +186 -0
- data/lib/insika/commands/run_harvest.rb +393 -0
- data/lib/insika/commands/seed_demo_data.rb +31 -0
- data/lib/insika/commands/send_message.rb +103 -6
- data/lib/insika/commands/session_purge.rb +67 -0
- data/lib/insika/commands/upsert_mcp.rb +6 -3
- data/lib/insika/commands/write_concept.rb +57 -0
- data/lib/insika/contact_store.rb +183 -0
- data/lib/insika/context/builder.rb +21 -3
- data/lib/insika/context/fragment.rb +7 -3
- data/lib/insika/context/priority.rb +5 -0
- data/lib/insika/context/provider.rb +17 -3
- data/lib/insika/context/providers/briefing.rb +96 -0
- data/lib/insika/context/providers/knowledge.rb +108 -0
- data/lib/insika/context/providers/memory.rb +16 -7
- data/lib/insika/context/providers/prompt.rb +57 -23
- data/lib/insika/context/providers/skill.rb +2 -0
- data/lib/insika/context/providers/tool_search.rb +2 -0
- data/lib/insika/context_trace_store.rb +38 -2
- data/lib/insika/cron.rb +189 -0
- data/lib/insika/demo/agent_attrs.rb +43 -0
- data/lib/insika/demo/golden_cases.rb +81 -0
- data/lib/insika/demo/seeder.rb +336 -0
- data/lib/insika/distill.rb +224 -0
- data/lib/insika/distill_engine.rb +169 -0
- data/lib/insika/doctor.rb +889 -11
- data/lib/insika/dsl/definition.rb +3 -2
- data/lib/insika/dsl/runtime.rb +69 -82
- data/lib/insika/dsl/server_boot.rb +92 -1
- data/lib/insika/dsl/system.rb +10 -2
- data/lib/insika/dsl.rb +186 -5
- data/lib/insika/edge_limiter.rb +28 -7
- data/lib/insika/env_schema.rb +25 -3
- data/lib/insika/errors.rb +11 -0
- data/lib/insika/evals/assertions.rb +3 -2
- data/lib/insika/evals/golden.rb +41 -4
- data/lib/insika/evals/judge.rb +47 -2
- data/lib/insika/evals/pairwise.rb +29 -6
- data/lib/insika/evals/persona.rb +98 -0
- data/lib/insika/evals/runner.rb +9 -0
- data/lib/insika/evals/simulator.rb +225 -0
- data/lib/insika/evals/transport.rb +83 -1
- data/lib/insika/event_stream.rb +10 -0
- data/lib/insika/evidence.rb +183 -0
- data/lib/insika/executor.rb +869 -76
- data/lib/insika/followup_engine.rb +207 -0
- data/lib/insika/followup_policy.rb +198 -0
- data/lib/insika/followup_store.rb +306 -0
- data/lib/insika/funnel_declaration.rb +106 -0
- data/lib/insika/funnel_fold.rb +179 -0
- data/lib/insika/funnel_store.rb +163 -0
- data/lib/insika/golden_store.rb +17 -2
- data/lib/insika/grounding/matcher.rb +69 -0
- data/lib/insika/grounding.rb +44 -0
- data/lib/insika/harvest/conversion_gate.rb +159 -0
- data/lib/insika/harvest/criterion.rb +98 -0
- data/lib/insika/harvest/gate.rb +194 -0
- data/lib/insika/harvest/negative_list.rb +199 -0
- data/lib/insika/harvest.rb +241 -0
- data/lib/insika/harvest_engine.rb +193 -0
- data/lib/insika/harvest_store.rb +548 -0
- data/lib/insika/knowledge.rb +680 -0
- data/lib/insika/knowledge_store.rb +140 -0
- data/lib/insika/mcp_client.rb +94 -0
- data/lib/insika/mcp_json.rb +74 -0
- data/lib/insika/mcp_live_tool.rb +43 -0
- data/lib/insika/mcp_store.rb +98 -26
- data/lib/insika/mcp_tool_ingestor.rb +30 -8
- data/lib/insika/mcp_tool_registry.rb +100 -0
- data/lib/insika/media.rb +382 -0
- data/lib/insika/memory_audit_store.rb +85 -0
- data/lib/insika/memory_store.rb +264 -23
- data/lib/insika/message_origin.rb +7 -2
- data/lib/insika/middleware.rb +9 -0
- data/lib/insika/model_visible.rb +87 -0
- data/lib/insika/model_visible_trace_store.rb +66 -0
- data/lib/insika/onboarding.rb +22 -1
- data/lib/insika/outbox_store.rb +42 -4
- data/lib/insika/outcome_store.rb +147 -0
- data/lib/insika/overlay_tool_registry.rb +37 -17
- data/lib/insika/packaging.rb +163 -0
- data/lib/insika/parity/criterion.rb +79 -0
- data/lib/insika/parity/verdict.rb +318 -0
- data/lib/insika/prefix_fingerprint.rb +58 -0
- data/lib/insika/profile_source.rb +29 -1
- data/lib/insika/prompt_catalog.rb +10 -0
- data/lib/insika/proposal_store.rb +271 -0
- data/lib/insika/queue_policy.rb +4 -1
- data/lib/insika/refinement/proposer.rb +1 -1
- data/lib/insika/reliability.rb +32 -6
- data/lib/insika/retention.rb +316 -0
- data/lib/insika/router/app.rb +157 -0
- data/lib/insika/router/backend_pool.rb +98 -0
- data/lib/insika/router/hash_ring.rb +55 -0
- data/lib/insika/router/proxy_body.rb +34 -0
- data/lib/insika/router/session_key.rb +54 -0
- data/lib/insika/router.rb +18 -0
- data/lib/insika/routing.rb +101 -0
- data/lib/insika/safety/config.rb +44 -4
- data/lib/insika/safety/corpus.rb +255 -0
- data/lib/insika/safety/detectors.rb +32 -113
- data/lib/insika/safety/factory.rb +15 -2
- data/lib/insika/safety/grounding_enforcer.rb +59 -0
- data/lib/insika/safety/grounding_validator.rb +49 -0
- data/lib/insika/safety/input_guardrail.rb +1 -1
- data/lib/insika/safety/moderator.rb +1 -1
- data/lib/insika/safety/output_filter.rb +9 -5
- data/lib/insika/safety/output_validator.rb +11 -5
- data/lib/insika/schedule.rb +177 -0
- data/lib/insika/schedule_engine.rb +314 -0
- data/lib/insika/schedule_store.rb +208 -0
- data/lib/insika/schema_guard.rb +35 -0
- data/lib/insika/server/app.rb +247 -17
- data/lib/insika/server/rack_app.rb +21 -1
- data/lib/insika/server/responses.rb +40 -1
- data/lib/insika/session_actor.rb +8 -4
- data/lib/insika/session_store.rb +65 -2
- data/lib/insika/settings_store.rb +10 -0
- data/lib/insika/shadow_pair_store.rb +258 -0
- data/lib/insika/skill_catalog.rb +16 -0
- data/lib/insika/soak/envelope.rb +140 -0
- data/lib/insika/soak/report.rb +392 -0
- data/lib/insika/soak/runner.rb +554 -0
- data/lib/insika/steer_injector.rb +21 -10
- data/lib/insika/store.rb +10 -1
- data/lib/insika/stores/memory.rb +6 -0
- data/lib/insika/stores/sqlite.rb +8 -0
- data/lib/insika/studio/app.rb +1437 -54
- data/lib/insika/studio/assets/dist/application.css +1 -1
- data/lib/insika/studio/assets/dist/application.js +23 -23
- data/lib/insika/studio/assets/dist/favicon.svg +6 -0
- data/lib/insika/studio/forms.rb +308 -10
- data/lib/insika/studio/nav_icons.rb +19 -1
- data/lib/insika/studio/views/_agent_tab_cache.erb +25 -0
- data/lib/insika/studio/views/_agent_tab_config.erb +514 -0
- data/lib/insika/studio/views/_agent_tab_history.erb +24 -0
- data/lib/insika/studio/views/_agent_tab_loops.erb +54 -0
- data/lib/insika/studio/views/_agent_tab_memory.erb +51 -0
- data/lib/insika/studio/views/_agent_tab_outcomes.erb +31 -0
- data/lib/insika/studio/views/_agent_tab_prompts.erb +108 -0
- data/lib/insika/studio/views/_agent_tab_skills.erb +38 -0
- data/lib/insika/studio/views/_agents_master.erb +44 -0
- data/lib/insika/studio/views/_message.erb +49 -32
- data/lib/insika/studio/views/agent_detail.erb +62 -278
- data/lib/insika/studio/views/agents.erb +71 -54
- data/lib/insika/studio/views/approvals.erb +4 -1
- data/lib/insika/studio/views/artifact.erb +23 -0
- data/lib/insika/studio/views/artifacts.erb +59 -0
- data/lib/insika/studio/views/chats.erb +4 -1
- data/lib/insika/studio/views/customer.erb +94 -0
- data/lib/insika/studio/views/customers.erb +32 -0
- data/lib/insika/studio/views/evals.erb +6 -3
- data/lib/insika/studio/views/facts.erb +133 -0
- data/lib/insika/studio/views/followups.erb +125 -0
- data/lib/insika/studio/views/funnel.erb +106 -0
- data/lib/insika/studio/views/harvest.erb +234 -0
- data/lib/insika/studio/views/home.erb +106 -66
- data/lib/insika/studio/views/knowledge.erb +123 -0
- data/lib/insika/studio/views/layout.erb +15 -11
- data/lib/insika/studio/views/mcp.erb +174 -80
- data/lib/insika/studio/views/parity.erb +147 -0
- data/lib/insika/studio/views/playground.erb +7 -1
- data/lib/insika/studio/views/session.erb +233 -124
- data/lib/insika/studio/views/settings.erb +41 -1
- data/lib/insika/studio/views/skills.erb +1 -2
- data/lib/insika/studio/views/system_files.erb +1 -1
- data/lib/insika/studio/views/task.erb +13 -0
- data/lib/insika/studio/views/tasks.erb +4 -1
- data/lib/insika/studio/views/tools.erb +24 -10
- data/lib/insika/task_store.rb +21 -1
- data/lib/insika/templates/browser-agent/README.md +36 -0
- data/lib/insika/templates/browser-agent/agent.rb +49 -0
- data/lib/insika/templates/daily-digest/README.md +38 -0
- data/lib/insika/templates/daily-digest/agent.rb +77 -0
- data/lib/insika/templates/repo-explorer/README.md +36 -0
- data/lib/insika/templates/repo-explorer/agent.rb +45 -0
- data/lib/insika/templates/research-analyst/README.md +26 -0
- data/lib/insika/templates/research-analyst/agent.rb +58 -0
- data/lib/insika/templates/review-panel/README.md +20 -0
- data/lib/insika/templates/review-panel/agent.rb +50 -0
- data/lib/insika/templates/travel-planner/README.md +35 -0
- data/lib/insika/templates/travel-planner/agent.rb +87 -0
- data/lib/insika/templates.rb +112 -0
- data/lib/insika/testing/store_contract.rb +27 -6
- data/lib/insika/tick.rb +47 -11
- data/lib/insika/timezone.rb +45 -0
- data/lib/insika/tool_definition.rb +17 -7
- data/lib/insika/tool_envelope.rb +69 -0
- data/lib/insika/tool_manifest.rb +5 -1
- data/lib/insika/tools/data_defined_tool.rb +10 -0
- data/lib/insika/tools/generate_image.rb +89 -0
- data/lib/insika/tools/load_knowledge.rb +74 -0
- data/lib/insika/tools/run_persona_eval.rb +328 -0
- data/lib/insika/tools/save_artifact.rb +95 -0
- data/lib/insika/tools/schedule_followup.rb +164 -0
- data/lib/insika/tools/tts.rb +47 -0
- data/lib/insika/tools/update_briefing.rb +126 -0
- data/lib/insika/turn_output.rb +1 -1
- data/lib/insika/turn_state.rb +49 -1
- data/lib/insika/turn_timing.rb +22 -2
- data/lib/insika/version.rb +1 -1
- data/lib/insika/vitals.rb +84 -0
- data/lib/insika/wiring/graph.rb +472 -13
- data/lib/insika/wiring/graph_chat.rb +102 -0
- data/lib/insika.rb +149 -3
- metadata +181 -5
- data/docs/build.md +0 -14
- data/docs/understand.md +0 -10
|
@@ -11,11 +11,12 @@ module Insika
|
|
|
11
11
|
# exactly like any other pack. That is what makes `#profile` identical to a
|
|
12
12
|
# hand-written equivalent pack (the parity spec) — there is only one path.
|
|
13
13
|
class Definition
|
|
14
|
-
attr_reader :pack, :runtime_options
|
|
14
|
+
attr_reader :pack, :runtime_options, :mcp_instances
|
|
15
15
|
|
|
16
|
-
def initialize(pack:, runtime: {})
|
|
16
|
+
def initialize(pack:, runtime: {}, mcp_instances: [])
|
|
17
17
|
@pack = pack
|
|
18
18
|
@runtime_options = runtime
|
|
19
|
+
@mcp_instances = mcp_instances
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def id = pack.config[:id].to_s
|
data/lib/insika/dsl/runtime.rb
CHANGED
|
@@ -14,8 +14,6 @@ module Insika
|
|
|
14
14
|
# Built lazily by Definition#runtime, so `require "insika"` never pays for
|
|
15
15
|
# ruby_llm or the HTTP server.
|
|
16
16
|
class Runtime
|
|
17
|
-
TERMINAL = %i[task_completed task_failed task_cancelled].freeze
|
|
18
|
-
|
|
19
17
|
# graph: the assembled Wiring::Graph::Result; pack: the PRIMARY imported
|
|
20
18
|
# Pack; packs: every imported Pack (one per agent — a System has N, a
|
|
21
19
|
# Definition has one). All read by the server boot and the specs.
|
|
@@ -40,6 +38,11 @@ module Insika
|
|
|
40
38
|
# rather than reading a process-wide singleton at call time.
|
|
41
39
|
@llm = build_llm_context
|
|
42
40
|
@graph = build_graph
|
|
41
|
+
# the dispatch+drain seam `#chat`/`#run_workflow` delegate to —
|
|
42
|
+
# `Insika::Wiring::GraphChat`, shared with `config/deployment.rb`'s own
|
|
43
|
+
# registration of `run_persona_eval` (see `register_persona_eval_tool`
|
|
44
|
+
# below).
|
|
45
|
+
@chat_runtime = Insika::Wiring::GraphChat.new(graph: @graph)
|
|
43
46
|
seed_default_model
|
|
44
47
|
import_packs
|
|
45
48
|
end
|
|
@@ -62,12 +65,8 @@ module Insika
|
|
|
62
65
|
# one-shot; a value threads multi-turn memory (created on first use).
|
|
63
66
|
# `agent:` targets one agent of a system (default: the primary).
|
|
64
67
|
def chat(message, session_id: nil, timeout: nil, agent: nil)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
{ agent: (agent || @definition.id).to_s, message: message, session_id: session_id },
|
|
68
|
-
transport: :cli
|
|
69
|
-
)
|
|
70
|
-
run_command(command, session_id: session_id, timeout: timeout)[:text]
|
|
68
|
+
@chat_runtime.chat(message, agent: (agent || @definition.id).to_s,
|
|
69
|
+
session_id: session_id, timeout: timeout)
|
|
71
70
|
end
|
|
72
71
|
|
|
73
72
|
# Triggers a declared workflow and returns its OUTPUT (the typed value the
|
|
@@ -78,7 +77,7 @@ module Insika
|
|
|
78
77
|
command = Insika::Command.build(
|
|
79
78
|
:trigger_workflow, { workflow: name, agent: agent, input: input }, transport: :cli
|
|
80
79
|
)
|
|
81
|
-
outcome = run_command(command, session_id: nil, timeout: timeout)
|
|
80
|
+
outcome = @chat_runtime.run_command(command, session_id: nil, timeout: timeout)
|
|
82
81
|
outcome.key?(:output) ? outcome[:output] : outcome[:text]
|
|
83
82
|
end
|
|
84
83
|
|
|
@@ -93,70 +92,6 @@ module Insika
|
|
|
93
92
|
|
|
94
93
|
private
|
|
95
94
|
|
|
96
|
-
# Dispatch + drain, shared by a turn and a workflow run: subscribe BEFORE
|
|
97
|
-
# dispatching (the fiber may emit eagerly), bind to the task, read to the
|
|
98
|
-
# terminal event. A synchronous handler error (unknown agent/workflow, bad
|
|
99
|
-
# input against the schema) propagates from `dispatch` untouched — the
|
|
100
|
-
# caller sees the real ValidationError/NotFoundError, not a turn failure.
|
|
101
|
-
def run_command(command, session_id:, timeout:)
|
|
102
|
-
require "async"
|
|
103
|
-
outcome = {}
|
|
104
|
-
rejected = nil
|
|
105
|
-
Async do |task|
|
|
106
|
-
serving = !session_id.nil?
|
|
107
|
-
@graph.executor.supervised = true if serving
|
|
108
|
-
ensure_session(session_id) if session_id
|
|
109
|
-
sub = @graph.event_stream.subscribe
|
|
110
|
-
res = begin
|
|
111
|
-
@graph.bus.dispatch(command)
|
|
112
|
-
rescue StandardError => e
|
|
113
|
-
# CAPTURED, not re-raised inside the reactor: letting it escape the
|
|
114
|
-
# Async block logs "Task may have ended with unhandled exception" —
|
|
115
|
-
# alarming noise for a DOCUMENTED rejection (a schema violation, an
|
|
116
|
-
# unknown agent). Re-raised verbatim below, outside the reactor.
|
|
117
|
-
sub.close
|
|
118
|
-
rejected = e
|
|
119
|
-
next
|
|
120
|
-
end
|
|
121
|
-
sub.bind(task_id: res[:task_id])
|
|
122
|
-
drain(sub, outcome, task, timeout)
|
|
123
|
-
teardown_serving if serving
|
|
124
|
-
end.wait
|
|
125
|
-
raise rejected if rejected
|
|
126
|
-
raise Insika::Error, "turn #{outcome[:error]}" if outcome[:error]
|
|
127
|
-
|
|
128
|
-
outcome
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Reads the stream until this turn reaches a terminal event, then stops.
|
|
132
|
-
# A workflow run also carries its typed OUTPUT, which is not the turn text.
|
|
133
|
-
def drain(sub, outcome, task, timeout)
|
|
134
|
-
reader = task.async do
|
|
135
|
-
sub.each do |ev|
|
|
136
|
-
case ev.type
|
|
137
|
-
when :task_completed then outcome[:text] = ev.data[:content].to_s
|
|
138
|
-
when :workflow_completed then outcome[:output] = ev.data[:output]
|
|
139
|
-
when :task_failed then outcome[:error] = "failed: #{ev.data[:message] || ev.data[:error]}"
|
|
140
|
-
when :task_cancelled then outcome[:error] = "cancelled"
|
|
141
|
-
end
|
|
142
|
-
sub.close if TERMINAL.include?(ev.type)
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
timeout ? task.with_timeout(timeout) { reader.wait } : reader.wait
|
|
146
|
-
rescue Async::TimeoutError
|
|
147
|
-
outcome[:error] = "timed out after #{timeout}s"
|
|
148
|
-
sub.close
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def ensure_session(session_id)
|
|
152
|
-
@graph.session_store.find(session_id) || @graph.session_store.create(id: session_id, vars: {})
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def teardown_serving
|
|
156
|
-
@graph.executor.stop_session_actors
|
|
157
|
-
@graph.executor.instance_variable_get(:@supervisor)&.stop
|
|
158
|
-
end
|
|
159
|
-
|
|
160
95
|
# --- graph assembly (mirrors config/deployment.rb, generically) ------
|
|
161
96
|
|
|
162
97
|
def build_graph
|
|
@@ -178,10 +113,25 @@ module Insika
|
|
|
178
113
|
guardrails: c[:guardrails], context_providers: context_providers(spine, c),
|
|
179
114
|
edge_limiter: c[:edge_limiter],
|
|
180
115
|
executor_extra: { settings_store: c[:settings_store], tool_trace_store: c[:tool_trace_store],
|
|
181
|
-
context_trace_store: c[:context_trace_store],
|
|
116
|
+
context_trace_store: c[:context_trace_store],
|
|
117
|
+
cache_series_store: c[:cache_series_store], llm: @llm }
|
|
182
118
|
)
|
|
183
119
|
register_authoring_commands(graph, c)
|
|
184
120
|
register_workflows(graph)
|
|
121
|
+
import_mcp_declarations(c)
|
|
122
|
+
# `run_persona_eval` (C3.1): the SAME registration `config/deployment.rb`
|
|
123
|
+
# does for its own graph — see `Wiring::Graph.register_persona_eval_tool`.
|
|
124
|
+
# One implementation, both roots; this one just already has its
|
|
125
|
+
# `golden_store`/`settings_store` at hand from `build_components`.
|
|
126
|
+
Insika::Wiring::Graph.register_persona_eval_tool(
|
|
127
|
+
graph, golden_store: Insika::GoldenStore.new(config_store: c[:config_store]),
|
|
128
|
+
settings_store: c[:settings_store], llm: @llm
|
|
129
|
+
)
|
|
130
|
+
# Same boot step the server roots run: an installed `insika-plugin-*`
|
|
131
|
+
# gem (or INSIKA_PLUGIN_DIR) extends a DSL-run agent too — a plugin
|
|
132
|
+
# works everywhere or it is not a plugin. No bundled root here: the DSL
|
|
133
|
+
# runs outside the repo checkout.
|
|
134
|
+
Insika::Wiring::Graph.load_plugins(graph)
|
|
185
135
|
graph
|
|
186
136
|
end
|
|
187
137
|
|
|
@@ -209,15 +159,42 @@ module Insika
|
|
|
209
159
|
))
|
|
210
160
|
end
|
|
211
161
|
|
|
162
|
+
# Upserts every DSL-declared `mcp` instance into the McpStore at boot.
|
|
163
|
+
# Code is the TEMPLATE — transport/command/args/url/
|
|
164
|
+
# description always follow the declaration — but `enabled` and the
|
|
165
|
+
# credentials (`env`/`headers`) are the operator's once the instance
|
|
166
|
+
# exists: an already-present record's own values are re-sent verbatim
|
|
167
|
+
# instead of the DSL's, so a Studio/CLI/API edit made after boot is never
|
|
168
|
+
# clobbered by the next restart (motor-vs-forja: the store's DATA wins).
|
|
169
|
+
def import_mcp_declarations(c)
|
|
170
|
+
declared = Array(@definition.mcp_instances)
|
|
171
|
+
return if declared.empty?
|
|
172
|
+
|
|
173
|
+
mcp_store = c[:mcp_store]
|
|
174
|
+
declared.each do |decl|
|
|
175
|
+
attrs = decl.dup
|
|
176
|
+
existing = mcp_store.get_raw(attrs[:name])
|
|
177
|
+
if existing
|
|
178
|
+
attrs[:enabled] = existing["enabled"]
|
|
179
|
+
attrs[:env] = existing["env"]
|
|
180
|
+
attrs[:headers] = existing["headers"]
|
|
181
|
+
end
|
|
182
|
+
mcp_store.upsert(attrs)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
212
186
|
def build_components(backend, spine)
|
|
213
187
|
config_store = Insika::ConfigStore.new(store: backend)
|
|
214
188
|
settings_store = Insika::SettingsStore.new(config_store: config_store)
|
|
215
189
|
provider_store = Insika::LLMProviderStore.new(config_store: config_store)
|
|
216
190
|
tool_store = Insika::ToolStore.new(config_store: config_store)
|
|
217
191
|
skill_store = Insika::SkillStore.new(config_store: config_store)
|
|
192
|
+
mcp_store = Insika::McpStore.new(config_store: config_store)
|
|
193
|
+
mcp_tool_registry = Insika::McpToolRegistry.new(mcp_store: mcp_store)
|
|
218
194
|
tool_registry = Insika::OverlayToolRegistry.new(
|
|
219
195
|
base: spine.code_tool_registry, tool_store: tool_store,
|
|
220
|
-
http: Insika::HttpClient.new, event_stream: spine.event_stream
|
|
196
|
+
http: Insika::HttpClient.new, event_stream: spine.event_stream,
|
|
197
|
+
mcp_registry: mcp_tool_registry
|
|
221
198
|
)
|
|
222
199
|
{
|
|
223
200
|
backend: backend, config_store: config_store,
|
|
@@ -226,9 +203,11 @@ module Insika
|
|
|
226
203
|
agent_file_store: Insika::AgentFileStore.new(config_store: config_store),
|
|
227
204
|
skill_store: skill_store, tool_store: tool_store,
|
|
228
205
|
system_file_store: Insika::SystemFileStore.new(config_store: config_store),
|
|
229
|
-
mcp_store:
|
|
206
|
+
mcp_store: mcp_store, mcp_tool_registry: mcp_tool_registry,
|
|
230
207
|
tool_trace_store: Insika::ToolTraceStore.new(store: backend),
|
|
231
208
|
context_trace_store: Insika::ContextTraceStore.new(store: backend),
|
|
209
|
+
# the per-AGENT cache-hit series (the Studio agent-detail card).
|
|
210
|
+
cache_series_store: Insika::CacheSeriesStore.new(store: backend),
|
|
232
211
|
tool_registry: tool_registry,
|
|
233
212
|
tool_catalog: Insika::ToolCatalog.new(tool_registry: tool_registry),
|
|
234
213
|
skill_catalog: Insika::SkillCatalog.new([], store: skill_store),
|
|
@@ -249,15 +228,16 @@ module Insika
|
|
|
249
228
|
[
|
|
250
229
|
Insika::Context::Providers::Request.new,
|
|
251
230
|
Insika::Context::Providers::Prompt.new(
|
|
252
|
-
base: "",
|
|
231
|
+
base: "", catalog: c[:prompt_catalog],
|
|
253
232
|
agent_files: c[:agent_file_store], system_files: c[:system_file_store]
|
|
254
233
|
),
|
|
255
234
|
Insika::Context::Providers::Skill.new(catalog: c[:skill_catalog]),
|
|
256
235
|
Insika::Context::Providers::SkillTrigger.new(catalog: c[:skill_catalog]),
|
|
257
236
|
Insika::Context::Providers::ToolSearch.new(catalog: c[:tool_catalog]),
|
|
258
237
|
Insika::Context::Providers::Memory.new(store: spine.memory_store),
|
|
238
|
+
Insika::Context::Providers::Knowledge.new(store: spine.knowledge_store),
|
|
259
239
|
Insika::Context::Providers::Session.new(session_store: spine.session_store)
|
|
260
|
-
]
|
|
240
|
+
] # NOT frozen: load_plugins appends plugin providers at boot
|
|
261
241
|
end
|
|
262
242
|
|
|
263
243
|
# The runtime-authoring surface a pack import needs (create_agent +
|
|
@@ -267,6 +247,7 @@ module Insika
|
|
|
267
247
|
bus = graph.bus
|
|
268
248
|
es = graph.event_stream
|
|
269
249
|
mem = graph.memory_store
|
|
250
|
+
audit = graph.memory_audit_store
|
|
270
251
|
bus.register(:create_agent, Insika::Commands::CreateAgent.new(profile_source: c[:profile_source], event_stream: es))
|
|
271
252
|
bus.register(:update_agent, Insika::Commands::UpdateAgent.new(profile_source: c[:profile_source], event_stream: es))
|
|
272
253
|
bus.register(:delete_agent, Insika::Commands::DeleteAgent.new(profile_source: c[:profile_source], event_stream: es))
|
|
@@ -276,20 +257,26 @@ module Insika
|
|
|
276
257
|
bus.register(:restore_agent_file, Insika::Commands::RestoreAgentFile.new(profile_source: c[:profile_source], agent_file_store: c[:agent_file_store], event_stream: es))
|
|
277
258
|
bus.register(:write_skill, Insika::Commands::WriteSkill.new(skill_store: c[:skill_store], skill_catalog: c[:skill_catalog], event_stream: es))
|
|
278
259
|
bus.register(:set_skill_agents, Insika::Commands::SetSkillAgents.new(profile_source: c[:profile_source], event_stream: es))
|
|
279
|
-
bus.register(:memory_put_fact, Insika::Commands::MemoryPutFact.new(memory_store: mem, event_stream: es))
|
|
280
|
-
bus.register(:memory_forget_fact, Insika::Commands::MemoryForgetFact.new(memory_store: mem, event_stream: es))
|
|
260
|
+
bus.register(:memory_put_fact, Insika::Commands::MemoryPutFact.new(memory_store: mem, event_stream: es, audit_store: audit))
|
|
261
|
+
bus.register(:memory_forget_fact, Insika::Commands::MemoryForgetFact.new(memory_store: mem, event_stream: es, audit_store: audit))
|
|
281
262
|
bus.register(:memory_add_note, Insika::Commands::MemoryAddNote.new(memory_store: mem, event_stream: es))
|
|
263
|
+
# the LGPD access right — the Studio Customers drill exports.
|
|
264
|
+
bus.register(:export_customer_memory, Insika::Commands::ExportCustomerMemory.new(memory_store: mem, event_stream: es))
|
|
282
265
|
bus.register(:update_settings, Insika::Commands::UpdateSettings.new(settings_store: c[:settings_store], event_stream: es))
|
|
283
266
|
bus.register(:upsert_llm_provider, Insika::Commands::UpsertLLMProvider.new(provider_store: c[:provider_store], configurator: c[:configurator], event_stream: es))
|
|
284
267
|
bus.register(:delete_llm_provider, Insika::Commands::DeleteLLMProvider.new(provider_store: c[:provider_store], configurator: c[:configurator], event_stream: es))
|
|
285
|
-
bus.register(:upsert_mcp, Insika::Commands::UpsertMcp.new(mcp_store: c[:mcp_store], event_stream: es))
|
|
286
|
-
bus.register(:delete_mcp, Insika::Commands::DeleteMcp.new(mcp_store: c[:mcp_store], event_stream: es))
|
|
268
|
+
bus.register(:upsert_mcp, Insika::Commands::UpsertMcp.new(mcp_store: c[:mcp_store], mcp_registry: c[:mcp_tool_registry], event_stream: es))
|
|
269
|
+
bus.register(:delete_mcp, Insika::Commands::DeleteMcp.new(mcp_store: c[:mcp_store], mcp_registry: c[:mcp_tool_registry], event_stream: es))
|
|
270
|
+
bus.register(:refresh_mcp_tools, Insika::Commands::RefreshMcpTools.new(mcp_registry: c[:mcp_tool_registry], event_stream: es))
|
|
287
271
|
bus.register(:write_system_file, Insika::Commands::WriteSystemFile.new(system_file_store: c[:system_file_store], event_stream: es))
|
|
288
272
|
bus.register(:delete_system_file, Insika::Commands::DeleteSystemFile.new(system_file_store: c[:system_file_store], event_stream: es))
|
|
289
273
|
bus.register(:restore_system_file, Insika::Commands::RestoreSystemFile.new(system_file_store: c[:system_file_store], event_stream: es))
|
|
290
274
|
bus.register(:write_data_tool, Insika::Commands::WriteDataTool.new(tool_store: c[:tool_store], registry: c[:tool_registry], tool_catalog: c[:tool_catalog], event_stream: es))
|
|
291
275
|
bus.register(:delete_data_tool, Insika::Commands::DeleteDataTool.new(tool_store: c[:tool_store], registry: c[:tool_registry], tool_catalog: c[:tool_catalog], event_stream: es))
|
|
292
276
|
bus.register(:restore_data_tool, Insika::Commands::RestoreDataTool.new(tool_store: c[:tool_store], registry: c[:tool_registry], tool_catalog: c[:tool_catalog], event_stream: es))
|
|
277
|
+
bus.register(:write_concept, Insika::Commands::WriteConcept.new(knowledge_store: graph.knowledge_store, event_stream: es))
|
|
278
|
+
bus.register(:delete_concept, Insika::Commands::DeleteConcept.new(knowledge_store: graph.knowledge_store, event_stream: es))
|
|
279
|
+
bus.register(:restore_concept, Insika::Commands::RestoreConcept.new(knowledge_store: graph.knowledge_store, event_stream: es))
|
|
293
280
|
end
|
|
294
281
|
|
|
295
282
|
# --- RubyLLM configuration ------------------------------------------
|
|
@@ -31,6 +31,11 @@ module Insika
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def run
|
|
34
|
+
# The boot refusal (shadow + no criterion) lives in the AppBuilder; run it
|
|
35
|
+
# BEFORE the Studio is configured so the criterion the Studio folds is the
|
|
36
|
+
# same object the delivery path stamps pairs with.
|
|
37
|
+
@builder.channels?
|
|
38
|
+
register_parity_commands
|
|
34
39
|
configure_studio
|
|
35
40
|
dispatch = Rack::URLMap.new("/studio" => Studio::App, "/" => @builder.app)
|
|
36
41
|
endpoint = Async::HTTP::Endpoint.parse("http://#{@host}:#{@port}")
|
|
@@ -46,6 +51,10 @@ module Insika
|
|
|
46
51
|
Insika::Shutdown.install(executor: @graph.executor)
|
|
47
52
|
Async do
|
|
48
53
|
@graph.executor.supervised = true # serving mode: turns survive disconnects
|
|
54
|
+
# forces the tick/alert/distill/harvest supervisor up now — otherwise a
|
|
55
|
+
# deployment with only scheduled agents (no live chat) never ticks until
|
|
56
|
+
# unrelated traffic happens to hit turn_parent first.
|
|
57
|
+
@graph.executor.start_supervisor!
|
|
49
58
|
Insika::Telemetry.attach(event_stream: @graph.event_stream, recorder: telemetry)
|
|
50
59
|
Async::HTTP::Server.new(middleware, endpoint).run
|
|
51
60
|
end
|
|
@@ -56,6 +65,23 @@ module Insika
|
|
|
56
65
|
def workflows? = @builder.workflows?
|
|
57
66
|
def channels? = @builder.channels?
|
|
58
67
|
|
|
68
|
+
# The parity page renders whenever a shadow channel exists, so the judge
|
|
69
|
+
# button must reach a registered command — the deployment bus registers the
|
|
70
|
+
# same one (config/deployment.rb); a page that answers "unknown command"
|
|
71
|
+
# would make this root a second-class citizen. Registered only when the
|
|
72
|
+
# criterion loaded (a command without one would be a fake judge).
|
|
73
|
+
def register_parity_commands
|
|
74
|
+
criterion = @builder.criterion
|
|
75
|
+
return unless criterion
|
|
76
|
+
|
|
77
|
+
@graph.bus.register(:judge_shadow_pairs,
|
|
78
|
+
Insika::Commands::JudgeShadowPairs.new(
|
|
79
|
+
shadow_pairs: @graph.shadow_pair_store,
|
|
80
|
+
settings_store: @rt.component(:settings_store),
|
|
81
|
+
criterion: criterion, event_stream: @graph.event_stream
|
|
82
|
+
))
|
|
83
|
+
end
|
|
84
|
+
|
|
59
85
|
def configure_studio
|
|
60
86
|
Studio::App.configure(
|
|
61
87
|
command_bus: @graph.bus, profile_source: @graph.profiles,
|
|
@@ -64,16 +90,81 @@ module Insika
|
|
|
64
90
|
agent_file_store: @rt.component(:agent_file_store), skill_store: @rt.component(:skill_store),
|
|
65
91
|
skill_catalog: @graph.skill_catalog, tool_catalog: @graph.tool_catalog,
|
|
66
92
|
tool_store: @rt.component(:tool_store), memory_store: @graph.memory_store,
|
|
93
|
+
# the Customers drill renders the audit lines.
|
|
94
|
+
memory_audit_store: @graph.memory_audit_store,
|
|
67
95
|
session_store: @graph.session_store,
|
|
68
96
|
settings_store: @rt.component(:settings_store), llm_provider_store: @rt.component(:provider_store),
|
|
69
97
|
mcp_store: @rt.component(:mcp_store), system_file_store: @rt.component(:system_file_store),
|
|
70
98
|
tool_trace_store: @rt.component(:tool_trace_store),
|
|
71
99
|
context_trace_store: @rt.component(:context_trace_store),
|
|
100
|
+
# the per-agent cache-hit series (agent-detail card).
|
|
101
|
+
cache_series_store: @rt.component(:cache_series_store),
|
|
72
102
|
task_store: @graph.task_store, checkpoint_store: @graph.checkpoint_store,
|
|
73
|
-
pending_action_store: @graph.pending_action_store
|
|
103
|
+
pending_action_store: @graph.pending_action_store,
|
|
104
|
+
# WS7: the agents-grid scorecard reads the outcome store.
|
|
105
|
+
outcome_store: @graph.outcome_store,
|
|
106
|
+
# the outcome-funnel page reads the fold's cells and the
|
|
107
|
+
# BudgetLedger's current counters (D6); the only mutation — the
|
|
108
|
+
# baseline freeze — dispatches :freeze_funnel_baseline on the bus.
|
|
109
|
+
funnel_store: @graph.funnel_store,
|
|
110
|
+
budget_ledger: @graph.budget_ledger,
|
|
111
|
+
# the parity page (only rendered when a shadow channel exists —
|
|
112
|
+
# the nav row keys off the registry).
|
|
113
|
+
shadow_pair_store: @graph.shadow_pair_store,
|
|
114
|
+
parity_criterion: @builder.criterion,
|
|
115
|
+
channel_registry: @graph.channel_registry,
|
|
116
|
+
# the Follow-ups page reads the stores directly;
|
|
117
|
+
# its only mutations (cancel, force-revoke) dispatch bus commands.
|
|
118
|
+
followup_store: @graph.followup_store,
|
|
119
|
+
contact_store: @graph.contact_store,
|
|
120
|
+
# the Facts (wiki) page reads the proposal store
|
|
121
|
+
# directly; its mutations dispatch :resolve_proposal on the bus.
|
|
122
|
+
proposal_store: @graph.proposal_store,
|
|
123
|
+
# the Harvest page reads the harvest store + the two
|
|
124
|
+
# pre-registered artifacts directly; its mutations dispatch the
|
|
125
|
+
# harvest bus commands. The criterion/negative list load best-effort
|
|
126
|
+
# (nil = the page renders the refusal/empty states).
|
|
127
|
+
harvest_store: @graph.harvest_store,
|
|
128
|
+
harvest_criterion: harvest_criterion,
|
|
129
|
+
negative_list: harvest_negative_list,
|
|
130
|
+
# the Knowledge page reads the store directly; its
|
|
131
|
+
# mutations (write/delete/restore) dispatch bus commands.
|
|
132
|
+
knowledge_store: @graph.knowledge_store,
|
|
133
|
+
# the Artifacts tab reads the report store; the signing
|
|
134
|
+
# config (nil without INSIKA_ARTIFACT_SIGNING_KEY) decides whether a
|
|
135
|
+
# signed sharing link exists at all.
|
|
136
|
+
artifact_store: @graph.artifact_store,
|
|
137
|
+
artifact_signing: artifact_signing
|
|
74
138
|
)
|
|
75
139
|
end
|
|
76
140
|
|
|
141
|
+
# -> { key:, ttl:, base_url: } | nil (no signing key -> no signed surface).
|
|
142
|
+
def artifact_signing
|
|
143
|
+
key = Insika::EnvSchema.read("INSIKA_ARTIFACT_SIGNING_KEY")
|
|
144
|
+
return nil if key.to_s.empty?
|
|
145
|
+
|
|
146
|
+
{ key: key,
|
|
147
|
+
ttl: (Insika::EnvSchema.read("INSIKA_ARTIFACT_SIGNING_TTL") || 604_800).to_i,
|
|
148
|
+
base_url: Insika::Coercion.presence(Insika::EnvSchema.read("INSIKA_PUBLIC_URL")).to_s }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# The frozen criterion — best-effort at boot: a bare install
|
|
152
|
+
# without the env var renders the ruler's hole instead of
|
|
153
|
+
# crashing the Studio.
|
|
154
|
+
def harvest_criterion
|
|
155
|
+
path = Insika::EnvSchema.read("INSIKA_HARVEST_CRITERION")
|
|
156
|
+
path && Insika::Harvest::Criterion.load(path)
|
|
157
|
+
rescue Insika::ConfigError, Insika::ValidationError
|
|
158
|
+
nil
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def harvest_negative_list
|
|
162
|
+
path = Insika::EnvSchema.read("INSIKA_HARVEST_NEGATIVE")
|
|
163
|
+
path && Insika::Harvest::NegativeList.parse(File.read(path))
|
|
164
|
+
rescue Errno::ENOENT
|
|
165
|
+
nil
|
|
166
|
+
end
|
|
167
|
+
|
|
77
168
|
def persistence
|
|
78
169
|
@graph.durable? ? "durable (sqlite)" : "ephemeral (memory)"
|
|
79
170
|
end
|
data/lib/insika/dsl/system.rb
CHANGED
|
@@ -15,13 +15,21 @@ module Insika
|
|
|
15
15
|
# require the child agents to be resolvable in the SAME ProfileSource. A
|
|
16
16
|
# Definition owns exactly one pack, so those patterns had no home in the DSL.
|
|
17
17
|
class System
|
|
18
|
-
attr_reader :definitions, :workflows, :runtime_options
|
|
18
|
+
attr_reader :definitions, :workflows, :mcp_instances, :runtime_options
|
|
19
19
|
|
|
20
20
|
# `backend`: the store this system's graph owns. nil = the
|
|
21
21
|
# historic path (INSIKA_DB, or memory when unset). Set by `Insika.embed`.
|
|
22
|
-
def initialize(definitions:, workflows: [], runtime: {}, backend: nil)
|
|
22
|
+
def initialize(definitions:, workflows: [], mcp_instances: [], runtime: {}, backend: nil)
|
|
23
23
|
@definitions = definitions.freeze
|
|
24
24
|
@workflows = workflows.freeze
|
|
25
|
+
# MCP instances are global to the graph (one McpStore, not per-agent),
|
|
26
|
+
# so a system-level `mcp` declaration and one nested inside a member
|
|
27
|
+
# `agent { … }` block land in the same set. A name declared both places
|
|
28
|
+
# is NOT an error here (unlike within one collection) — the later one
|
|
29
|
+
# (system-level, applied last) simply wins; Insika::DSL::Runtime upserts
|
|
30
|
+
# this combined list once at boot.
|
|
31
|
+
@mcp_instances = (definitions.flat_map(&:mcp_instances) + mcp_instances)
|
|
32
|
+
.each_with_object({}) { |m, acc| acc[m[:name]] = m }.values.freeze
|
|
25
33
|
@backend = backend
|
|
26
34
|
# Agent-level runtime knobs merged in declaration order, then the
|
|
27
35
|
# system-level ones on top (an explicit `provider`/`api_key` in the
|