insika 0.3.0 → 0.8.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 +296 -0
- data/README.md +48 -12
- data/bin/insika +725 -0
- data/bin/insika-router +87 -0
- data/docs/AGENTS.md +116 -406
- data/docs/API.md +5 -5
- data/docs/ARCHITECTURE.md +3 -2
- data/docs/ARTIFACTS.md +137 -0
- data/docs/BENCHMARK.md +2 -2
- data/docs/CHANNELS.md +14 -14
- data/docs/CONTEXT.md +63 -19
- data/docs/DEMO.md +80 -0
- data/docs/DEPLOY.md +87 -10
- data/docs/EMBEDDING.md +1 -1
- data/docs/EVALS.md +128 -3
- data/docs/FACTS.md +3 -3
- data/docs/HARVEST.md +5 -6
- data/docs/KNOWLEDGE.md +290 -0
- data/docs/LOADTEST.md +17 -29
- data/docs/MEDIA.md +128 -0
- data/docs/OBSERVABILITY.md +46 -12
- data/docs/OUTCOMES.md +137 -0
- data/docs/PLUGINS.md +51 -6
- data/docs/POLICY.md +222 -0
- data/docs/REFINEMENT.md +14 -9
- data/docs/RELEASING.md +4 -4
- data/docs/ROUTER.md +213 -0
- data/docs/RUNNING-LOCAL.md +5 -5
- data/docs/SCHEDULING.md +121 -0
- data/docs/SECURITY.md +23 -7
- data/docs/SKILLS.md +11 -2
- data/docs/SOAK.md +3 -3
- data/docs/TEMPLATES.md +134 -0
- data/docs/TOOLS.md +176 -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 +4 -4
- data/docs/improve.md +20 -0
- data/docs/index.md +8 -5
- 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 +99 -17
- data/lib/insika/artifact_signing.rb +82 -0
- data/lib/insika/artifact_store.rb +160 -0
- data/lib/insika/channel_delivery.rb +1 -1
- data/lib/insika/chat_builder.rb +50 -19
- data/lib/insika/commands/agent_payload.rb +2 -2
- data/lib/insika/commands/backfill_knowledge.rb +145 -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 +15 -3
- data/lib/insika/commands/gate_refinement.rb +1 -1
- data/lib/insika/commands/refresh_mcp_tools.rb +47 -0
- data/lib/insika/commands/restore_concept.rb +34 -0
- data/lib/insika/commands/seed_demo_data.rb +31 -0
- data/lib/insika/commands/upsert_mcp.rb +6 -3
- data/lib/insika/commands/write_concept.rb +57 -0
- data/lib/insika/compaction.rb +196 -0
- data/lib/insika/context/builder.rb +6 -2
- data/lib/insika/context/fragment.rb +4 -1
- data/lib/insika/context/priority.rb +8 -0
- data/lib/insika/context/providers/briefing.rb +53 -24
- data/lib/insika/context/providers/knowledge.rb +108 -0
- data/lib/insika/context/providers/prompt.rb +30 -24
- data/lib/insika/context/providers/session.rb +46 -10
- data/lib/insika/context_trace_store.rb +11 -1
- 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/doctor.rb +280 -17
- data/lib/insika/dsl/definition.rb +3 -2
- data/lib/insika/dsl/runtime.rb +64 -79
- data/lib/insika/dsl/server_boot.rb +23 -1
- data/lib/insika/dsl/system.rb +10 -2
- data/lib/insika/dsl.rb +103 -2
- data/lib/insika/env_schema.rb +21 -7
- data/lib/insika/evals/golden.rb +41 -4
- data/lib/insika/evals/judge.rb +47 -2
- data/lib/insika/evals/pairwise.rb +11 -0
- 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 +84 -2
- data/lib/insika/event_stream.rb +10 -0
- data/lib/insika/executor.rb +295 -55
- data/lib/insika/followup_policy.rb +2 -25
- data/lib/insika/golden_store.rb +16 -1
- data/lib/insika/grounding/matcher.rb +1 -1
- data/lib/insika/knowledge.rb +680 -0
- data/lib/insika/knowledge_store.rb +140 -0
- data/lib/insika/loop_detector.rb +5 -34
- 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 +115 -31
- data/lib/insika/message_origin.rb +1 -1
- data/lib/insika/middleware.rb +9 -0
- data/lib/insika/onboarding.rb +17 -1
- data/lib/insika/outcome_store.rb +1 -1
- data/lib/insika/overlay_tool_registry.rb +37 -17
- data/lib/insika/packaging.rb +2 -2
- data/lib/insika/profile_source.rb +15 -1
- data/lib/insika/prompt_catalog.rb +10 -0
- data/lib/insika/retention.rb +36 -1
- 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/schedule.rb +177 -0
- data/lib/insika/schedule_engine.rb +314 -0
- data/lib/insika/schedule_store.rb +208 -0
- data/lib/insika/server/app.rb +105 -15
- data/lib/insika/server/rack_app.rb +5 -1
- data/lib/insika/server/responses.rb +5 -5
- data/lib/insika/session_store.rb +34 -4
- data/lib/insika/settings_store.rb +8 -1
- data/lib/insika/skill_catalog.rb +12 -0
- data/lib/insika/soak/runner.rb +4 -4
- data/lib/insika/steer_injector.rb +21 -10
- data/lib/insika/studio/app.rb +591 -47
- data/lib/insika/studio/assets/dist/application.css +1 -1
- data/lib/insika/studio/assets/dist/application.js +21 -21
- data/lib/insika/studio/forms.rb +57 -5
- data/lib/insika/studio/nav_icons.rb +14 -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 +61 -820
- data/lib/insika/studio/views/agents.erb +70 -57
- data/lib/insika/studio/views/artifact.erb +23 -0
- data/lib/insika/studio/views/artifacts.erb +59 -0
- data/lib/insika/studio/views/evals.erb +2 -2
- data/lib/insika/studio/views/facts.erb +1 -1
- data/lib/insika/studio/views/funnel.erb +1 -1
- data/lib/insika/studio/views/home.erb +106 -67
- data/lib/insika/studio/views/knowledge.erb +123 -0
- data/lib/insika/studio/views/layout.erb +14 -11
- data/lib/insika/studio/views/mcp.erb +174 -80
- data/lib/insika/studio/views/session.erb +231 -177
- data/lib/insika/studio/views/settings.erb +50 -1
- data/lib/insika/studio/views/skills.erb +1 -1
- data/lib/insika/studio/views/tools.erb +24 -9
- data/lib/insika/telemetry/recorder.rb +49 -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 +47 -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 +68 -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/tick.rb +24 -12
- data/lib/insika/timezone.rb +45 -0
- data/lib/insika/tool_batch.rb +67 -0
- data/lib/insika/tool_usage_report.rb +162 -0
- data/lib/insika/tools/generate_image.rb +52 -7
- 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/turn_budget.rb +91 -0
- data/lib/insika/turn_output.rb +1 -1
- data/lib/insika/turn_state.rb +15 -4
- data/lib/insika/version.rb +1 -1
- data/lib/insika/wiring/graph.rb +184 -12
- data/lib/insika/wiring/graph_chat.rb +102 -0
- data/lib/insika.rb +64 -0
- metadata +109 -5
- data/docs/build.md +0 -14
- data/docs/understand.md +0 -10
data/lib/insika/studio/forms.rb
CHANGED
|
@@ -30,6 +30,21 @@ module Studio
|
|
|
30
30
|
limits[field.to_sym] = v
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
|
+
# Burst policy (QueuePolicy). Opt-in keys, same convention as the edge
|
|
34
|
+
# overrides above: blank DELETES (inherit platform / DEFAULTS — followup,
|
|
35
|
+
# steer_max 5); an explicit value pins it for this agent. steer_max 0 is
|
|
36
|
+
# a legitimate "never steer" and is stored, not deleted.
|
|
37
|
+
if (qm = presence(r.params["queue_mode"]))
|
|
38
|
+
unless Insika::QueuePolicy::MODES.map(&:to_s).include?(qm)
|
|
39
|
+
raise Insika::ValidationError, "queue_mode must be one of: #{Insika::QueuePolicy::MODES.join(', ')}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
limits[:queue_mode] = qm
|
|
43
|
+
else
|
|
44
|
+
limits.delete(:queue_mode)
|
|
45
|
+
end
|
|
46
|
+
smm = edge_int(r.params["steer_max_messages"], "steer_max_messages")
|
|
47
|
+
smm.nil? ? limits.delete(:steer_max_messages) : limits[:steer_max_messages] = smm
|
|
33
48
|
{
|
|
34
49
|
id: @agent.id,
|
|
35
50
|
model: presence(r.params["model"]),
|
|
@@ -44,6 +59,7 @@ module Studio
|
|
|
44
59
|
grounding: grounding_patch(r),
|
|
45
60
|
funnel: funnel_patch(r),
|
|
46
61
|
followup: followup_patch(r),
|
|
62
|
+
schedules: schedules_patch(r),
|
|
47
63
|
distill: distill_patch(r),
|
|
48
64
|
harvest: harvest_patch(r),
|
|
49
65
|
refinement: refinement_patch(r),
|
|
@@ -51,6 +67,7 @@ module Studio
|
|
|
51
67
|
reliability: reliability_patch(r),
|
|
52
68
|
routes: json_patch(r, "routes"),
|
|
53
69
|
outputs: json_patch(r, "outputs"),
|
|
70
|
+
stt_prompt: presence(r.params["stt_prompt"]),
|
|
54
71
|
metadata: json_patch(r, "metadata") || {},
|
|
55
72
|
alerts: alerts_patch(r),
|
|
56
73
|
edge_stream: edge_stream_patch(r),
|
|
@@ -124,6 +141,20 @@ module Studio
|
|
|
124
141
|
{ "arm" => arm, "policy" => policy }
|
|
125
142
|
end
|
|
126
143
|
|
|
144
|
+
# recurring schedules — a JSON array of declarations
|
|
145
|
+
# (name / trigger / tz / message / session mode / overrides / enabled).
|
|
146
|
+
# Same honest shape as routes/metadata: the list is unbounded, and a
|
|
147
|
+
# JSON textarea is the CSP-safe editor. Blank = no schedules (parity).
|
|
148
|
+
def schedules_patch(r)
|
|
149
|
+
parsed = json_block(r.params["schedules"], "schedules")
|
|
150
|
+
return nil if parsed.nil?
|
|
151
|
+
|
|
152
|
+
parsed = parsed.is_a?(Hash) ? [parsed] : parsed
|
|
153
|
+
raise Insika::ValidationError, "schedules must be an array of declarations" unless parsed.is_a?(Array)
|
|
154
|
+
|
|
155
|
+
parsed
|
|
156
|
+
end
|
|
157
|
+
|
|
127
158
|
# distillation — enabled checkbox + the forge's knobs. Blank
|
|
128
159
|
# prompt/model drop the keys (the engine defaults take over).
|
|
129
160
|
def distill_patch(r)
|
|
@@ -430,6 +461,17 @@ module Studio
|
|
|
430
461
|
# per-tenant map in the record — the view says so.
|
|
431
462
|
v = presence(r.params["memory_ttl_days"])
|
|
432
463
|
patch["memory_ttl_days"] = v.nil? ? nil : Integer(v)
|
|
464
|
+
# In-session compaction (RFC-0044). The checkbox is authoritative on
|
|
465
|
+
# this form (unchecked = disable); the numbers keep their stored value
|
|
466
|
+
# when cleared (deep_merge — the defaults backstop a fresh record);
|
|
467
|
+
# model blank = nil = the platform utility_model.
|
|
468
|
+
compaction = { "enabled" => r.params["compaction_enabled"] == "1",
|
|
469
|
+
"model" => presence(r.params["compaction_model"]) }
|
|
470
|
+
%w[keep_last compact_after].each do |f|
|
|
471
|
+
v = presence(r.params["compaction_#{f}"])
|
|
472
|
+
compaction[f] = Integer(v) if v
|
|
473
|
+
end
|
|
474
|
+
patch["compaction"] = compaction
|
|
433
475
|
patch
|
|
434
476
|
end
|
|
435
477
|
|
|
@@ -559,19 +601,24 @@ module Studio
|
|
|
559
601
|
}
|
|
560
602
|
end
|
|
561
603
|
|
|
562
|
-
# MCP instance from the form
|
|
563
|
-
#
|
|
564
|
-
#
|
|
565
|
-
#
|
|
604
|
+
# MCP instance from the form (transport-aware, but the
|
|
605
|
+
# form always submits every field regardless of which the JS shows;
|
|
606
|
+
# McpStore stores them all either way). `env`/`headers` come as
|
|
607
|
+
# "KEY=value" lines, `args` as one argv token per line (CSP forbids
|
|
608
|
+
# inline JS for add/remove line; a textarea is the simple, honest path).
|
|
609
|
+
# Masked credential values come back as a sentinel — keeping them
|
|
610
|
+
# preserves the secret; changing replaces; deleting the line clears it.
|
|
566
611
|
def mcp_patch(r)
|
|
567
612
|
{
|
|
568
613
|
name: presence(r.params["name"]),
|
|
569
614
|
transport: presence(r.params["transport"]) || "stdio",
|
|
570
615
|
command: r.params["command"].to_s,
|
|
616
|
+
args: split_lines(r.params["args"]),
|
|
571
617
|
url: r.params["url"].to_s,
|
|
572
618
|
description: r.params["description"].to_s,
|
|
573
619
|
enabled: r.params["enabled"] == "1",
|
|
574
|
-
env: parse_kv_lines(r.params["env"])
|
|
620
|
+
env: parse_kv_lines(r.params["env"]),
|
|
621
|
+
headers: parse_kv_lines(r.params["headers"])
|
|
575
622
|
}
|
|
576
623
|
end
|
|
577
624
|
|
|
@@ -588,5 +635,10 @@ module Studio
|
|
|
588
635
|
[k, v.to_s.strip]
|
|
589
636
|
end.to_h
|
|
590
637
|
end
|
|
638
|
+
|
|
639
|
+
# One argv token per line (stdio's `args`). Ignores blank lines/comments.
|
|
640
|
+
def split_lines(text)
|
|
641
|
+
text.to_s.each_line.map(&:strip).reject { |l| l.empty? || l.start_with?("#") }
|
|
642
|
+
end
|
|
591
643
|
end
|
|
592
644
|
end
|
|
@@ -25,12 +25,25 @@ module Studio
|
|
|
25
25
|
funnel: '<path d="M3 5h18l-8 9v6l-2 1v-7z"/>',
|
|
26
26
|
followups: '<path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/>',
|
|
27
27
|
facts: '<path d="M7 3h11a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H7z"/><path d="M7 3v18"/><path d="M11 8h5"/><path d="M11 12h5"/><path d="M11 16h3"/>',
|
|
28
|
-
harvest: '<path d="M4 20V10"/><path d="M20 20V4"/><path d="M4 10h4v10"/><path d="M20 4h-4v16"/><path d="M4 20h16"/><path d="m8 8 4-4 4 4"/>'
|
|
28
|
+
harvest: '<path d="M4 20V10"/><path d="M20 20V4"/><path d="M4 10h4v10"/><path d="M20 4h-4v16"/><path d="M4 20h16"/><path d="m8 8 4-4 4 4"/>',
|
|
29
|
+
knowledge: '<path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 12.7V17h8v-2.3A7 7 0 0 0 12 2z"/>',
|
|
30
|
+
artifacts: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="m9.5 13 2 2 3.5-3.5"/><path d="M12 15v-4"/>'
|
|
29
31
|
}.freeze
|
|
30
32
|
|
|
31
33
|
def nav_icon(key)
|
|
32
34
|
%(<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" ) +
|
|
33
35
|
%(stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">#{NAV_ICONS[key]}</svg>)
|
|
34
36
|
end
|
|
37
|
+
|
|
38
|
+
# Transcript event-chip icons: a tool call, a result, a
|
|
39
|
+
# skill, an artifact, a guardrail — each reuses a nav icon (same inline-SVG
|
|
40
|
+
# grammar, CSP-safe). Sized by the chip CSS, colored per kind.
|
|
41
|
+
CHIP_ICONS = { call: :tools, result: :approvals, skill: :skills,
|
|
42
|
+
artifact: :artifacts, guardrail: :system }.freeze
|
|
43
|
+
|
|
44
|
+
def chip_icon(kind)
|
|
45
|
+
key = CHIP_ICONS[kind]
|
|
46
|
+
key ? %(<span class="chip-ic">#{nav_icon(key)}</span>) : ""
|
|
47
|
+
end
|
|
35
48
|
end
|
|
36
49
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!-- ── Cache ─────────────────────────────────────────────────── -->
|
|
2
|
+
<section class="section" id="cache" data-tabs-target="panel">
|
|
3
|
+
<div class="section-head">
|
|
4
|
+
<h2>Cache</h2>
|
|
5
|
+
<p class="muted">prefix cache-hit per turn (provider-reported) — the invalidation reason names the first block whose bytes changed vs the previous turn.</p>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="card">
|
|
8
|
+
<% if @cache_series.empty? %>
|
|
9
|
+
<p class="muted">No turns recorded.</p>
|
|
10
|
+
<% else %>
|
|
11
|
+
<ul class="kv">
|
|
12
|
+
<% @cache_series.last(20).reverse.each do |c| %>
|
|
13
|
+
<li>
|
|
14
|
+
<span class="k mono"><%= c["at"] %></span>
|
|
15
|
+
<span class="v">
|
|
16
|
+
<%== c["hit_pct"].nil? ? "—" : "#{c['hit_pct']}% cached" %>
|
|
17
|
+
· <%= c["cached_tokens"] %>/<%= c["prompt_tokens"] %> tokens
|
|
18
|
+
<% if c["invalidation_reason"] %><span class="lat">broke: <%= c["invalidation_reason"] %></span><% end %>
|
|
19
|
+
</span>
|
|
20
|
+
</li>
|
|
21
|
+
<% end %>
|
|
22
|
+
</ul>
|
|
23
|
+
<% end %>
|
|
24
|
+
</div>
|
|
25
|
+
</section>
|