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
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
<%# MCP — miller columns: instances master | editor detail.
|
|
2
|
+
?i= selects the instance whose editor fills the detail pane; nil shows the
|
|
3
|
+
empty state with the new/import forms. Master rows and the editor form
|
|
4
|
+
frame-navigate (mcp-detail); create/import/delete submit to the top window
|
|
5
|
+
so the master list re-renders with the change. Credentials never come back
|
|
6
|
+
in plaintext — each value shows as the sentinel; keeping it preserves the
|
|
7
|
+
secret, changing it replaces, clearing the line removes it. %>
|
|
8
|
+
<%# Roda renders with plain locals — no local_assigns hash (Rails-ism). %>
|
|
9
|
+
<% frame_only = defined?(frame_only) && frame_only %>
|
|
10
|
+
<% unless frame_only %>
|
|
1
11
|
<header class="page-head">
|
|
2
12
|
<div>
|
|
3
13
|
<nav class="crumbs"><a href="/studio/agents">agents</a> / <span>mcp</span></nav>
|
|
@@ -5,87 +15,171 @@
|
|
|
5
15
|
</div>
|
|
6
16
|
</header>
|
|
7
17
|
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
<div class="drill" data-controller="list-filter">
|
|
19
|
+
<aside class="drill-pane drill-master">
|
|
20
|
+
<div class="drill-pane-head">
|
|
21
|
+
<div class="title"><strong>MCP <span class="count"><%= @instances.size %></span></strong></div>
|
|
22
|
+
<a class="btn primary btn-sm" href="/studio/mcp?i=" data-turbo-frame="mcp-detail" data-turbo-action="advance">+ new</a>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="drill-search">
|
|
25
|
+
<input type="search" class="filter-input" placeholder="Filter by name, transport or status…" aria-label="Filter MCP instances"
|
|
26
|
+
data-list-filter-target="query" data-action="input->list-filter#filter keydown->list-filter#clear">
|
|
27
|
+
</div>
|
|
28
|
+
<nav class="drill-pane-body drill-list" aria-label="MCP instances" data-controller="active-link">
|
|
29
|
+
<% if @instances.empty? %>
|
|
30
|
+
<div class="empty">No MCP instances. Create one on the right (e.g. <code>tavily</code>) to expose an MCP server to your agents' tools.</div>
|
|
31
|
+
<% else %>
|
|
32
|
+
<% @instances.each do |m| %>
|
|
33
|
+
<% status_label, status_cls = m["enabled"] ? mcp_status(m) : ["off", "warn"] %>
|
|
34
|
+
<a class="drill-item<%== ' active' if @selected_mcp && @selected_mcp["name"] == m["name"] %>"
|
|
35
|
+
href="/studio/mcp?i=<%= Rack::Utils.escape(m["name"]) %>"
|
|
36
|
+
data-turbo-frame="mcp-detail" data-turbo-action="advance"
|
|
37
|
+
data-list-filter-target="item" data-filter-text="<%= mcp_filter_text(m) %>"
|
|
38
|
+
data-active-link-target="item" data-action="click->active-link#select">
|
|
39
|
+
<div class="drill-item-name">
|
|
40
|
+
<span class="txt" title="<%= m["name"] %>"><%= m["name"] %></span>
|
|
41
|
+
<span class="pill <%= status_cls %>"><%= status_label %></span>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="drill-item-sub"><%= m["transport"] %><%== " · #{m['url']}" if m["url"].to_s != "" %><%== " · #{Array(m['tools_cache']).size} tool(s)" if Array(m["tools_cache"]).any? %></div>
|
|
44
|
+
</a>
|
|
45
|
+
<% end %>
|
|
46
|
+
<div class="empty" data-list-filter-target="empty" hidden>No MCP instances match your filter.</div>
|
|
47
|
+
<% end %>
|
|
48
|
+
</nav>
|
|
49
|
+
</aside>
|
|
12
50
|
<% end %>
|
|
51
|
+
<turbo-frame id="mcp-detail" class="drill-pane drill-detail">
|
|
52
|
+
<% if frame_only %>
|
|
53
|
+
<% if flash["error"] %><div class="flash err" role="alert"><%= flash["error"] %></div><% end %>
|
|
54
|
+
<% if flash["notice"] %><div class="flash ok" role="status"><%= flash["notice"] %></div><% end %>
|
|
55
|
+
<% end %>
|
|
56
|
+
<% if @selected_mcp %>
|
|
57
|
+
<% m = @selected_mcp %>
|
|
58
|
+
<div class="drill-pane-head">
|
|
59
|
+
<div class="title">
|
|
60
|
+
<strong class="mono"><%= m["name"] %></strong>
|
|
61
|
+
<span class="count"><%= m["transport"] %><%== " · #{m['description']}" if m["description"].to_s != "" %></span>
|
|
62
|
+
</div>
|
|
63
|
+
<div class="head-actions">
|
|
64
|
+
<form method="post" action="/studio/mcp/test">
|
|
65
|
+
<%== csrf_tag %>
|
|
66
|
+
<input type="hidden" name="name" value="<%= m["name"] %>">
|
|
67
|
+
<button type="submit" class="btn btn-sm" data-turbo-submits-with="Testing…">Test connection</button>
|
|
68
|
+
</form>
|
|
69
|
+
<form method="post" action="/studio/mcp/delete" data-turbo-frame="_top" data-turbo-confirm="Remove MCP instance "<%= m["name"] %>"? Agents stop seeing its tools next turn.">
|
|
70
|
+
<%== csrf_tag %>
|
|
71
|
+
<input type="hidden" name="name" value="<%= m["name"] %>">
|
|
72
|
+
<button type="submit" class="btn btn-sm danger" data-turbo-submits-with="Removing…">Remove</button>
|
|
73
|
+
</form>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="drill-pane-body">
|
|
77
|
+
<% tools = Array(m["tools_cache"]) %>
|
|
78
|
+
<% if tools.any? %>
|
|
79
|
+
<details class="mcp-tools-details" open>
|
|
80
|
+
<summary><%= tools.size %> tool(s) · live cache</summary>
|
|
81
|
+
<ul class="mcp-tools muted">
|
|
82
|
+
<% tools.each do |t| %>
|
|
83
|
+
<li><code><%= t["name"] %></code><% if presence(t["description"]) %> — <%= t["description"] %><% end %></li>
|
|
84
|
+
<% end %>
|
|
85
|
+
</ul>
|
|
86
|
+
</details>
|
|
87
|
+
<% end %>
|
|
13
88
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
</
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
89
|
+
<form class="mcp-edit" method="post" action="/studio/mcp" data-controller="transport-fields">
|
|
90
|
+
<%== csrf_tag %>
|
|
91
|
+
<input type="hidden" name="name" value="<%= m["name"] %>">
|
|
92
|
+
<div class="grid-2">
|
|
93
|
+
<label>transport
|
|
94
|
+
<select name="transport" data-transport-fields-target="select" data-action="change->transport-fields#sync">
|
|
95
|
+
<% %w[stdio http sse].each do |t| %>
|
|
96
|
+
<option value="<%= t %>"<%== " selected" if m["transport"] == t %>><%= t %></option>
|
|
97
|
+
<% end %>
|
|
98
|
+
</select>
|
|
99
|
+
</label>
|
|
100
|
+
<label class="check">
|
|
101
|
+
<input type="checkbox" name="enabled" value="1"<%== " checked" if m["enabled"] %>>
|
|
102
|
+
enabled
|
|
103
|
+
</label>
|
|
104
|
+
</div>
|
|
105
|
+
<label data-transport-fields-target="stdio">command <span class="muted">(stdio)</span>
|
|
106
|
+
<input type="text" name="command" value="<%= m["command"] %>" placeholder="npx -y @modelcontextprotocol/server-...">
|
|
107
|
+
</label>
|
|
108
|
+
<label data-transport-fields-target="stdio">args <span class="muted">(stdio, one argv token per line)</span>
|
|
109
|
+
<textarea name="args" rows="3" placeholder="-y @modelcontextprotocol/server-filesystem /tmp"><%= list_lines(m["args"]) %></textarea>
|
|
110
|
+
</label>
|
|
111
|
+
<label data-transport-fields-target="http">url <span class="muted">(http/sse)</span>
|
|
112
|
+
<input type="text" name="url" value="<%= m["url"] %>" placeholder="https://mcp.example.com">
|
|
113
|
+
</label>
|
|
114
|
+
<label>description
|
|
115
|
+
<input type="text" name="description" value="<%= m["description"] %>">
|
|
116
|
+
</label>
|
|
117
|
+
<label data-transport-fields-target="stdio">env <span class="muted">(stdio process env, one <code>KEY=value</code> per line)</span>
|
|
118
|
+
<textarea name="env" rows="4"><%= env_lines(m["env"]) %></textarea>
|
|
119
|
+
</label>
|
|
120
|
+
<label data-transport-fields-target="http">headers <span class="muted">(http/sse request headers, one <code>Name=value</code> per line)</span>
|
|
121
|
+
<textarea name="headers" rows="4"><%= env_lines(m["headers"]) %></textarea>
|
|
122
|
+
</label>
|
|
123
|
+
<div class="actions">
|
|
124
|
+
<button type="submit" class="primary" data-turbo-submits-with="Saving…">Save instance</button>
|
|
125
|
+
</div>
|
|
126
|
+
</form>
|
|
41
127
|
</div>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
128
|
+
<% else %>
|
|
129
|
+
<div class="drill-pane-head">
|
|
130
|
+
<div class="title">
|
|
131
|
+
<strong>Pick an instance</strong>
|
|
132
|
+
<span class="count">select on the left, or add one below</span>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="drill-pane-body">
|
|
136
|
+
<form method="post" action="/studio/mcp" data-controller="transport-fields" data-turbo-frame="_top">
|
|
137
|
+
<%== csrf_tag %>
|
|
138
|
+
<div class="grid-2">
|
|
139
|
+
<label>name <span class="muted">(slug)</span>
|
|
140
|
+
<input type="text" name="name" placeholder="tavily" required>
|
|
141
|
+
</label>
|
|
142
|
+
<label>transport
|
|
143
|
+
<select name="transport" data-transport-fields-target="select" data-action="change->transport-fields#sync">
|
|
144
|
+
<% %w[stdio http sse].each do |t| %><option value="<%= t %>"><%= t %></option><% end %>
|
|
145
|
+
</select>
|
|
146
|
+
</label>
|
|
147
|
+
</div>
|
|
148
|
+
<label class="check">
|
|
149
|
+
<input type="checkbox" name="enabled" value="1" checked>
|
|
150
|
+
enabled
|
|
151
|
+
</label>
|
|
152
|
+
<label data-transport-fields-target="stdio">command <span class="muted">(stdio)</span>
|
|
153
|
+
<input type="text" name="command" placeholder="npx -y @modelcontextprotocol/server-...">
|
|
154
|
+
</label>
|
|
155
|
+
<label data-transport-fields-target="stdio">args <span class="muted">(stdio, one argv token per line)</span>
|
|
156
|
+
<textarea name="args" rows="3" placeholder="-y @modelcontextprotocol/server-filesystem /tmp"></textarea>
|
|
157
|
+
</label>
|
|
158
|
+
<label data-transport-fields-target="http">url <span class="muted">(http/sse)</span>
|
|
159
|
+
<input type="text" name="url" placeholder="https://mcp.example.com">
|
|
160
|
+
</label>
|
|
161
|
+
<label>description
|
|
162
|
+
<input type="text" name="description">
|
|
163
|
+
</label>
|
|
164
|
+
<label data-transport-fields-target="stdio">env <span class="muted">(stdio process env, one <code>KEY=value</code> per line)</span>
|
|
165
|
+
<textarea name="env" rows="4" placeholder="TAVILY_API_KEY=tvly-…"></textarea>
|
|
166
|
+
</label>
|
|
167
|
+
<label data-transport-fields-target="http">headers <span class="muted">(http/sse request headers, one <code>Name=value</code> per line)</span>
|
|
168
|
+
<textarea name="headers" rows="4" placeholder="Authorization=Bearer tvly-…"></textarea>
|
|
169
|
+
</label>
|
|
170
|
+
<div class="actions"><button type="submit" class="primary" data-turbo-submits-with="Creating…">Create instance</button></div>
|
|
171
|
+
</form>
|
|
58
172
|
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
</
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
</div>
|
|
73
|
-
<label class="check">
|
|
74
|
-
<input type="checkbox" name="enabled" value="1" checked>
|
|
75
|
-
enabled
|
|
76
|
-
</label>
|
|
77
|
-
<label>command <span class="muted">(stdio)</span>
|
|
78
|
-
<input type="text" name="command" placeholder="npx -y @modelcontextprotocol/server-...">
|
|
79
|
-
</label>
|
|
80
|
-
<label>url <span class="muted">(http/sse)</span>
|
|
81
|
-
<input type="text" name="url" placeholder="https://mcp.example.com">
|
|
82
|
-
</label>
|
|
83
|
-
<label>description
|
|
84
|
-
<input type="text" name="description">
|
|
85
|
-
</label>
|
|
86
|
-
<label>env <span class="muted">(one <code>KEY=value</code> per line)</span>
|
|
87
|
-
<textarea name="env" rows="4" placeholder="TAVILY_API_KEY=tvly-…"></textarea>
|
|
88
|
-
</label>
|
|
89
|
-
<div class="actions"><button type="submit" class="primary" data-turbo-submits-with="Creating…">Create instance</button></div>
|
|
90
|
-
</form>
|
|
91
|
-
</details>
|
|
173
|
+
<form method="post" action="/studio/mcp/import" data-turbo-frame="_top">
|
|
174
|
+
<%== csrf_tag %>
|
|
175
|
+
<label>mcpServers JSON <span class="muted">(paste a <code>{"mcpServers": {...}}</code> document — the same format Claude Desktop/Cursor use)</span>
|
|
176
|
+
<textarea name="json" rows="8" placeholder='{"mcpServers": {"tavily": {"url": "https://mcp.tavily.com/mcp", "headers": {"Authorization": "Bearer …"}}}}'></textarea>
|
|
177
|
+
</label>
|
|
178
|
+
<div class="actions"><button type="submit" class="primary" data-turbo-submits-with="Importing…">Import</button></div>
|
|
179
|
+
</form>
|
|
180
|
+
</div>
|
|
181
|
+
<% end %>
|
|
182
|
+
</turbo-frame>
|
|
183
|
+
<% unless frame_only %>
|
|
184
|
+
</div>
|
|
185
|
+
<% end %>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<header class="page-head">
|
|
2
|
+
<div>
|
|
3
|
+
<nav class="crumbs"><span>operate</span></nav>
|
|
4
|
+
<h1>Parity<span class="sub">the shadow experiment</span></h1>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="head-actions">
|
|
7
|
+
<%== agent_filter_form("/studio/parity", @agent) %>
|
|
8
|
+
</div>
|
|
9
|
+
</header>
|
|
10
|
+
|
|
11
|
+
<% if @criterion.nil? %>
|
|
12
|
+
<div class="empty">
|
|
13
|
+
No shadow channel is registered. Set <span class="mono">INSIKA_RELAY_SHADOW=1</span> and
|
|
14
|
+
restart — this page appears only when a channel is actually running in shadow.
|
|
15
|
+
</div>
|
|
16
|
+
<% elsif @pairs.empty? %>
|
|
17
|
+
<div class="empty">
|
|
18
|
+
No pairs yet. A mirrored exchange records a pair the moment our turn ends; the
|
|
19
|
+
incumbent's half lands when the mirror reports its reply
|
|
20
|
+
(<span class="mono">POST /channels/relay/shadow-reply</span>).
|
|
21
|
+
</div>
|
|
22
|
+
<% else %>
|
|
23
|
+
<% r = @report %>
|
|
24
|
+
<div class="card">
|
|
25
|
+
<div class="skill-desc">
|
|
26
|
+
<span class="pill <%= { pass: 'ok', fail: 'err', insufficient: 'warn', invalid: 'err' }.fetch(r.verdict, 'info') %>"><%= r.verdict %></span>
|
|
27
|
+
<span class="muted"><%= r.reason %></span>
|
|
28
|
+
</div>
|
|
29
|
+
<% sha_check = r.checks.find { |c| c.id == :criterion_sha } %>
|
|
30
|
+
<div class="skill-desc">
|
|
31
|
+
<span class="mono"><%= r.criterion_sha %></span>
|
|
32
|
+
<% if sha_check&.met %>
|
|
33
|
+
<span class="pill ok">frozen · matches every pair in the window</span>
|
|
34
|
+
<% else %>
|
|
35
|
+
<span class="pill err">NOT VERIFIED — the window's pairs do not all carry this frozen hash</span>
|
|
36
|
+
<% end %>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<h2>Checks<span class="sub">the arithmetic, not the conclusion</span></h2>
|
|
41
|
+
<div class="card">
|
|
42
|
+
<table class="grid">
|
|
43
|
+
<thead><tr><th>check</th><th>required</th><th>actual</th><th></th></tr></thead>
|
|
44
|
+
<tbody>
|
|
45
|
+
<% r.checks.each do |c| %>
|
|
46
|
+
<tr>
|
|
47
|
+
<td class="mono"><%= c.id %></td>
|
|
48
|
+
<td><%= c.required %></td>
|
|
49
|
+
<td><% if c.actual.is_a?(Hash) %><%= c.actual.map { |k, v| "#{k}: #{v}" }.join(" · ") %><% else %><%= c.actual %><% end %></td>
|
|
50
|
+
<td><span class="pill <%= c.met ? 'ok' : 'err' %>"><%= c.met ? "met" : "no" %></span></td>
|
|
51
|
+
</tr>
|
|
52
|
+
<% end %>
|
|
53
|
+
</tbody>
|
|
54
|
+
</table>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<h2>Pairs / day<span class="sub">floor <%= @criterion.rule.pairs_per_day %> · a gap day invalidates the window</span></h2>
|
|
58
|
+
<div class="card">
|
|
59
|
+
<table class="grid">
|
|
60
|
+
<thead><tr><th>date</th><th>pairs</th><th>decided</th><th></th></tr></thead>
|
|
61
|
+
<tbody>
|
|
62
|
+
<% r.daily.each do |d| %>
|
|
63
|
+
<tr>
|
|
64
|
+
<td class="mono"><%= d[:date] %></td>
|
|
65
|
+
<td><%= d[:pairs] %></td>
|
|
66
|
+
<td><%= d[:decided] %></td>
|
|
67
|
+
<td><% if d[:floor].zero? %><span class="muted">partial day — reported, not required</span><% elsif d[:pairs] >= d[:floor] %><span class="pill ok">met</span><% else %><span class="pill err">below floor</span><% end %></td>
|
|
68
|
+
</tr>
|
|
69
|
+
<% end %>
|
|
70
|
+
</tbody>
|
|
71
|
+
</table>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<% if r.counts %>
|
|
75
|
+
<h2>Per store<span class="sub">a store blocks its own cut even when the aggregate passes</span></h2>
|
|
76
|
+
<div class="card">
|
|
77
|
+
<table class="grid">
|
|
78
|
+
<thead><tr><th>store</th><th>decided</th><th>win-or-tie</th><th>worse</th><th>meets floor</th></tr></thead>
|
|
79
|
+
<tbody>
|
|
80
|
+
<% r.per_agent.each do |agent, stats| %>
|
|
81
|
+
<tr>
|
|
82
|
+
<td class="mono"><%= agent %></td>
|
|
83
|
+
<td><%= stats[:decided] %></td>
|
|
84
|
+
<td><%= stats[:win_or_tie] %></td>
|
|
85
|
+
<td><%= stats[:worse] %></td>
|
|
86
|
+
<td><% if stats[:meets].nil? %><span class="muted">below per-store minimum</span><% elsif stats[:meets] %><span class="pill ok">yes</span><% else %><span class="pill err">no</span><% end %></td>
|
|
87
|
+
</tr>
|
|
88
|
+
<% end %>
|
|
89
|
+
</tbody>
|
|
90
|
+
</table>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<h2>Judge agreement<span class="sub">split / unknown / order-dependent</span></h2>
|
|
94
|
+
<div class="card">
|
|
95
|
+
<div class="skill-desc">
|
|
96
|
+
<span class="pill <%= r.counts[:split].positive? ? 'warn' : 'ok' %>"><%= r.counts[:split] %> split</span>
|
|
97
|
+
<span class="pill <%= r.counts[:unknown].positive? ? 'warn' : 'ok' %>"><%= r.counts[:unknown] %> unknown</span>
|
|
98
|
+
<span class="pill <%= @agreement[:order_dependent].positive? ? 'warn' : 'ok' %>"><%= @agreement[:order_dependent] %> flipped with order</span>
|
|
99
|
+
<span class="muted">of <%= r.counts[:judged] %> judged pairs</span>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<% end %>
|
|
103
|
+
|
|
104
|
+
<h2>Judge a batch</h2>
|
|
105
|
+
<div class="card">
|
|
106
|
+
<form method="post" action="/studio/parity" class="actions">
|
|
107
|
+
<%== csrf_tag %>
|
|
108
|
+
<label>
|
|
109
|
+
Agent (all stores when blank)
|
|
110
|
+
<input type="text" name="agent" placeholder="agent-id">
|
|
111
|
+
</label>
|
|
112
|
+
<label>
|
|
113
|
+
Limit
|
|
114
|
+
<input type="number" name="limit" value="50" min="1" max="200">
|
|
115
|
+
</label>
|
|
116
|
+
<button type="submit" class="primary">Judge <%= @pending %> complete pair(s)</button>
|
|
117
|
+
</form>
|
|
118
|
+
<div class="skill-desc muted">
|
|
119
|
+
Cost: <span class="mono"><%= @judge_cost %></span> provider calls
|
|
120
|
+
(<%= @pending %> pairs × <%= @criterion.rule.min_judge_models %> judges × 2 orders).
|
|
121
|
+
<% if Array((@judge_models || {})["judges"]).empty? %>
|
|
122
|
+
<span class="pill err">no judges configured in settings['evals'] — the press will refuse</span>
|
|
123
|
+
<% end %>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<h2>Recent pairs<span class="sub">inbound · incumbent · insika</span></h2>
|
|
128
|
+
<div class="card">
|
|
129
|
+
<ul class="history">
|
|
130
|
+
<% @pairs.each do |p| %>
|
|
131
|
+
<li>
|
|
132
|
+
<span class="pill <%= { open: 'warn', complete: 'info', silent: 'info', judged: 'ok', incomplete: 'err' }.fetch(p.status, 'info') %>"><%= p.status %></span>
|
|
133
|
+
<span class="mono"><%= p.id[0, 12] %></span>
|
|
134
|
+
<% if p.agent %><span class="mono"><%= p.agent %></span><% end %>
|
|
135
|
+
<% if p.outcome %><span class="pill"><%= p.outcome %></span><% end %>
|
|
136
|
+
<% if p.session_id %><a class="mono" href="/studio/sessions/<%= Rack::Utils.escape(p.session_id) %>">session →</a><% end %>
|
|
137
|
+
<div class="skill-desc muted"><strong>customer:</strong> <%= p.inbound.to_s %></div>
|
|
138
|
+
<div class="skill-desc"><strong>incumbent:</strong> <%= p.incumbent_reply.to_s %></div>
|
|
139
|
+
<div class="skill-desc"><strong>insika:</strong> <%= p.insika_reply.to_s %></div>
|
|
140
|
+
<% if p.verdict.is_a?(Hash) && presence(p.verdict["reason"]) %>
|
|
141
|
+
<div class="skill-desc muted">why: <%= p.verdict["reason"] %></div>
|
|
142
|
+
<% end %>
|
|
143
|
+
</li>
|
|
144
|
+
<% end %>
|
|
145
|
+
</ul>
|
|
146
|
+
</div>
|
|
147
|
+
<% end %>
|
|
@@ -15,7 +15,13 @@
|
|
|
15
15
|
</select>
|
|
16
16
|
</label>
|
|
17
17
|
<label class="chat-field">session
|
|
18
|
-
<input type="text" name="session_id" value="<%= @session_id %>" placeholder="empty = new conversation"
|
|
18
|
+
<input type="text" name="session_id" value="<%= @session_id %>" placeholder="empty = new conversation"
|
|
19
|
+
list="recent-sessions" autocomplete="off">
|
|
20
|
+
<datalist id="recent-sessions">
|
|
21
|
+
<% Array(@recent).each do |s| %>
|
|
22
|
+
<option value="<%= s.id %>" label="<%= [time_ago(s.updated_at), presence(session_preview(s).to_s[0, 60])].compact.join(" · ") %>">
|
|
23
|
+
<% end %>
|
|
24
|
+
</datalist>
|
|
19
25
|
</label>
|
|
20
26
|
<span class="pill info chat-status" data-live-transcript-target="status">disconnected</span>
|
|
21
27
|
<details class="pin chat-pin">
|