insika 0.8.0 → 0.9.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 +75 -0
- data/README.md +5 -3
- data/bin/insika +1 -1
- data/docs/AGENTS.md +52 -11
- data/docs/API.md +73 -0
- data/docs/ARCHITECTURE.md +45 -44
- data/docs/CHANNELS.md +19 -2
- data/docs/CONTEXT.md +33 -27
- data/docs/DEPLOY.md +13 -2
- data/docs/EVALS.md +98 -8
- data/docs/FACTS.md +4 -0
- data/docs/KNOWLEDGE.md +7 -0
- data/docs/OBSERVABILITY.md +21 -6
- data/docs/POLICY.md +4 -1
- data/docs/RELEASING.md +4 -0
- data/docs/SECURITY.md +27 -1
- data/docs/TOOLS.md +127 -33
- data/docs/prompts/ADD-TOOL.md +12 -2
- data/docs/prompts/DIAGNOSE-TURN.md +3 -0
- data/docs/prompts/GO-LIVE.md +3 -1
- data/lib/insika/agent_profile.rb +21 -9
- data/lib/insika/channels/web/widget.js +33 -0
- data/lib/insika/channels/web.rb +5 -2
- data/lib/insika/chat_builder.rb +62 -20
- data/lib/insika/commands/agent_payload.rb +1 -1
- data/lib/insika/commands/run_distillation.rb +5 -8
- data/lib/insika/commands/seed_session.rb +118 -0
- data/lib/insika/context/builder.rb +29 -9
- data/lib/insika/context/priority.rb +2 -0
- data/lib/insika/context/provider.rb +5 -0
- data/lib/insika/context/providers/briefing.rb +11 -8
- data/lib/insika/context/providers/fence_notice.rb +27 -0
- data/lib/insika/context/providers/knowledge.rb +7 -4
- data/lib/insika/context/providers/memory.rb +8 -4
- data/lib/insika/context/providers/session.rb +7 -3
- data/lib/insika/doctor.rb +109 -1
- data/lib/insika/dsl/runtime.rb +1 -0
- data/lib/insika/dsl.rb +6 -0
- data/lib/insika/edge_limiter.rb +4 -1
- data/lib/insika/errors.rb +1 -0
- data/lib/insika/evals/assertions.rb +92 -6
- data/lib/insika/evals/golden.rb +91 -2
- data/lib/insika/evals/runner.rb +20 -0
- data/lib/insika/evals/simulator.rb +11 -2
- data/lib/insika/evals/transport.rb +117 -15
- data/lib/insika/evidence.rb +79 -12
- data/lib/insika/executor.rb +30 -23
- data/lib/insika/fence.rb +96 -0
- data/lib/insika/golden_store.rb +3 -0
- data/lib/insika/mcp_store.rb +5 -2
- data/lib/insika/mcp_tool_registry.rb +8 -1
- data/lib/insika/memory_store.rb +12 -0
- data/lib/insika/overlay_tool_registry.rb +5 -0
- data/lib/insika/prefix_fingerprint.rb +32 -27
- data/lib/insika/profile_source.rb +1 -0
- data/lib/insika/server/app.rb +43 -1
- data/lib/insika/server/rack_app.rb +2 -0
- data/lib/insika/server/responses.rb +31 -4
- data/lib/insika/session_store.rb +4 -1
- data/lib/insika/settings_store.rb +10 -1
- data/lib/insika/spoken_transcript.rb +31 -0
- data/lib/insika/studio/app.rb +6 -2
- data/lib/insika/studio/forms.rb +18 -3
- data/lib/insika/studio/views/_agent_tab_config.erb +5 -1
- data/lib/insika/studio/views/session.erb +1 -1
- data/lib/insika/studio/views/tool_edit.erb +6 -2
- data/lib/insika/telemetry/recorder.rb +13 -1
- data/lib/insika/tool_assembly.rb +21 -13
- data/lib/insika/tool_definition.rb +73 -10
- data/lib/insika/tool_envelope.rb +102 -2
- data/lib/insika/tool_store.rb +9 -4
- data/lib/insika/tool_trace_store.rb +1 -1
- data/lib/insika/tool_usage_report.rb +12 -2
- data/lib/insika/tools/data_defined_tool.rb +1 -0
- data/lib/insika/tools/present.rb +122 -0
- data/lib/insika/tools/run_persona_eval.rb +6 -1
- data/lib/insika/tools/tool_search.rb +4 -2
- data/lib/insika/turn_state.rb +13 -1
- data/lib/insika/version.rb +1 -1
- data/lib/insika/wiring/graph.rb +7 -0
- data/lib/insika/wiring/graph_chat.rb +4 -0
- data/lib/insika.rb +4 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ec89eb1dae94e5a57733d0dd88a19d92ff98eb7e2ae0b907792e2a4ce8d508aa
|
|
4
|
+
data.tar.gz: cb882c8015e00c0c905e5945a05a5a465c938dedda762914c3737a64a9f0d4fb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e2acb07f2ae896ee762cd3f1113dca3d462f80ea0bb32caea69f5db3fd6fbd31821cf9a5140b32c64c26b702b29b78aaf9fec0ca8f59653178dcb37afe9eca38
|
|
7
|
+
data.tar.gz: f8c98a2b76c802a75e18a77e0762ef42c650aa956afb3f131e1e9ccacfb2a56de87540b7389c740021008b45a174fa371d7a6dbb0a5a7cf83f248ce41b696770
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,81 @@ it is released. Entries land with the pull request that makes the change.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.9.0] - 2026-09-07
|
|
12
|
+
|
|
13
|
+
The commerce-agent wave: the engine now enforces what the prompt used to ask for —
|
|
14
|
+
a write names only IDs a lookup returned, the model picks which cards to show and
|
|
15
|
+
the engine shows them, third-party text can be fenced before the model reads it,
|
|
16
|
+
and an eval case can start from a seeded snapshot. Additive on the wire: `/v1`
|
|
17
|
+
gains frames and keys, none change meaning.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Write provenance** — `requires_evidence` on HTTP data tools checks declared
|
|
22
|
+
ID parameters against the session evidence ledger before approval or execution.
|
|
23
|
+
Unknown IDs and an unavailable ledger return `blocked` with gate `provenance`.
|
|
24
|
+
Studio editing, Doctor diagnostics, session traces, `tool_blocked`, the
|
|
25
|
+
`insika.tool.blocked` counter and `insika tools:report` expose the gate.
|
|
26
|
+
See [Tools](docs/TOOLS.md#provenance-checking-ids-before-a-write).
|
|
27
|
+
- **Snapshot evals** — cases can seed evidence IDs, memory, user/assistant history
|
|
28
|
+
and briefing before the first turn. The authenticated seed endpoint is disabled
|
|
29
|
+
by default (`evals.seeding`); Doctor warns when enabled. New assertions cover
|
|
30
|
+
forbidden/alternative/first calls, call limits, reply text and blocked gates.
|
|
31
|
+
Responses SSE now carries tool arguments, completion status and the provider's
|
|
32
|
+
`call_id` on both item frames, so a consumer pairs `added` with `done`.
|
|
33
|
+
See [Evals](docs/EVALS.md#state--a-case-starts-from-a-snapshot).
|
|
34
|
+
- **Presentation tools** — data tools with `presentation` instead of `request`
|
|
35
|
+
select evidence cards by known ID — this turn's, or the last 64 the session
|
|
36
|
+
ledger kept (snapshots seed them as `evidence.cards`). They report shown/dropped IDs,
|
|
37
|
+
emit `insika.ui` (web channel: `ui`) and select the outbox attachments. Evidence
|
|
38
|
+
cards carry IDs; empty selections suppress automatic card delivery. Includes
|
|
39
|
+
`ui_components` / `no_ui` eval assertions and Doctor diagnostics.
|
|
40
|
+
See [Tools](docs/TOOLS.md#presentation-tools-the-model-picks-ids-the-engine-shows-the-cards).
|
|
41
|
+
- **Fencing** — opt-in per agent through the DSL, payload or Studio. Sanitizes
|
|
42
|
+
ordinary tool result strings, memory text and injected knowledge summaries, and adds a fixed
|
|
43
|
+
identity-layer notice. The tool-string cap `fencing.max_chars` defaults to 12,000; the agent flag
|
|
44
|
+
defaults to off — and stays off in this release: flipping the default waits for the
|
|
45
|
+
golden-case comparison with fencing on ([Releasing](docs/RELEASING.md), gate 4).
|
|
46
|
+
Doctor warns for unfenced agents exposed by relay/widget env configuration.
|
|
47
|
+
See [Agents](docs/AGENTS.md#fencing--third-party-text-is-data-never-instructions).
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- **Cache boundary** — explicit Anthropic cache control ends at the identity layer;
|
|
52
|
+
volatile system text follows in a separate block. Prefix fingerprints cover
|
|
53
|
+
identity and tool schemas; memory/knowledge/request changes retain separate
|
|
54
|
+
diagnostic digests without reporting identity-prefix invalidation.
|
|
55
|
+
See [Context](docs/CONTEXT.md#the-provider-prefix-cache).
|
|
56
|
+
- **Serial writes in parallel batches** — tools marked `side_effect` acquire a
|
|
57
|
+
per-session serial gate before the shared concurrency slot. Independent reads
|
|
58
|
+
can overlap writes; queued writes do not consume that shared slot. MCP tools
|
|
59
|
+
annotated `readOnlyHint` are reads.
|
|
60
|
+
- Memory and knowledge extraction read only nonblank user/assistant text, preserving
|
|
61
|
+
original transcript indexes. Direct tool messages are excluded; assistant
|
|
62
|
+
paraphrases can still reach extraction. This applies regardless of `fencing`.
|
|
63
|
+
- A tool's own `{"status":"blocked"}` answer is a call that ran: only the engine's
|
|
64
|
+
gate refusal reports `blocked` on `:tool_result`, the trace and `tools:report`.
|
|
65
|
+
- A Studio save that changes a tool's HTTP method re-derives `side_effect`.
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- Data tools declaring `evidence` no longer fail when the envelope parses an
|
|
70
|
+
already-parsed `Evidence::Spec`.
|
|
71
|
+
- Studio tool saves preserve `evidence` and `presentation` declarations.
|
|
72
|
+
- `scripts/serve_real.rb` passes channel and settings stores to the edge, enabling
|
|
73
|
+
configured relay/widget routes and the eval seeding setting in the demo server.
|
|
74
|
+
- Fencing: the notice ships with the flag regardless of the `context_providers`
|
|
75
|
+
allowlist; briefing values and the compaction summary are sanitized too; evidence
|
|
76
|
+
item ids stay byte-exact for the model (only the line is sanitized).
|
|
77
|
+
- Provenance: a schema-optional parameter the model omits is not checked; a blocked
|
|
78
|
+
call on a one-shot turn no longer fails emitting `tool_blocked`.
|
|
79
|
+
- `POST /v1/conversations/:id/seed` decodes a percent-encoded conversation id.
|
|
80
|
+
- Widget `ui` cards link only `http(s)` urls.
|
|
81
|
+
- The evidence ledger reads the session row once per turn, not once per gated call.
|
|
82
|
+
- Studio reads its templates as UTF-8 regardless of the process locale: a host with
|
|
83
|
+
no `LANG` answered 500 on the login page (found by the release install proof).
|
|
84
|
+
|
|
85
|
+
|
|
11
86
|
## [0.8.0] - 2026-08-31
|
|
12
87
|
|
|
13
88
|
The post-consolidation hardening wave: the last two OpenClaw-era names are gone
|
data/README.md
CHANGED
|
@@ -18,7 +18,9 @@ deployment.
|
|
|
18
18
|
- **Drop-in `/v1/responses`** — SSE streaming and usage, the shape existing clients already speak.
|
|
19
19
|
- **Durable & resumable** — every turn checkpoints; a crash resumes without repeating side-effects.
|
|
20
20
|
- **Agents as data** — agents, prompts, skills and tools are editable at runtime (UI or API), no redeploy.
|
|
21
|
-
- **Tools & skills** — code
|
|
21
|
+
- **Tools & skills** — code, HTTP data and live MCP tools; skills load on demand.
|
|
22
|
+
- **Evidence-backed actions** — ID checks before writes, serial side effects per session, and selected evidence cards ([Tools](docs/TOOLS.md)).
|
|
23
|
+
- **Snapshot evals** — seed a case's starting state and check calls, refusals, replies and cards ([Evals](docs/EVALS.md)).
|
|
22
24
|
- **Safe by default** — content-safety guardrails, an egress guard, confined execution, approvals, edge limits.
|
|
23
25
|
- **Observable** — an event stream, per-session tool-call traces, optional OpenTelemetry.
|
|
24
26
|
- **~0.4 ms of engine per turn** — p50 overhead on a neutral, key-free benchmark you can rerun yourself ([methodology](docs/BENCHMARK.md)).
|
|
@@ -106,7 +108,7 @@ navigates by.
|
|
|
106
108
|
|
|
107
109
|
- [Agents](docs/AGENTS.md) — the AgentProfile and every key on it; create and edit at runtime.
|
|
108
110
|
- [Limits and policy](docs/POLICY.md) — the five layers: allowed tools, approvals, guardrails, edge limits, reasoning.
|
|
109
|
-
- [Tools](docs/TOOLS.md) — code
|
|
111
|
+
- [Tools](docs/TOOLS.md) — code, HTTP, MCP and presentation tools; evidence gates and serial writes.
|
|
110
112
|
- [Skills](docs/SKILLS.md) — the SKILL.md format and progressive loading.
|
|
111
113
|
- [Context](docs/CONTEXT.md) — what fills a turn's prompt; budget, eviction, memory.
|
|
112
114
|
- [Workflows](docs/WORKFLOWS.md) — deterministic orchestration of several agents: the five patterns, and when to let the model choose instead.
|
|
@@ -140,7 +142,7 @@ navigates by.
|
|
|
140
142
|
|
|
141
143
|
**Improve**
|
|
142
144
|
|
|
143
|
-
- [Evals](docs/EVALS.md) —
|
|
145
|
+
- [Evals](docs/EVALS.md) — snapshot cases, tool/UI assertions, rubrics, the judge panel and the pre-merge gate.
|
|
144
146
|
- [Refinement](docs/REFINEMENT.md) — read an agent's own traffic back as a ranked report of what broke.
|
|
145
147
|
- [Outcomes and follow-ups](docs/OUTCOMES.md) — what the traffic was worth in business terms, and the tool that comes back on a promise.
|
|
146
148
|
- [Knowledge](docs/KNOWLEDGE.md) — extract durable concepts from finished conversations into a per-agent knowledge base; provenance-stamped, PII-redacted, best-effort with a re-scan recovery path.
|
data/bin/insika
CHANGED
|
@@ -390,7 +390,7 @@ def evals_simulate(argv)
|
|
|
390
390
|
# paired comparison, which it silently invalidates.
|
|
391
391
|
require "securerandom"
|
|
392
392
|
conv = opts[:conv] || "sim-#{golden.id}-#{SecureRandom.hex(4)}"
|
|
393
|
-
run = simulator.run(persona: golden.persona, agent: target_agent, conv: conv)
|
|
393
|
+
run = simulator.run(persona: golden.persona, agent: target_agent, conv: conv, state: golden.state)
|
|
394
394
|
|
|
395
395
|
verdict = judge && judge.score_conversation(
|
|
396
396
|
rubric: golden.rubric, transcript: run.transcript, policy: golden.policy,
|
data/docs/AGENTS.md
CHANGED
|
@@ -144,9 +144,44 @@ the thing worth declaring:
|
|
|
144
144
|
tool_persistence false # remove the block for this agent
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
The block is
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
The block is constant, so it does not invalidate the identity prefix between turns.
|
|
148
|
+
|
|
149
|
+
### `prompt_caching` — explicit cache control
|
|
150
|
+
|
|
151
|
+
Set `"prompt_caching": true` in the agent payload, or enable it in the Studio
|
|
152
|
+
agent configuration. There is no dedicated DSL method for this flag.
|
|
153
|
+
|
|
154
|
+
For Anthropic, this puts the cache breakpoint after the identity layer and leaves
|
|
155
|
+
volatile system text below it. Other providers receive a plain system string and
|
|
156
|
+
may cache automatically. See [Context](CONTEXT.md#the-provider-prefix-cache) for
|
|
157
|
+
layering, fallback behavior and token accounting.
|
|
158
|
+
|
|
159
|
+
### `fencing` — third-party text is data, never instructions
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
fencing true
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Off by default. When enabled, the engine sanitizes ordinary tool result string
|
|
166
|
+
leaves after evidence processing, memory facts/notes, briefing values and the next
|
|
167
|
+
step, the compaction summary, and the knowledge names and descriptions injected in
|
|
168
|
+
the context block. It normalizes Unicode (NFKC), removes invisible format characters,
|
|
169
|
+
replaces control characters except tabs/newlines/carriage returns, strips known
|
|
170
|
+
engine/transcript tag shapes and defuses forged role markers. Tool result strings
|
|
171
|
+
are also capped at `fencing.max_chars` (platform default 12,000); memory and knowledge
|
|
172
|
+
context sanitization does not apply that cap. Tool error and halt results bypass
|
|
173
|
+
this sanitizer; tool Hash keys, evidence item `id`s and attachment captions are
|
|
174
|
+
unchanged (an id is a key the ledger holds byte-exact).
|
|
175
|
+
`load_knowledge` returns the full body without fencing.
|
|
176
|
+
|
|
177
|
+
A fixed notice beneath the identity tells the model to treat contextual material
|
|
178
|
+
as data. It ships with the flag — the `context_providers` allowlist does not govern
|
|
179
|
+
it. It is stable across turns; it does not guarantee prompt-injection resistance.
|
|
180
|
+
|
|
181
|
+
Memory/knowledge extraction reads only nonblank user/assistant text regardless
|
|
182
|
+
of the flag. Direct tool messages are excluded, but text repeated by the
|
|
183
|
+
assistant can still reach extraction. `insika doctor` warns for unfenced agents
|
|
184
|
+
exposed by the configured relay/widget environment settings.
|
|
150
185
|
|
|
151
186
|
### Why some limits are missing from that list
|
|
152
187
|
|
|
@@ -173,17 +208,16 @@ So the two groups read differently on purpose:
|
|
|
173
208
|
### `tool_concurrency` — parallel tool calls
|
|
174
209
|
|
|
175
210
|
When the model asks for several tools in one step, they run **one at a time by
|
|
176
|
-
default**. Raise `tool_concurrency`
|
|
177
|
-
in flight:
|
|
211
|
+
default**. Raise `tool_concurrency` to overlap independent reads, capped at that
|
|
212
|
+
number in flight. Tools marked `side_effect` still execute one at a time per session:
|
|
178
213
|
|
|
179
214
|
```ruby
|
|
180
215
|
limit :tool_concurrency, 4 # nil / 0 / 1 = serial (the default); N = at most N at once
|
|
181
216
|
```
|
|
182
217
|
|
|
183
218
|
One number is both the switch and the cap. It pays off only when a turn issues
|
|
184
|
-
several **slow, independent**
|
|
185
|
-
|
|
186
|
-
tools, and it is the *model* that decides the fan-out, which is why the cap is not
|
|
219
|
+
several **slow, independent reads** (data tools waiting on HTTP). It buys nothing
|
|
220
|
+
for fast in-process tools, and it is the *model* that decides the fan-out, which is why the cap is not
|
|
187
221
|
optional: an uncapped batch of 15 data tools is 15 simultaneous requests to the
|
|
188
222
|
same backend.
|
|
189
223
|
|
|
@@ -405,9 +439,9 @@ Three capabilities invert the default — `nil`/absent means **OFF**, not "all":
|
|
|
405
439
|
`subagents`, `memory`, and `guardrails` (each defaults to off or a conservative
|
|
406
440
|
setting, never "everything on"). `tool_output_compression` is a fourth: opt-in
|
|
407
441
|
mechanical dedupe of repeated tool results in the history (see
|
|
408
|
-
[Context](CONTEXT.md#
|
|
409
|
-
off by default because it changes what the model sees
|
|
410
|
-
other way: `tool_persistence` is **ON unless you set it to `false`** (see
|
|
442
|
+
[Context](CONTEXT.md#budget-and-eviction--the-actual-compaction)),
|
|
443
|
+
off by default because it changes what the model sees; `fencing` is a fifth (see
|
|
444
|
+
[`fencing`](#fencing--third-party-text-is-data-never-instructions)), off by default. One flag inverts the other way: `tool_persistence` is **ON unless you set it to `false`** (see
|
|
411
445
|
[`tool_persistence`](#tool_persistence--dont-give-up-on-the-first-empty-result)).
|
|
412
446
|
|
|
413
447
|
### Declaring what this deployment has
|
|
@@ -692,6 +726,13 @@ optionally shared outside the Studio via an expiring signed link
|
|
|
692
726
|
saving agent — never a parameter the model types. Artifact content is LLM
|
|
693
727
|
output and is served as untrusted. See [Artifacts](ARTIFACTS.md).
|
|
694
728
|
|
|
729
|
+
## Review before enabling writes
|
|
730
|
+
|
|
731
|
+
Does each ID come from the server? Declare `evidence` on the lookup tool and
|
|
732
|
+
`requires_evidence` on the write tool so the engine enforces session provenance.
|
|
733
|
+
Mark writes as `side_effect` so parallel batches serialize them and recovery skips
|
|
734
|
+
completed calls. See [Tools](TOOLS.md#provenance-checking-ids-before-a-write).
|
|
735
|
+
|
|
695
736
|
## See also
|
|
696
737
|
|
|
697
738
|
- [Tools](TOOLS.md) — define, register, and troubleshoot tools.
|
data/docs/API.md
CHANGED
|
@@ -21,6 +21,7 @@ two cannot drift (a spec pins the gate's version to the date below).
|
|
|
21
21
|
| `POST /v1/agents` | provisioning by definition/pack (idempotent, authoritative) |
|
|
22
22
|
| `GET/DELETE /v1/agents` | list / remove agents |
|
|
23
23
|
| `POST /v1/sessions` | create a session explicitly |
|
|
24
|
+
| `POST /v1/conversations/:id/seed` | load an eval snapshot before the first turn; see [Seeding](#seeding-an-eval-conversation) |
|
|
24
25
|
| `POST /v1/outcomes` | record a business outcome (`conversion`, `escalation`, any label) |
|
|
25
26
|
| `GET /v1/outcomes` | last outcome per agent + the per-day series |
|
|
26
27
|
| `POST /v1/commands/...` | operator commands (`forget_customer`, `delete_tenant_data`, the follow-up mutations) |
|
|
@@ -30,6 +31,78 @@ two cannot drift (a spec pins the gate's version to the date below).
|
|
|
30
31
|
The turn endpoints speak the OpenAI `response.completed` wire format; the
|
|
31
32
|
`Insika-Version` header declares the compatibility vintage of a request.
|
|
32
33
|
|
|
34
|
+
## Tool and presentation SSE events
|
|
35
|
+
|
|
36
|
+
`POST /v1/responses` emits a tool name on `response.output_item.added`, with
|
|
37
|
+
`arguments` as a JSON string when available. `response.output_item.done` reports
|
|
38
|
+
how it ended. These `ok` / `error` / `blocked` statuses and optional `gate` are
|
|
39
|
+
Insika extensions, not OpenAI function-call lifecycle statuses.
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
event: response.output_item.added
|
|
43
|
+
data: {"type":"response.output_item.added","item":{"type":"function_call","name":"add_to_cart","call_id":"toolu_01","arguments":"{\"product_id\":\"SKU-1\"}"}}
|
|
44
|
+
|
|
45
|
+
event: response.output_item.done
|
|
46
|
+
data: {"type":"response.output_item.done","item":{"type":"function_call","name":"add_to_cart","call_id":"toolu_01","status":"blocked","gate":"provenance"}}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`added` and `done` are two frames of **one** call, paired by `call_id` (the provider's
|
|
50
|
+
tool-call id, present whenever the provider gave one). A consumer that counts tool
|
|
51
|
+
calls counts `added` frames; `done` only says how each ended.
|
|
52
|
+
|
|
53
|
+
The completion frame does not contain the tool result body. Use stored session
|
|
54
|
+
traces for a per-call audit of what a tool returned.
|
|
55
|
+
|
|
56
|
+
A presentation call also emits a namespaced event:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
event: insika.ui
|
|
60
|
+
data: {"type":"insika.ui","component":"product_cards","title":"Selected products","items":[{"type":"card","id":"SKU-1","url":"https://shop.example/products/1","caption":"Dark chocolate"}],"count":1,"dropped":[{"id":"SKU-2","reason":"no_card"}]}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`count` is the number shown; `dropped` reasons are `unknown`, `no_card` or `max`.
|
|
64
|
+
An empty selection still emits the event with `count: 0`. Clients that render
|
|
65
|
+
cards consume this event; text-only clients can ignore it. See
|
|
66
|
+
[Presentation tools](TOOLS.md#presentation-tools-the-model-picks-ids-the-engine-shows-the-cards).
|
|
67
|
+
|
|
68
|
+
## Seeding an eval conversation
|
|
69
|
+
|
|
70
|
+
Enable `evals.seeding` in platform settings on the eval deployment. For example,
|
|
71
|
+
an operator can `POST /v1/commands/update_settings` with
|
|
72
|
+
`{"patch":{"evals":{"seeding":true}}}`. Turn it off after the run.
|
|
73
|
+
|
|
74
|
+
`POST /v1/conversations/:id/seed` uses the same Bearer and tenant ID namespacing
|
|
75
|
+
as a turn. Its body is the snapshot itself, plus optional `customer`; do not wrap
|
|
76
|
+
it in `state`:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"customer": "eval-customer-1",
|
|
81
|
+
"evidence": {
|
|
82
|
+
"ids": ["SKU-1"],
|
|
83
|
+
"cards": [{ "type": "card", "id": "SKU-1", "url": "https://shop.example/1", "caption": "Dark chocolate" }]
|
|
84
|
+
},
|
|
85
|
+
"history": [
|
|
86
|
+
{ "role": "assistant", "content": "The dark chocolate is SKU-1." }
|
|
87
|
+
],
|
|
88
|
+
"memory": { "facts": { "preference": "dark chocolate" }, "notes": [] },
|
|
89
|
+
"briefing": { "fields": { "delivery_day": "Friday" } }
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
It creates a missing session and returns `200` with `{ "session": … }`; seeding
|
|
94
|
+
does not run a model or create a task. Disabled seeding returns `403`, a session
|
|
95
|
+
with messages returns `409`, and invalid supported field shapes return `422`.
|
|
96
|
+
History accepts only `user` / `assistant` roles and is stamped `origin: engine`.
|
|
97
|
+
|
|
98
|
+
Memory uses `[tenant:]customer` when `customer` is supplied; subsequent turns
|
|
99
|
+
must carry the same customer. Without one, memory is written to the tenant cell,
|
|
100
|
+
or `chat:<session-id>` when there is no tenant. Use an isolated eval deployment:
|
|
101
|
+
a fresh conversation ID alone does not isolate tenant memory. Evidence IDs seed
|
|
102
|
+
only the runtime ledger, never model-visible text; `evidence.cards` seeds the cards
|
|
103
|
+
a presentation tool can show.
|
|
104
|
+
See [Snapshot evals](EVALS.md#state--a-case-starts-from-a-snapshot).
|
|
105
|
+
|
|
33
106
|
## The compatibility rule
|
|
34
107
|
|
|
35
108
|
**Request and response shapes are stable; changes are additive-only.** A new
|
data/docs/ARCHITECTURE.md
CHANGED
|
@@ -177,64 +177,65 @@ The order is not arbitrary:
|
|
|
177
177
|
|
|
178
178
|
## The tool-loop
|
|
179
179
|
|
|
180
|
-
Stage 6 is the
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
already-completed side-effects on resume, and fires the approval gate.
|
|
180
|
+
Stage 6 is the agent interaction. RubyLLM owns the reason→act→observe loop;
|
|
181
|
+
`ToolEnvelope` adds provenance, approval, concurrency, timeout, evidence processing,
|
|
182
|
+
fencing, traces and side-effect checkpoints around registered tools.
|
|
184
183
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
Calls run serially unless `limits[:tool_concurrency]` permits parallel execution.
|
|
185
|
+
Marked side effects acquire a serial gate before the shared concurrency slot,
|
|
186
|
+
so writes in one session cannot overlap and queued writes leave slots for reads.
|
|
187
|
+
Turns exposing approval-required tools run serially. See
|
|
188
|
+
[Tools](TOOLS.md#parallel-tool-calls) for limits and timeout behavior.
|
|
190
189
|
|
|
191
190
|
```mermaid
|
|
192
191
|
flowchart TD
|
|
193
|
-
ask[chat.ask
|
|
192
|
+
ask[chat.ask → model] --> dec{tool call?}
|
|
194
193
|
dec -->|no| done[final content]
|
|
195
|
-
dec -->|yes| env[ToolEnvelope]
|
|
196
|
-
env -->
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
http
|
|
204
|
-
|
|
194
|
+
dec -->|yes| env[ToolEnvelope: skip completed side effects on resume]
|
|
195
|
+
env --> prov{declared evidence IDs known?}
|
|
196
|
+
prov -->|no| blocked[blocked: provenance]
|
|
197
|
+
blocked --> ask
|
|
198
|
+
prov -->|yes / undeclared| appr[approval gate]
|
|
199
|
+
appr --> gates[side-effect serial gate → shared concurrency slot → timeout]
|
|
200
|
+
gates --> kind{tool kind}
|
|
201
|
+
kind -->|code| ruby[Ruby / sandbox]
|
|
202
|
+
kind -->|HTTP data| http[EgressGuard → HTTP]
|
|
203
|
+
kind -->|MCP| mcp[live MCP client]
|
|
204
|
+
kind -->|presentation| cards[select turn or session evidence cards → UI event]
|
|
205
|
+
ruby --> result[evidence reshape → fencing → checkpoint and trace]
|
|
206
|
+
http --> result
|
|
207
|
+
mcp --> result
|
|
208
|
+
cards --> result
|
|
209
|
+
result --> ask
|
|
205
210
|
```
|
|
206
211
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
HTTP data tools and presentation tools are both stored definitions. Presentation
|
|
213
|
+
runs in-process; MCP remains a live server call (HTTP/SSE or stdio). HTTP egress
|
|
214
|
+
is checked before a request. Tool errors reach the model as error results so it
|
|
215
|
+
can recover; a provenance refusal reports `blocked` without contacting the backend
|
|
216
|
+
or asking an operator. Completed side effects are recorded for resume.
|
|
212
217
|
|
|
213
218
|
## Ingesting tools: manifest and MCP
|
|
214
219
|
|
|
215
|
-
|
|
220
|
+
Data definitions and MCP instances have separate stores. Both are hot config:
|
|
216
221
|
|
|
217
222
|
```mermaid
|
|
218
223
|
flowchart TD
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
val1 --> store[(ToolStore)]
|
|
229
|
-
val2 --> store
|
|
230
|
-
store --> cat[reload catalog + registry]
|
|
231
|
-
cat --> loop[available in the tool-loop]
|
|
224
|
+
manifest[POST /v1/tools/manifest] --> substitute[resolve env / secret placeholders]
|
|
225
|
+
substitute --> validate[validate HTTP or presentation definition]
|
|
226
|
+
validate --> store[(ToolStore)]
|
|
227
|
+
store --> data[DataToolRegistry]
|
|
228
|
+
config[MCP instance configuration] --> mcpstore[(McpStore)]
|
|
229
|
+
mcpstore --> live[McpToolRegistry → live server tools]
|
|
230
|
+
data --> catalog[effective registry and catalog]
|
|
231
|
+
live --> catalog
|
|
232
|
+
catalog --> policy[agent allowlist → tool-loop]
|
|
232
233
|
```
|
|
233
234
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
Only manifest ingestion resolves `{{env.*}}` and `{{secret.*}}`; other data-tool
|
|
236
|
+
write paths require literal values. One malformed manifest tool is reported in
|
|
237
|
+
`errors[]` while valid entries import. MCP tools are never converted to stored
|
|
238
|
+
HTTP data tools. See [Tools](TOOLS.md#registering-a-tool).
|
|
238
239
|
|
|
239
240
|
## Durability: checkpoints and resume
|
|
240
241
|
|
|
@@ -316,7 +317,7 @@ validator as the after-task hook, so both roots enforce content safety identical
|
|
|
316
317
|
| Recovery | `lib/insika/recovery.rb` |
|
|
317
318
|
| Inbound queue (one turn at a time per session, and what happens to a message that arrives while one is running) | `lib/insika/session_actor.rb`, `lib/insika/queue_policy.rb`, `lib/insika/steer_injector.rb` |
|
|
318
319
|
| Channels (a way in and out for people; the reply that travels after the turn ends) | `lib/insika/channel_registry.rb`, `lib/insika/channels/*`, `lib/insika/channel_delivery.rb`, `lib/insika/outbox_store.rb`, `lib/insika/inbound_log.rb` |
|
|
319
|
-
| Tools (data/manifest/MCP) | `lib/insika/tool_definition.rb`, `tool_manifest.rb`, `
|
|
320
|
+
| Tools (data/manifest/MCP) | `lib/insika/tool_definition.rb`, `lib/insika/tool_manifest.rb`, `lib/insika/mcp_tool_registry.rb`, `lib/insika/tools/present.rb` |
|
|
320
321
|
| Plugin loading (boot) | `lib/insika/plugin.rb`, `lib/insika/plugin/loader.rb` |
|
|
321
322
|
| Refinement (traffic → report) | `lib/insika/refinement/*`, `lib/insika/refinement_store.rb` |
|
|
322
323
|
| Post-turn learning (facts, skills, knowledge — extracted from finished conversations) | `lib/insika/distill.rb`, `lib/insika/harvest.rb`, `lib/insika/knowledge.rb`, `lib/insika/knowledge_store.rb`; the per-turn hook lives in `Executor#persist_turn`, next to `finalize_delegation` |
|
data/docs/CHANNELS.md
CHANGED
|
@@ -149,16 +149,24 @@ Content-Type: application/json
|
|
|
149
149
|
{ "agent": "support", "session_id": "web:8f3c…", "message": "cadê meu pedido?" }
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
-
The reply is SSE on that same connection —
|
|
152
|
+
The reply is SSE on that same connection — five frame types, and an unknown one is
|
|
153
153
|
safe to ignore:
|
|
154
154
|
|
|
155
155
|
```
|
|
156
|
-
event: delta data: {"delta":"Seu pedido "}
|
|
156
|
+
event: delta data: {"delta":"Seu pedido chegou."} the published answer
|
|
157
157
|
event: working data: {"name":"order_status"} a tool is running
|
|
158
|
+
event: ui data: {"component":"product_cards","title":"Chocolate","items":[{"id":"SKU-1","url":"https://shop.example/1","caption":"Dark chocolate"}]}
|
|
159
|
+
a presentation tool picked cards to show
|
|
158
160
|
event: done data: {} the turn ended
|
|
159
161
|
event: error data: {"message":"…"} it ended badly
|
|
160
162
|
```
|
|
161
163
|
|
|
164
|
+
The shipped widget renders `ui` as a plain list of caption + link under the
|
|
165
|
+
conversation (only an `http(s)` url becomes a link; any other scheme renders as
|
|
166
|
+
text); a host page that wants real cards restyles or replaces that — the
|
|
167
|
+
protocol is the frame. An empty selection sends `items: []`. Unlike `/v1/responses`,
|
|
168
|
+
the web frame omits `count` and `dropped`. See [presentation tools](TOOLS.md#presentation-tools-the-model-picks-ids-the-engine-shows-the-cards).
|
|
169
|
+
|
|
162
170
|
**The engine issues the session id and the client never proposes one.** `POST
|
|
163
171
|
/messages` with an id nobody minted is a `404`, never a new conversation: on an
|
|
164
172
|
anonymous endpoint, create-on-write means anyone who guesses an id can read someone
|
|
@@ -315,6 +323,15 @@ stays internal unless the agent opts in. That contract is
|
|
|
315
323
|
[the edge contract](ARCHITECTURE.md#what-crosses-the-edge), and it is why you can
|
|
316
324
|
forward `content` straight to the customer.
|
|
317
325
|
|
|
326
|
+
The last balloon may also carry `attachments`: objects with `type`, `url`, `caption`
|
|
327
|
+
and an optional `id`,
|
|
328
|
+
the cards an evidence tool returned (see [Evidence](TOOLS.md#evidence-the-lean-envelope-and-grounding)).
|
|
329
|
+
When the turn called a **presentation tool**, the list is exactly the cards the model
|
|
330
|
+
selected, in call order, each also carrying the `component` and `title` of that call;
|
|
331
|
+
otherwise it is every card the turn's evidence tools returned. An empty presentation
|
|
332
|
+
selection suppresses that automatic fallback. Ignore the key if the consumer only
|
|
333
|
+
supports text.
|
|
334
|
+
|
|
318
335
|
### Delivery policy
|
|
319
336
|
|
|
320
337
|
A relay declares **how the outbox flushes** — a property of the surface
|
data/docs/CONTEXT.md
CHANGED
|
@@ -23,6 +23,7 @@ into a deterministic prompt:
|
|
|
23
23
|
| Provider | Block | Priority | Layer | Notes |
|
|
24
24
|
|----------|-------|:--------:|-------|-------|
|
|
25
25
|
| **Identity** | system | **100 — pinned** | identity | The agent's prompt files (global system files first). Never cut. |
|
|
26
|
+
| **Fence notice** | system | **99 — pinned** | identity | Fixed instruction to treat contextual material as data; only with `fencing` on. |
|
|
26
27
|
| **Skills** | `<available_skills>` | 80 | identity | Level-1 skill list, minus whatever is already eager — see [Skills](SKILLS.md). |
|
|
27
28
|
| **Tool search** | `<available_tools>` | 70 | identity | Level-1 list of deferred tools — see [Tools](TOOLS.md). |
|
|
28
29
|
| **Skill trigger** | `<active_skill>` | 85 | volatile | Level-2 bodies: the agent's `skills_eager` set, plus the ones whose `triggers:` match the message — see [Skills](SKILLS.md). |
|
|
@@ -106,10 +107,10 @@ budget first.
|
|
|
106
107
|
With `memory` enabled, an agent gains a built-in `remember` tool for durable
|
|
107
108
|
facts, and those facts (plus recent notes) are injected back into the prompt on
|
|
108
109
|
later turns — **including turns in a different session**. Memory is scoped per
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
`(tenant, customer)` when the message carries a `customer`. Without a customer,
|
|
111
|
+
it uses the tenant's cell; without either, it uses the session's marked
|
|
112
|
+
`memory:chat:<session id>` cell. A fresh session therefore does not isolate
|
|
113
|
+
memory when it still carries the same tenant. This is distinct from *session history*, which is the transcript of one
|
|
113
114
|
conversation; memory is the small set of facts that should outlive any single
|
|
114
115
|
conversation. Facts and notes are editable from the Studio agent page. See
|
|
115
116
|
[`examples/memory/`](https://github.com/guizaols/insika/tree/main/examples/memory/) for a runnable cross-session example.
|
|
@@ -174,7 +175,7 @@ Attention is strongest at the end of the context: a goal stated only at the top
|
|
|
174
175
|
is the first thing a 30-call turn forgets. So the recitation was **moved** there,
|
|
175
176
|
not copied — the head never repeats it, and the turn pays for it once. It rides
|
|
176
177
|
as a `user` message, like every other engine append inside a turn, so the system
|
|
177
|
-
prefix stays byte-stable
|
|
178
|
+
identity prefix stays byte-stable.
|
|
178
179
|
|
|
179
180
|
The `still missing` list is the point: the *model* sees which declared fields are
|
|
180
181
|
still unanswered, so it stops re-asking for something already given. Stored keys
|
|
@@ -194,17 +195,21 @@ turns and resumes — a resumed conversation re-opens with the briefing intact.
|
|
|
194
195
|
|
|
195
196
|
## The provider prefix cache
|
|
196
197
|
|
|
197
|
-
|
|
198
|
+
A stable prefix makes provider caching possible; eligibility, expiry and reported
|
|
199
|
+
savings still depend on the provider.
|
|
198
200
|
|
|
199
|
-
- **Automatic
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
201
|
+
- **Automatic prefix caching** needs no Insika flag. The identity-first render
|
|
202
|
+
order keeps changing memory and request data below the stable prefix.
|
|
203
|
+
- **Explicit cache breakpoints** are opt-in via `"prompt_caching": true` on
|
|
204
|
+
the agent profile and applied only when the resolved provider is `anthropic`. With both system layers present,
|
|
205
|
+
Insika sends two text blocks: identity with `cache_control`, then volatile text
|
|
206
|
+
without it. With no volatile text there is one block; with no identity there is
|
|
207
|
+
no breakpoint. Other providers receive a plain system string.
|
|
208
|
+
|
|
209
|
+
Custom context builders and hooks must keep `system_identity`, `system_volatile`
|
|
210
|
+
and their joined `system` consistent. Without a split, or after a hook replaces
|
|
211
|
+
only `system`, the chat builder falls back to treating the whole system as identity.
|
|
212
|
+
That fallback cannot protect a volatile suffix from cache invalidation.
|
|
208
213
|
|
|
209
214
|
Cache accounting surfaces as `cached_tokens` (reads) and `cache_creation_tokens`
|
|
210
215
|
(writes), visible in telemetry and the Studio tokens chip.
|
|
@@ -215,13 +220,14 @@ The system block is partitioned into two cache layers:
|
|
|
215
220
|
|
|
216
221
|
- **Identity** — bytes that change only on deploy/config edit: the persona
|
|
217
222
|
prompt (`Prompt`), the level-1 skill list (`Skill`) and the deferred-tool
|
|
218
|
-
catalog (`ToolSearch`)
|
|
223
|
+
catalog (`ToolSearch`), the fixed tool-discipline instructions and, when enabled,
|
|
224
|
+
the fencing notice. This is the cacheable prefix.
|
|
219
225
|
- **Volatile** — bytes that may change per turn: memory, session history,
|
|
220
226
|
triggered skill bodies, the `<request_context>`. Everything else.
|
|
221
227
|
|
|
222
228
|
The layer is a **provider-class contract**, not profile data: `ContextProvider`
|
|
223
229
|
declares `def layer = :volatile` (conservative — nothing gets pinned by
|
|
224
|
-
accident) and the
|
|
230
|
+
accident) and the identity providers override to `:identity`. A pack does
|
|
225
231
|
not set it — a pack reorganizes *which content goes into the Prompt provider vs
|
|
226
232
|
the volatile providers*. The Builder stamps the layer on every fragment at
|
|
227
233
|
production, and the render order is **identity first, volatile after** — a
|
|
@@ -236,13 +242,14 @@ output must be byte-stable across turns).
|
|
|
236
242
|
|
|
237
243
|
### The observable cache: fingerprints and the invalidation reason
|
|
238
244
|
|
|
239
|
-
Each turn, the Executor hashes the rendered
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
not a
|
|
245
|
+
Each turn, the Executor hashes the rendered identity and tool schemas into
|
|
246
|
+
SHA-256 fingerprints and a cumulative `prefix`. The invalidation reason names
|
|
247
|
+
the first changed part of that stable prefix. Volatile system categories have
|
|
248
|
+
separate diagnostic digests: changes to memory, knowledge or request context do
|
|
249
|
+
not report an identity-prefix invalidation. History is excluded.
|
|
250
|
+
|
|
251
|
+
These fingerprints explain local prompt changes; they do not prove a provider
|
|
252
|
+
cache hit. Use the provider's token accounting for that.
|
|
246
253
|
|
|
247
254
|
The Studio surfaces it in two places: the **session Context card** shows the
|
|
248
255
|
turn's cache-hit percentage and the `broke: <category>` line (plus the
|
|
@@ -251,9 +258,8 @@ cache tab with the per-agent hit series over time. The per-agent series lives
|
|
|
251
258
|
in its own capped store, because a session does not stamp its author — the
|
|
252
259
|
per-session trace cannot answer "cache-hit over time for *this* agent".
|
|
253
260
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
cache write once, every subsequent turn reads.
|
|
261
|
+
A stable identity can be reused while the provider cache remains eligible and valid.
|
|
262
|
+
A changed tool schema still invalidates the local prefix fingerprint.
|
|
257
263
|
|
|
258
264
|
## The volume
|
|
259
265
|
|