insika 0.3.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.
Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +180 -0
  3. data/README.md +45 -10
  4. data/bin/insika +684 -0
  5. data/bin/insika-router +87 -0
  6. data/docs/AGENTS.md +94 -403
  7. data/docs/API.md +5 -5
  8. data/docs/ARCHITECTURE.md +3 -2
  9. data/docs/ARTIFACTS.md +95 -0
  10. data/docs/BENCHMARK.md +2 -2
  11. data/docs/CHANNELS.md +14 -14
  12. data/docs/CONTEXT.md +9 -7
  13. data/docs/DEMO.md +80 -0
  14. data/docs/DEPLOY.md +71 -3
  15. data/docs/EMBEDDING.md +1 -1
  16. data/docs/EVALS.md +128 -3
  17. data/docs/FACTS.md +3 -3
  18. data/docs/HARVEST.md +5 -6
  19. data/docs/KNOWLEDGE.md +290 -0
  20. data/docs/LOADTEST.md +2 -2
  21. data/docs/MEDIA.md +128 -0
  22. data/docs/OBSERVABILITY.md +15 -10
  23. data/docs/OUTCOMES.md +137 -0
  24. data/docs/PLUGINS.md +51 -6
  25. data/docs/POLICY.md +216 -0
  26. data/docs/REFINEMENT.md +14 -9
  27. data/docs/RELEASING.md +4 -4
  28. data/docs/ROUTER.md +213 -0
  29. data/docs/RUNNING-LOCAL.md +3 -3
  30. data/docs/SCHEDULING.md +121 -0
  31. data/docs/SECURITY.md +22 -6
  32. data/docs/SKILLS.md +11 -2
  33. data/docs/SOAK.md +2 -2
  34. data/docs/TEMPLATES.md +134 -0
  35. data/docs/TOOLS.md +152 -27
  36. data/docs/WHY.md +1 -1
  37. data/docs/WORKFLOWS.md +2 -2
  38. data/docs/_includes/head_custom.html +5 -0
  39. data/docs/_includes/title.html +13 -0
  40. data/docs/_sass/color_schemes/insika.scss +32 -0
  41. data/docs/_sass/custom/custom.scss +199 -0
  42. data/docs/_sass/custom/setup.scss +26 -0
  43. data/docs/assets/img/favicon.svg +7 -0
  44. data/docs/assets/img/insika-mark.svg +7 -0
  45. data/docs/core-concepts.md +21 -0
  46. data/docs/domain.md +4 -4
  47. data/docs/improve.md +20 -0
  48. data/docs/index.md +8 -5
  49. data/docs/integrate.md +20 -0
  50. data/docs/operate.md +13 -6
  51. data/docs/prompts/ADD-TOOL.md +118 -0
  52. data/docs/prompts/DIAGNOSE-TURN.md +65 -0
  53. data/docs/prompts/GO-LIVE.md +138 -0
  54. data/docs/prompts/RUN-EXAMPLES.md +70 -0
  55. data/docs/reference.md +19 -0
  56. data/docs/ship.md +10 -2
  57. data/docs/start-here.md +18 -0
  58. data/lib/insika/agent_profile.rb +73 -16
  59. data/lib/insika/artifact_signing.rb +82 -0
  60. data/lib/insika/artifact_store.rb +160 -0
  61. data/lib/insika/channel_delivery.rb +1 -1
  62. data/lib/insika/chat_builder.rb +22 -2
  63. data/lib/insika/commands/agent_payload.rb +2 -2
  64. data/lib/insika/commands/backfill_knowledge.rb +145 -0
  65. data/lib/insika/commands/delete_artifact.rb +35 -0
  66. data/lib/insika/commands/delete_concept.rb +34 -0
  67. data/lib/insika/commands/delete_mcp.rb +6 -2
  68. data/lib/insika/commands/delete_tenant_data.rb +15 -3
  69. data/lib/insika/commands/gate_refinement.rb +1 -1
  70. data/lib/insika/commands/refresh_mcp_tools.rb +47 -0
  71. data/lib/insika/commands/restore_concept.rb +34 -0
  72. data/lib/insika/commands/seed_demo_data.rb +31 -0
  73. data/lib/insika/commands/upsert_mcp.rb +6 -3
  74. data/lib/insika/commands/write_concept.rb +57 -0
  75. data/lib/insika/context/priority.rb +2 -0
  76. data/lib/insika/context/providers/knowledge.rb +108 -0
  77. data/lib/insika/context/providers/prompt.rb +30 -24
  78. data/lib/insika/cron.rb +189 -0
  79. data/lib/insika/demo/agent_attrs.rb +43 -0
  80. data/lib/insika/demo/golden_cases.rb +81 -0
  81. data/lib/insika/demo/seeder.rb +336 -0
  82. data/lib/insika/doctor.rb +176 -8
  83. data/lib/insika/dsl/definition.rb +3 -2
  84. data/lib/insika/dsl/runtime.rb +60 -79
  85. data/lib/insika/dsl/server_boot.rb +23 -1
  86. data/lib/insika/dsl/system.rb +10 -2
  87. data/lib/insika/dsl.rb +103 -2
  88. data/lib/insika/env_schema.rb +16 -1
  89. data/lib/insika/evals/golden.rb +41 -4
  90. data/lib/insika/evals/judge.rb +47 -2
  91. data/lib/insika/evals/pairwise.rb +11 -0
  92. data/lib/insika/evals/persona.rb +98 -0
  93. data/lib/insika/evals/runner.rb +9 -0
  94. data/lib/insika/evals/simulator.rb +225 -0
  95. data/lib/insika/evals/transport.rb +83 -1
  96. data/lib/insika/event_stream.rb +10 -0
  97. data/lib/insika/executor.rb +231 -55
  98. data/lib/insika/followup_policy.rb +2 -25
  99. data/lib/insika/golden_store.rb +16 -1
  100. data/lib/insika/grounding/matcher.rb +1 -1
  101. data/lib/insika/knowledge.rb +680 -0
  102. data/lib/insika/knowledge_store.rb +140 -0
  103. data/lib/insika/mcp_client.rb +94 -0
  104. data/lib/insika/mcp_json.rb +74 -0
  105. data/lib/insika/mcp_live_tool.rb +43 -0
  106. data/lib/insika/mcp_store.rb +98 -26
  107. data/lib/insika/mcp_tool_ingestor.rb +30 -8
  108. data/lib/insika/mcp_tool_registry.rb +100 -0
  109. data/lib/insika/media.rb +115 -31
  110. data/lib/insika/message_origin.rb +1 -1
  111. data/lib/insika/middleware.rb +9 -0
  112. data/lib/insika/onboarding.rb +17 -1
  113. data/lib/insika/outcome_store.rb +1 -1
  114. data/lib/insika/overlay_tool_registry.rb +37 -17
  115. data/lib/insika/packaging.rb +2 -2
  116. data/lib/insika/profile_source.rb +8 -1
  117. data/lib/insika/prompt_catalog.rb +10 -0
  118. data/lib/insika/retention.rb +36 -1
  119. data/lib/insika/router/app.rb +157 -0
  120. data/lib/insika/router/backend_pool.rb +98 -0
  121. data/lib/insika/router/hash_ring.rb +55 -0
  122. data/lib/insika/router/proxy_body.rb +34 -0
  123. data/lib/insika/router/session_key.rb +54 -0
  124. data/lib/insika/router.rb +18 -0
  125. data/lib/insika/schedule.rb +177 -0
  126. data/lib/insika/schedule_engine.rb +314 -0
  127. data/lib/insika/schedule_store.rb +208 -0
  128. data/lib/insika/server/app.rb +105 -15
  129. data/lib/insika/server/rack_app.rb +5 -1
  130. data/lib/insika/server/responses.rb +1 -1
  131. data/lib/insika/skill_catalog.rb +12 -0
  132. data/lib/insika/steer_injector.rb +21 -10
  133. data/lib/insika/studio/app.rb +567 -45
  134. data/lib/insika/studio/assets/dist/application.css +1 -1
  135. data/lib/insika/studio/assets/dist/application.js +21 -21
  136. data/lib/insika/studio/forms.rb +46 -5
  137. data/lib/insika/studio/nav_icons.rb +14 -1
  138. data/lib/insika/studio/views/_agent_tab_cache.erb +25 -0
  139. data/lib/insika/studio/views/_agent_tab_config.erb +514 -0
  140. data/lib/insika/studio/views/_agent_tab_history.erb +24 -0
  141. data/lib/insika/studio/views/_agent_tab_loops.erb +54 -0
  142. data/lib/insika/studio/views/_agent_tab_memory.erb +51 -0
  143. data/lib/insika/studio/views/_agent_tab_outcomes.erb +31 -0
  144. data/lib/insika/studio/views/_agent_tab_prompts.erb +108 -0
  145. data/lib/insika/studio/views/_agent_tab_skills.erb +38 -0
  146. data/lib/insika/studio/views/_agents_master.erb +44 -0
  147. data/lib/insika/studio/views/_message.erb +49 -32
  148. data/lib/insika/studio/views/agent_detail.erb +61 -820
  149. data/lib/insika/studio/views/agents.erb +70 -57
  150. data/lib/insika/studio/views/artifact.erb +23 -0
  151. data/lib/insika/studio/views/artifacts.erb +59 -0
  152. data/lib/insika/studio/views/evals.erb +2 -2
  153. data/lib/insika/studio/views/facts.erb +1 -1
  154. data/lib/insika/studio/views/funnel.erb +1 -1
  155. data/lib/insika/studio/views/home.erb +106 -67
  156. data/lib/insika/studio/views/knowledge.erb +123 -0
  157. data/lib/insika/studio/views/layout.erb +14 -11
  158. data/lib/insika/studio/views/mcp.erb +174 -80
  159. data/lib/insika/studio/views/session.erb +231 -177
  160. data/lib/insika/studio/views/settings.erb +39 -1
  161. data/lib/insika/studio/views/skills.erb +1 -1
  162. data/lib/insika/studio/views/tools.erb +24 -9
  163. data/lib/insika/templates/browser-agent/README.md +36 -0
  164. data/lib/insika/templates/browser-agent/agent.rb +49 -0
  165. data/lib/insika/templates/daily-digest/README.md +38 -0
  166. data/lib/insika/templates/daily-digest/agent.rb +77 -0
  167. data/lib/insika/templates/repo-explorer/README.md +36 -0
  168. data/lib/insika/templates/repo-explorer/agent.rb +45 -0
  169. data/lib/insika/templates/research-analyst/README.md +26 -0
  170. data/lib/insika/templates/research-analyst/agent.rb +58 -0
  171. data/lib/insika/templates/review-panel/README.md +20 -0
  172. data/lib/insika/templates/review-panel/agent.rb +50 -0
  173. data/lib/insika/templates/travel-planner/README.md +35 -0
  174. data/lib/insika/templates/travel-planner/agent.rb +87 -0
  175. data/lib/insika/templates.rb +112 -0
  176. data/lib/insika/tick.rb +24 -12
  177. data/lib/insika/timezone.rb +45 -0
  178. data/lib/insika/tools/generate_image.rb +52 -7
  179. data/lib/insika/tools/load_knowledge.rb +74 -0
  180. data/lib/insika/tools/run_persona_eval.rb +328 -0
  181. data/lib/insika/tools/save_artifact.rb +95 -0
  182. data/lib/insika/turn_output.rb +1 -1
  183. data/lib/insika/turn_state.rb +15 -4
  184. data/lib/insika/version.rb +1 -1
  185. data/lib/insika/wiring/graph.rb +184 -12
  186. data/lib/insika/wiring/graph_chat.rb +102 -0
  187. data/lib/insika.rb +57 -0
  188. metadata +105 -5
  189. data/docs/build.md +0 -14
  190. data/docs/understand.md +0 -10
@@ -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)
@@ -559,19 +590,24 @@ module Studio
559
590
  }
560
591
  end
561
592
 
562
- # MCP instance from the form. `env` comes as "KEY=value" lines (CSP
563
- # forbids inline JS for add/remove line; a textarea is the simple, honest
564
- # path). Masked values come back as a sentinel — keeping them preserves
565
- # the secret; changing replaces; deleting the line clears it.
593
+ # MCP instance from the form (transport-aware, but the
594
+ # form always submits every field regardless of which the JS shows;
595
+ # McpStore stores them all either way). `env`/`headers` come as
596
+ # "KEY=value" lines, `args` as one argv token per line (CSP forbids
597
+ # inline JS for add/remove line; a textarea is the simple, honest path).
598
+ # Masked credential values come back as a sentinel — keeping them
599
+ # preserves the secret; changing replaces; deleting the line clears it.
566
600
  def mcp_patch(r)
567
601
  {
568
602
  name: presence(r.params["name"]),
569
603
  transport: presence(r.params["transport"]) || "stdio",
570
604
  command: r.params["command"].to_s,
605
+ args: split_lines(r.params["args"]),
571
606
  url: r.params["url"].to_s,
572
607
  description: r.params["description"].to_s,
573
608
  enabled: r.params["enabled"] == "1",
574
- env: parse_kv_lines(r.params["env"])
609
+ env: parse_kv_lines(r.params["env"]),
610
+ headers: parse_kv_lines(r.params["headers"])
575
611
  }
576
612
  end
577
613
 
@@ -588,5 +624,10 @@ module Studio
588
624
  [k, v.to_s.strip]
589
625
  end.to_h
590
626
  end
627
+
628
+ # One argv token per line (stdio's `args`). Ignores blank lines/comments.
629
+ def split_lines(text)
630
+ text.to_s.each_line.map(&:strip).reject { |l| l.empty? || l.start_with?("#") }
631
+ end
591
632
  end
592
633
  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>