@beingmartinbmc/ojas 0.2.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 (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +308 -0
  3. package/dist/aahar/index.d.ts +179 -0
  4. package/dist/aahar/index.d.ts.map +1 -0
  5. package/dist/aahar/index.js +657 -0
  6. package/dist/aahar/index.js.map +1 -0
  7. package/dist/aahar/scoring.d.ts +85 -0
  8. package/dist/aahar/scoring.d.ts.map +1 -0
  9. package/dist/aahar/scoring.js +268 -0
  10. package/dist/aahar/scoring.js.map +1 -0
  11. package/dist/agni/index.d.ts +113 -0
  12. package/dist/agni/index.d.ts.map +1 -0
  13. package/dist/agni/index.js +328 -0
  14. package/dist/agni/index.js.map +1 -0
  15. package/dist/agni/model-router.d.ts +77 -0
  16. package/dist/agni/model-router.d.ts.map +1 -0
  17. package/dist/agni/model-router.js +163 -0
  18. package/dist/agni/model-router.js.map +1 -0
  19. package/dist/agni/response-distiller.d.ts +37 -0
  20. package/dist/agni/response-distiller.d.ts.map +1 -0
  21. package/dist/agni/response-distiller.js +193 -0
  22. package/dist/agni/response-distiller.js.map +1 -0
  23. package/dist/agni/tiktoken-adapter.d.ts +55 -0
  24. package/dist/agni/tiktoken-adapter.d.ts.map +1 -0
  25. package/dist/agni/tiktoken-adapter.js +113 -0
  26. package/dist/agni/tiktoken-adapter.js.map +1 -0
  27. package/dist/chikitsa/index.d.ts +130 -0
  28. package/dist/chikitsa/index.d.ts.map +1 -0
  29. package/dist/chikitsa/index.js +565 -0
  30. package/dist/chikitsa/index.js.map +1 -0
  31. package/dist/demo.d.ts +15 -0
  32. package/dist/demo.d.ts.map +1 -0
  33. package/dist/demo.js +278 -0
  34. package/dist/demo.js.map +1 -0
  35. package/dist/index.d.ts +201 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +588 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/mcp/audit.d.ts +39 -0
  40. package/dist/mcp/audit.d.ts.map +1 -0
  41. package/dist/mcp/audit.js +73 -0
  42. package/dist/mcp/audit.js.map +1 -0
  43. package/dist/mcp/contracts.d.ts +76 -0
  44. package/dist/mcp/contracts.d.ts.map +1 -0
  45. package/dist/mcp/contracts.js +44 -0
  46. package/dist/mcp/contracts.js.map +1 -0
  47. package/dist/mcp/envelope.d.ts +107 -0
  48. package/dist/mcp/envelope.d.ts.map +1 -0
  49. package/dist/mcp/envelope.js +162 -0
  50. package/dist/mcp/envelope.js.map +1 -0
  51. package/dist/mcp/registry.d.ts +110 -0
  52. package/dist/mcp/registry.d.ts.map +1 -0
  53. package/dist/mcp/registry.js +258 -0
  54. package/dist/mcp/registry.js.map +1 -0
  55. package/dist/mcp/server.d.ts +26 -0
  56. package/dist/mcp/server.d.ts.map +1 -0
  57. package/dist/mcp/server.js +107 -0
  58. package/dist/mcp/server.js.map +1 -0
  59. package/dist/mcp/tools/agent.d.ts +4 -0
  60. package/dist/mcp/tools/agent.d.ts.map +1 -0
  61. package/dist/mcp/tools/agent.js +300 -0
  62. package/dist/mcp/tools/agent.js.map +1 -0
  63. package/dist/mcp/tools/context.d.ts +4 -0
  64. package/dist/mcp/tools/context.d.ts.map +1 -0
  65. package/dist/mcp/tools/context.js +261 -0
  66. package/dist/mcp/tools/context.js.map +1 -0
  67. package/dist/mcp/tools/index.d.ts +5 -0
  68. package/dist/mcp/tools/index.d.ts.map +1 -0
  69. package/dist/mcp/tools/index.js +20 -0
  70. package/dist/mcp/tools/index.js.map +1 -0
  71. package/dist/mcp/tools/memory.d.ts +4 -0
  72. package/dist/mcp/tools/memory.d.ts.map +1 -0
  73. package/dist/mcp/tools/memory.js +220 -0
  74. package/dist/mcp/tools/memory.js.map +1 -0
  75. package/dist/mcp/tools/output.d.ts +4 -0
  76. package/dist/mcp/tools/output.d.ts.map +1 -0
  77. package/dist/mcp/tools/output.js +206 -0
  78. package/dist/mcp/tools/output.js.map +1 -0
  79. package/dist/mcp/tools/recovery.d.ts +4 -0
  80. package/dist/mcp/tools/recovery.d.ts.map +1 -0
  81. package/dist/mcp/tools/recovery.js +165 -0
  82. package/dist/mcp/tools/recovery.js.map +1 -0
  83. package/dist/mcp/tools/registrar.d.ts +4 -0
  84. package/dist/mcp/tools/registrar.d.ts.map +1 -0
  85. package/dist/mcp/tools/registrar.js +17 -0
  86. package/dist/mcp/tools/registrar.js.map +1 -0
  87. package/dist/mcp/tools/report.d.ts +4 -0
  88. package/dist/mcp/tools/report.d.ts.map +1 -0
  89. package/dist/mcp/tools/report.js +68 -0
  90. package/dist/mcp/tools/report.js.map +1 -0
  91. package/dist/mcp/tools/shared.d.ts +37 -0
  92. package/dist/mcp/tools/shared.d.ts.map +1 -0
  93. package/dist/mcp/tools/shared.js +214 -0
  94. package/dist/mcp/tools/shared.js.map +1 -0
  95. package/dist/mcp/trace.d.ts +47 -0
  96. package/dist/mcp/trace.d.ts.map +1 -0
  97. package/dist/mcp/trace.js +216 -0
  98. package/dist/mcp/trace.js.map +1 -0
  99. package/dist/nidra/index.d.ts +275 -0
  100. package/dist/nidra/index.d.ts.map +1 -0
  101. package/dist/nidra/index.js +889 -0
  102. package/dist/nidra/index.js.map +1 -0
  103. package/dist/persistence/migrations.d.ts +10 -0
  104. package/dist/persistence/migrations.d.ts.map +1 -0
  105. package/dist/persistence/migrations.js +77 -0
  106. package/dist/persistence/migrations.js.map +1 -0
  107. package/dist/persistence/sqlite.d.ts +30 -0
  108. package/dist/persistence/sqlite.d.ts.map +1 -0
  109. package/dist/persistence/sqlite.js +209 -0
  110. package/dist/persistence/sqlite.js.map +1 -0
  111. package/dist/persistence/types.d.ts +104 -0
  112. package/dist/persistence/types.d.ts.map +1 -0
  113. package/dist/persistence/types.js +5 -0
  114. package/dist/persistence/types.js.map +1 -0
  115. package/dist/pulse/index.d.ts +144 -0
  116. package/dist/pulse/index.d.ts.map +1 -0
  117. package/dist/pulse/index.js +453 -0
  118. package/dist/pulse/index.js.map +1 -0
  119. package/dist/raksha/classifiers/http-classifier.d.ts +26 -0
  120. package/dist/raksha/classifiers/http-classifier.d.ts.map +1 -0
  121. package/dist/raksha/classifiers/http-classifier.js +62 -0
  122. package/dist/raksha/classifiers/http-classifier.js.map +1 -0
  123. package/dist/raksha/classifiers/index.d.ts +5 -0
  124. package/dist/raksha/classifiers/index.d.ts.map +1 -0
  125. package/dist/raksha/classifiers/index.js +8 -0
  126. package/dist/raksha/classifiers/index.js.map +1 -0
  127. package/dist/raksha/classifiers/onnx-classifier.d.ts +41 -0
  128. package/dist/raksha/classifiers/onnx-classifier.d.ts.map +1 -0
  129. package/dist/raksha/classifiers/onnx-classifier.js +99 -0
  130. package/dist/raksha/classifiers/onnx-classifier.js.map +1 -0
  131. package/dist/raksha/hallucination-detectors.d.ts +106 -0
  132. package/dist/raksha/hallucination-detectors.d.ts.map +1 -0
  133. package/dist/raksha/hallucination-detectors.js +327 -0
  134. package/dist/raksha/hallucination-detectors.js.map +1 -0
  135. package/dist/raksha/index.d.ts +168 -0
  136. package/dist/raksha/index.d.ts.map +1 -0
  137. package/dist/raksha/index.js +597 -0
  138. package/dist/raksha/index.js.map +1 -0
  139. package/dist/raksha/prompt-injection-detectors.d.ts +30 -0
  140. package/dist/raksha/prompt-injection-detectors.d.ts.map +1 -0
  141. package/dist/raksha/prompt-injection-detectors.js +153 -0
  142. package/dist/raksha/prompt-injection-detectors.js.map +1 -0
  143. package/dist/types.d.ts +1115 -0
  144. package/dist/types.d.ts.map +1 -0
  145. package/dist/types.js +71 -0
  146. package/dist/types.js.map +1 -0
  147. package/dist/util/calibration.d.ts +32 -0
  148. package/dist/util/calibration.d.ts.map +1 -0
  149. package/dist/util/calibration.js +108 -0
  150. package/dist/util/calibration.js.map +1 -0
  151. package/dist/util/id.d.ts +2 -0
  152. package/dist/util/id.d.ts.map +1 -0
  153. package/dist/util/id.js +9 -0
  154. package/dist/util/id.js.map +1 -0
  155. package/dist/vyayam/index.d.ts +76 -0
  156. package/dist/vyayam/index.d.ts.map +1 -0
  157. package/dist/vyayam/index.js +528 -0
  158. package/dist/vyayam/index.js.map +1 -0
  159. package/dist/vyayam/tool-fault-proxy.d.ts +95 -0
  160. package/dist/vyayam/tool-fault-proxy.d.ts.map +1 -0
  161. package/dist/vyayam/tool-fault-proxy.js +170 -0
  162. package/dist/vyayam/tool-fault-proxy.js.map +1 -0
  163. package/docs/ARCHITECTURE.md +162 -0
  164. package/docs/BACKLOG.md +342 -0
  165. package/docs/CONFIGURATION.md +305 -0
  166. package/docs/EVIDENCE.md +232 -0
  167. package/docs/EVIDENCE_MATRIX.md +293 -0
  168. package/docs/KNOWN_FAILURES.md +367 -0
  169. package/docs/MCP.md +614 -0
  170. package/docs/MODULES.md +368 -0
  171. package/docs/SECURITY.md +251 -0
  172. package/docs/TRUST.md +88 -0
  173. package/docs/assets/ojas-hero.png +0 -0
  174. package/package.json +101 -0
package/docs/MCP.md ADDED
@@ -0,0 +1,614 @@
1
+ # MCP Server
2
+
3
+ Ojas ships an MCP server that exposes 18 agent-health tools to Claude Code, Cursor, Windsurf, and other MCP-compatible clients.
4
+
5
+ Each IDE session gets its own multi-agent registry. Every `agent_id` maps to an Ojas instance and `HealthContract`. Unknown agents can auto-register with conservative defaults so tools can be called without manual setup — but production deployments should disable that and use the allowlist (see [Environment variables](#env)).
6
+
7
+ | Section | What's inside |
8
+ |---|---|
9
+ | [Build](#build) | Compile the MCP server entry point |
10
+ | [MCP Configuration](#mcp-config) | Claude Code, Cursor, Windsurf wiring |
11
+ | [Environment variables](#env) | Operational knobs for shared deployments |
12
+ | [A typical first session](#first-session) | Six tool calls that show the full loop |
13
+ | [Tools — Setup & telemetry](#tools-setup) | `ojas_register_agent`, `ojas_ingest_trace` |
14
+ | [Tools — Decisions](#tools-decisions) | `ojas_get_health`, `ojas_is_agent_fit_to_continue` |
15
+ | [Tools — Context safety](#tools-context) | `ojas_score_context`, `ojas_build_context`, `ojas_scan_for_injection` |
16
+ | [Tools — Output quality & routing](#tools-output) | `ojas_detect_hallucination`, `ojas_recommend_model_route`, `ojas_distill_response`, `ojas_record_task_outcome` |
17
+ | [Tools — Memory hygiene](#tools-memory) | `ojas_audit_memory`, `ojas_validate_memory_write`, `ojas_consolidate_memory` |
18
+ | [Tools — Repair](#tools-repair) | `ojas_diagnose_failure`, `ojas_run_recovery` |
19
+ | [Tools — Reporting & handoff](#tools-reporting) | `ojas_generate_handoff`, `ojas_generate_health_report` |
20
+ | [Response envelope](#envelope) | Standard fields wrapping every tool reply |
21
+ | [Suggested usage loop](#usage-loop) | The canonical call order |
22
+ | [Verification](#verification) | One-line smoke test for a fresh install |
23
+
24
+ ---
25
+
26
+ <a id="build"></a>
27
+ ## Build
28
+
29
+ ```bash
30
+ npm install
31
+ npm run build
32
+ ```
33
+
34
+ Compiled entry point:
35
+
36
+ ```text
37
+ dist/mcp/server.js
38
+ ```
39
+
40
+ Replace `/absolute/path/to/ojas` in the examples below with your local clone path.
41
+
42
+ ---
43
+
44
+ <a id="mcp-config"></a>
45
+ ## MCP Configuration
46
+
47
+ ### Claude Code
48
+
49
+ Project-level `.mcp.json`:
50
+
51
+ ```json
52
+ {
53
+ "mcpServers": {
54
+ "ojas": {
55
+ "command": "node",
56
+ "args": ["/absolute/path/to/ojas/dist/mcp/server.js"],
57
+ "env": {
58
+ "OJAS_AGENT_ID": "claude-code"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ Or with the CLI:
66
+
67
+ ```bash
68
+ claude mcp add ojas -- node /absolute/path/to/ojas/dist/mcp/server.js
69
+ ```
70
+
71
+ ### Cursor
72
+
73
+ Project-level `.cursor/mcp.json` or global `~/.cursor/mcp.json`:
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "ojas": {
79
+ "command": "node",
80
+ "args": ["/absolute/path/to/ojas/dist/mcp/server.js"],
81
+ "env": {
82
+ "OJAS_AGENT_ID": "cursor"
83
+ }
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### Windsurf
90
+
91
+ `~/.codeium/windsurf/mcp_config.json`:
92
+
93
+ ```json
94
+ {
95
+ "mcpServers": {
96
+ "ojas": {
97
+ "command": "node",
98
+ "args": ["/absolute/path/to/ojas/dist/mcp/server.js"],
99
+ "env": {
100
+ "OJAS_AGENT_ID": "windsurf"
101
+ }
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ <a id="env"></a>
110
+ ## Environment variables
111
+
112
+ | Variable | Default | Effect |
113
+ |---|---|---|
114
+ | `OJAS_AGENT_ID` | unset | Pre-register a single agent at startup with conservative defaults. Additional agents can still be added via `ojas_register_agent`. |
115
+ | `OJAS_DISABLE_AUTO_REGISTER` | unset | Set to `1` to reject MCP calls whose `agent_id` has not been explicitly registered. Recommended for production. |
116
+ | `OJAS_MAX_AGENTS` | `256` | Hard cap on simultaneously-tracked agents. Once full, `ojas_register_agent` throws rather than silently evicting. |
117
+ | `OJAS_ALLOWED_AGENT_IDS` | unset | Comma-separated allowlist of `agent_id`s. When set, any registration or auto-register attempt for an id outside this list is rejected. |
118
+ | `OJAS_ALLOW_REPLACE_EXISTING` | unset | Set to `1` to permit `register_agent(replace_existing=true)`. **Disabled by default** because replacement wipes the agent's accumulated Ojas state. |
119
+ | `OJAS_TRUSTED_SINGLE_TENANT` | unset | Acknowledgement that the operator understands the no-auth single-tenant trust model. Suppresses the loud stderr warning emitted at startup; does **not** add authentication. |
120
+
121
+ > `register_agent` rejects a duplicate `agent_id` by default. To replace an existing agent (and discard its accumulated Ojas state) the caller must pass `replace_existing: true` **and** the registry must be configured with `allowReplaceExisting: true` (or the server started with `OJAS_ALLOW_REPLACE_EXISTING=1`). Silently overwriting a long-lived agent's history would erase the very telemetry the system exists to capture.
122
+
123
+ ---
124
+
125
+ <a id="trust-boundary"></a>
126
+ ## Local-only stdio trust boundary
127
+
128
+ The MCP server is designed for **local stdio use**. It assumes the MCP
129
+ host that starts the process is trusted. Agent IDs are routing
130
+ identifiers, **not credentials**, and there is no per-call authentication
131
+ inside the server — there is no portable stdio auth channel for one to
132
+ hook into. This is the intended trust boundary for v0.2, not a deferred
133
+ fix; see the [security non-goals](./SECURITY.md#security-non-goals-for-v02).
134
+
135
+ Recommended locked-down local configuration:
136
+
137
+ ```bash
138
+ OJAS_TRUSTED_SINGLE_TENANT=1 # acknowledge the trust model (silences warning)
139
+ OJAS_DISABLE_AUTO_REGISTER=1 # reject unknown agent_id, force explicit registration
140
+ OJAS_ALLOWED_AGENT_IDS=my-agent # hard allowlist
141
+ OJAS_MAX_AGENTS=1 # one agent per process
142
+ # do NOT set OJAS_ALLOW_REPLACE_EXISTING unless intentionally resetting state
143
+ ```
144
+
145
+ Operationally:
146
+
147
+ - Run **one process per trusted user / workspace**.
148
+ - Do **not** expose this server over a network or share one process across untrusted users.
149
+ - For multi-user or network deployments, front Ojas with an authenticated MCP gateway, or run one isolated Ojas process per user / workspace.
150
+
151
+ If Ojas ever ships a non-stdio transport (HTTP, SSE, streamable-HTTP),
152
+ that transport will fail closed unless auth is configured.
153
+
154
+ ---
155
+
156
+ <a id="first-session"></a>
157
+ ## A typical first session
158
+
159
+ A new agent typically issues this exact sequence during one task. Each step shows the request shape (`→`) and the most-relevant response fields (`←`). The standard envelope (`status`, `correlation_id`, `affected_modules`, …) is present on every response but omitted here for brevity.
160
+
161
+ ```jsonc
162
+ // 1. Register the agent and create its health contract.
163
+ → ojas_register_agent
164
+ {
165
+ "agent_id": "research-agent",
166
+ "risk_level": "medium",
167
+ "health_thresholds": { "minimum_ojas_score": 80 },
168
+ "recovery_policy": { "on_prompt_injection": "quarantine_input" }
169
+ }
170
+ ← {
171
+ "registered": true,
172
+ "initial_ojas_score": 100,
173
+ "health_state": "healthy",
174
+ "health_contract": { "agent_id": "research-agent", "health_contract_id": "contract_..." }
175
+ }
176
+
177
+ // 2. Filter retrieved content through Raksha + Aahar before reading it.
178
+ → ojas_build_context
179
+ {
180
+ "agent_id": "research-agent",
181
+ "task": "Compare vector databases for enterprise RAG",
182
+ "max_tokens": 4096,
183
+ "candidate_items": [ /* 17 retrieved items */ ]
184
+ }
185
+ ← {
186
+ "context_bundle_id": "bundle_...",
187
+ "context_health_score": 87,
188
+ "included_items": 7,
189
+ "excluded_items": 10,
190
+ "retained_token_ratio": 0.42, // accepted_tokens / total_tokens
191
+ "compression_ratio": 0.42, // legacy alias for retained_token_ratio
192
+ "warnings": ["excluded 2 stale items", "quarantined 1 injection attempt"],
193
+ "context": "[#ctx-001 | analyst_report] Pinecone offers managed scaling ..."
194
+ }
195
+
196
+ // 3. Stream a trace every time the agent calls a tool.
197
+ → ojas_ingest_trace
198
+ {
199
+ "agent_id": "research-agent",
200
+ "event_type": "tool_call_failed",
201
+ "data": { "tool_name": "web_search", "error_type": "timeout", "latency_ms": 8000 }
202
+ }
203
+ ← {
204
+ "accepted": true,
205
+ "event_id": "evt_...",
206
+ "trace_id": "trace_...",
207
+ "health_impact_detected": true,
208
+ "recommended_action": "diagnose_tool_failure"
209
+ }
210
+
211
+ // 4. Before the next risky action, ask the gate.
212
+ → ojas_is_agent_fit_to_continue
213
+ {
214
+ "agent_id": "research-agent",
215
+ "task_risk_level": "high",
216
+ "planned_next_action": "call_web_search"
217
+ }
218
+ ← {
219
+ "fit_to_continue": false,
220
+ "confidence": 0.62,
221
+ "health_state": "watch",
222
+ "ojas_score": 62,
223
+ "required_score": 90,
224
+ "risks": ["recent tool timeout"],
225
+ "required_actions_before_continue": ["run ojas_run_recovery to lift score above threshold"],
226
+ "safe_mode_required": false
227
+ }
228
+
229
+ // 5. Not fit — diagnose, then run recovery in recommend mode (the default).
230
+ → ojas_diagnose_failure
231
+ {
232
+ "agent_id": "research-agent",
233
+ "failure_description": "web_search timed out 3 times in 60 seconds"
234
+ }
235
+ ← {
236
+ "diagnosis_id": "chikitsa-...",
237
+ "primary_cause": "loop",
238
+ "secondary_causes": ["high_cost_pressure"],
239
+ "recommended_repair": ["context_reset", "plan_regeneration"],
240
+ "severity": "warning"
241
+ }
242
+
243
+ → ojas_run_recovery
244
+ { "agent_id": "research-agent", "recovery_type": "tool_failure_loop" }
245
+ ← {
246
+ "recovery_id": "recovery_...",
247
+ "mode": "recommend",
248
+ "outcome": "recommended",
249
+ "actions": [
250
+ { "action": "freeze_memory_writes", "reason": "avoid storing degraded loop behavior" },
251
+ { "action": "reset_plan", "reason": "agent is stuck in a repeated tool-call loop" },
252
+ { "action": "generate_fallback_response", "reason": "underlying tool unavailable after repeated attempts" }
253
+ ]
254
+ }
255
+
256
+ // 6. After applying recovery, generate the session report.
257
+ → ojas_generate_health_report
258
+ { "agent_id": "research-agent", "scope": "session" }
259
+ ← {
260
+ "overall_ojas_score": 87,
261
+ "module_scores": { "aahar": 91, "nidra": 78, "vyayam": 97, "raksha": 93, "agni": 96, "pulse": 68, "chikitsa": 72 },
262
+ "summary": "research-agent recovered from a tool-failure loop; healthy to resume.",
263
+ "top_risks": ["over_retrieval"],
264
+ "recommended_next_steps": ["compress_context"]
265
+ }
266
+ ```
267
+
268
+ ---
269
+
270
+ <a id="tools-setup"></a>
271
+ ## 1. Setup & telemetry
272
+
273
+ Every interaction starts here.
274
+
275
+ | Tool | Purpose | Key input | Key output |
276
+ |---|---|---|---|
277
+ | `ojas_register_agent` | Create the agent's health contract | `agent_id`, `risk_level`, `health_thresholds`, `recovery_policy`, optional `allowed_tools[]`, optional `replace_existing` | `registered`, `health_contract`, `initial_ojas_score`, `health_state` |
278
+ | `ojas_ingest_trace` | Stream a runtime event into Ojas | `event_type` (one of 17), `data` | `accepted`, `event_id`, `health_impact_detected`, `policy_violation`, `recommended_action` |
279
+
280
+ > When a contract sets `allowed_tools`, any `ojas_ingest_trace` call with `data.tool_name` outside that list is recorded as a `tool_policy_violation`. The response sets `accepted: false`, `policy_violation: true`, `health_impact_detected: true`, `recommended_action: "review_tool_policy_violation"`, and flags `requires_human_review: true` in the envelope. The tool name is also reflected in the trace's `error` field so Chikitsa picks it up for diagnosis.
281
+
282
+ Supported `event_type` values:
283
+
284
+ ```text
285
+ agent_input agent_output
286
+ tool_call_started tool_call_succeeded tool_call_failed
287
+ retrieval_result memory_read memory_write
288
+ failure latency_measurement token_usage
289
+ user_correction planning_loop fallback
290
+ policy_violation prompt_injection_detected context_overload_detected
291
+ ```
292
+
293
+ Each event is routed to the relevant modules — for example `tool_call_failed` flows to Agni (cost), Pulse (telemetry), and Chikitsa (diagnosis), and the response includes the recommended follow-up.
294
+
295
+ ---
296
+
297
+ <a id="tools-decisions"></a>
298
+ ## 2. Decisions
299
+
300
+ Current state, fitness gating, and operational fitness.
301
+
302
+ | Tool | Purpose | Key input | Key output |
303
+ |---|---|---|---|
304
+ | `ojas_get_health` | Current Ojas snapshot | `agent_id`, `include_modules`, `include_recent_events` | `ojas_score`, `health_state`, `module_scores`, `active_risks`, `recommended_actions` |
305
+ | **`ojas_is_agent_fit_to_continue`** | **Signature gate.** Should the agent proceed? | `agent_id`, `task_risk_level`, `planned_next_action` | `fit_to_continue`, `confidence`, `required_score`, `required_actions_before_continue`, `safe_mode_required` |
306
+
307
+ The fitness gate answers the operational question most agent frameworks don't ask:
308
+
309
+ > Is this agent cognitively healthy enough to proceed?
310
+
311
+ The required score scales with `task_risk_level`: **medium** uses the contract's `minimum_ojas_score`, **high** adds +10, **critical** adds +20. Critical-risk tasks on a non-healthy agent flip `safe_mode_required` to `true` and `requires_human_review` to `true` in the envelope.
312
+
313
+ ---
314
+
315
+ <a id="tools-context"></a>
316
+ ## 3. Context safety
317
+
318
+ Everything the agent reads passes through one of these.
319
+
320
+ | Tool | Purpose | Key input | Key output |
321
+ |---|---|---|---|
322
+ | `ojas_score_context` | Per-item nutrition + threat scoring | `agent_id`, `task`, `context_items[]` | `context_health_score`, `item_scores[]` (with `relevance_score`, `relevance_source` (`caller`/`lexical_fallback`), `freshness_score`, `trust_score`, `risk_score`, `recommendation`), `warnings` |
323
+ | `ojas_build_context` | Assemble a clean bundle within a token budget | `agent_id`, `task`, `max_tokens`, `risk_level`, `candidate_items[]` | `context_bundle_id`, `included_ids`, `retained_token_ratio` (and legacy `compression_ratio = retained_token_ratio`), `warnings`, `context` |
324
+ | `ojas_scan_for_injection` | One-shot threat scan on a single piece of content | `agent_id`, `source_type`, `content`, `intended_use` | `injection_detected`, `risk_score`, `attack_type`, `allowed_for_context`, `allowed_for_memory`, `reasons` |
325
+
326
+ `scan_for_injection` is the right tool when you want a verdict on a single string (e.g. one retrieved page) without putting it through Aahar. `score_context` and `build_context` are batch tools that run the same Raksha scan plus Aahar nutrition.
327
+
328
+ ---
329
+
330
+ <a id="tools-output"></a>
331
+ ## 4. Output quality & routing
332
+
333
+ These tools operate after or around generation: checking the answer, trimming response load, and feeding success/failure learning back into Agni and Chikitsa.
334
+
335
+ | Tool | Purpose | Key input | Key output |
336
+ |---|---|---|---|
337
+ | `ojas_detect_hallucination` | Score an agent output for hallucination risk | `output`, optional `prompt`, `samples[]`, `context[]` | `hallucination_detected`, `risk_score`, `confidence`, `detector`, `reasons`, `claims[]`, `abstention` |
338
+ | `ojas_recommend_model_route` | Decide whether a task class can use a cheap route | `task_class`, optional `include_report` | `route` (`cheap` / `flagship`), `reason`, `observations`, `success_ci`, `router_report?` |
339
+ | `ojas_distill_response` | Remove low-signal filler from an output | `response`, optional `intensity` (`lite` / `full` / `ultra`) | `distilled_response`, `tokens_removed`, `chars_removed`, `removed_categories`, `distiller` |
340
+ | `ojas_record_task_outcome` | Feed task success/failure back into routing and velocity stats | `task_id`, `succeeded`, `duration_ms`, optional `task_class`, `category`, `completed_at` | `recorded`, `route_after_recording`, `velocity_stats` |
341
+
342
+ `recommend_model_route` is deliberately fail-closed: sparse data, safety classes such as `security` / `auth`, or a weak Wilson confidence lower bound all return `flagship`. `record_task_outcome` is how MCP clients teach that router over time; it also updates Chikitsa's rolling velocity window for handoffs.
343
+
344
+ `distill_response` uses the configured Agni distiller by default and updates `lifetime_output_tokens_saved`. If a per-call `intensity` is supplied, Ojas uses a temporary default distiller at that intensity and reports `lifetime_counter_updated: false`.
345
+
346
+ ---
347
+
348
+ <a id="tools-memory"></a>
349
+ ## 5. Memory hygiene
350
+
351
+ Memory is a living organ; these tools curate it.
352
+
353
+ | Tool | Purpose | Key input | Key output |
354
+ |---|---|---|---|
355
+ | `ojas_audit_memory` | Audit the agent's memory store | `agent_id`, `scope`, `include_recommendations` | `memory_health_score`, `issues` (stale / duplicate / conflicting / high_risk / low_usefulness), `recommended_actions` |
356
+ | **`ojas_validate_memory_write`** | Policy gate before a memory commit | `candidate_memory` (`content`, `source`, `confidence`), `reason_for_write` | `allowed`, `memory_status` (`committed` / `candidate` / `session_note` / `rejected`), `reason`, `risk_score` |
357
+ | `ojas_consolidate_memory` | Run a Nidra cycle over recent traces | `consolidation_depth`, `allow_new_memories`, `allow_memory_pruning`, `include_failure_reflection` | `consolidation_id`, `cycle_status`, `memories[]` (proposed), `memories_to_prune`, `lessons_extracted`, `drift_reduction` |
358
+
359
+ `validate_memory_write` is deliberately conservative by default:
360
+
361
+ ```text
362
+ confidence ≥ 0.75 + no Raksha hit → committed
363
+ confidence ≥ 0.50 + no Raksha hit → candidate (not yet committed)
364
+ confidence < 0.50 → session_note (scoped to this session)
365
+ any Raksha hit (risk_score > 0.5) → rejected
366
+ ```
367
+
368
+ `consolidate_memory` **proposes** memories but does not commit them unless `allow_new_memories: true`. The default is `false` — memory writes are dangerous and should require explicit opt-in.
369
+
370
+ - A preview run emits a `recovery_cycle_previewed` event and returns `cycle_status: "preview"`, `health_after_estimated: true`. The unprocessed-trace set is untouched, so calling `consolidate_memory` twice with `allow_new_memories: false` is guaranteed to be a no-op.
371
+ - A commit run emits `recovery_cycle_completed` and returns `cycle_status: "committed"`, `health_after_estimated: false`. The bound agent's `injectMemory()` is invoked for every memory before Nidra commits — see the SDK note below.
372
+
373
+ > **SDK transactionality:** when you bind an `AgentAdapter` and call `Ojas.recover()`, the cycle runs as `analyse → injectMemory(...) → commit`. If any `injectMemory` throws, Nidra state is **not** mutated: no traces are marked processed, no memories are persisted, and a `recovery_injection_failed` pulse event explains the abort. This guarantees the agent's view of memory and Ojas's view stay in sync.
374
+
375
+ ---
376
+
377
+ <a id="tools-repair"></a>
378
+ ## 6. Repair
379
+
380
+ Diagnose failures and apply structured recovery.
381
+
382
+ | Tool | Purpose | Key input | Key output |
383
+ |---|---|---|---|
384
+ | `ojas_diagnose_failure` | Chikitsa classifies a failure | `agent_id`, `failure_description`, `failure_type?`, `severity` | `diagnosis_id`, `primary_cause`, `secondary_causes`, `recommended_repair`, `severity` |
385
+ | **`ojas_run_recovery`** | Execute a recovery protocol | `recovery_type`, `mode` (`observe` / `recommend` / `apply`), `allowed_actions?` | `recovery_id`, `mode`, `outcome` (`recommended` / `applied` / `instructed` / `no_effect` / `requires_human_review`), `actions[]`, `mutated_actions[]`, `instruction_actions[]`, `skipped_actions[]`, `applied_actions[]` (deprecated alias for durable mutations), `ojas_score_before`, `ojas_score_after`, `ojas_score_delta` |
386
+
387
+ `run_recovery` is safe-by-default: `mode` defaults to `recommend`, so the tool returns the action plan without executing it. In `apply` mode, Ojas separates durable mutations from caller instructions: `enter_safe_mode` appears under `mutated_actions`, advisory steps such as `compress_context` appear under `instruction_actions`, and `human_escalation` appears under `skipped_actions`. The legacy `applied_actions` field is retained for one release as a deprecated alias for `mutated_actions` only.
388
+
389
+ `recovery_type` values:
390
+
391
+ ```text
392
+ tool_failure_loop context_overload prompt_injection
393
+ memory_conflict hallucination high_cost_pressure
394
+ unknown
395
+ ```
396
+
397
+ Recovery action vocabulary (also reusable as `allowed_actions`):
398
+
399
+ ```text
400
+ compress_context reset_plan freeze_memory_writes
401
+ quarantine_input generate_fallback_response
402
+ enter_safe_mode human_escalation
403
+ reduce_tool_calls switch_to_cached_sources
404
+ ```
405
+
406
+ ---
407
+
408
+ <a id="tools-reporting"></a>
409
+ ## 7. Reporting & handoff
410
+
411
+ Human-readable continuity and summary tools.
412
+
413
+ | Tool | Purpose | Key input | Key output |
414
+ |---|---|---|---|
415
+ | `ojas_generate_handoff` | Markdown handoff for another agent/session | `focus`, `pending_tasks[]`, `completed_tasks[]`, `notes[]`, `include_velocity` | `handoff_markdown`, `generated_at`, `pending_count`, `completed_count`, `velocity_stats` |
416
+ | `ojas_generate_health_report` | Executive summary for dashboards / release gates / postmortems | `agent_id`, `scope`, `report_type`, `include_recommendations` | `report_id`, `overall_ojas_score`, `health_state`, `module_scores`, `summary`, `top_risks`, `recommended_next_steps` |
417
+
418
+ Use `generate_handoff` when work is moving between agents or sessions. Use `generate_health_report` at session end, on a periodic cron, or as a CI release gate. `scope` accepts `current`, `session`, or `last_24h`; `report_type` accepts `executive_summary` or `technical`.
419
+
420
+ ---
421
+
422
+ <a id="session-isolation"></a>
423
+ ## Session isolation
424
+
425
+ Ojas MCP tools accept an optional `session_id` argument. Calls with the same
426
+ `agent_id` but different `session_id`s receive separate Ojas runtime state:
427
+ trace windows, memories, tool-outcome history, recovery cycles, safe-mode
428
+ flags, and health snapshots are isolated per session. Omitting `session_id`
429
+ uses the backward-compatible default session.
430
+
431
+ Every response from a session-aware tool surfaces this honestly:
432
+
433
+ ```json
434
+ {
435
+ "session_id": "sess_abc",
436
+ "session_scope_supported": true
437
+ }
438
+ ```
439
+
440
+ When `OJAS_DB_PATH=/path/to/ojas.sqlite` is set, the registry persists those
441
+ session snapshots to SQLite and hydrates them on server restart.
442
+
443
+ ---
444
+
445
+ <a id="error-semantics"></a>
446
+ ## Error semantics: structured envelopes vs MCP protocol errors
447
+
448
+ Ojas distinguishes two error paths and clients should handle both.
449
+
450
+ **1. Structured `failed` envelope (`status: 'failed'`).** Any exception
451
+ raised *inside* an Ojas tool handler — including typed `OjasError`s and
452
+ unexpected throws — is caught by `safeHandler` and returned as a normal
453
+ tool response with this shape:
454
+
455
+ ```json
456
+ {
457
+ "status": "failed",
458
+ "accepted": false,
459
+ "error": "Agent 'x' is already registered. ...",
460
+ "error_class": "ojas_register_agent_error",
461
+ "error_code": "policy_violation",
462
+ "requires_human_review": false
463
+ }
464
+ ```
465
+
466
+ `error_code` is one of `invalid_input`, `policy_violation`, `not_found`,
467
+ or `internal_error`. Only `internal_error` defaults to
468
+ `requires_human_review: true` — validation and policy refusals are
469
+ caller-side problems and should be surfaced in the client UI, not the
470
+ operator pager.
471
+
472
+ **2. MCP-protocol-level error.** Input that fails the tool's Zod
473
+ `inputSchema` (e.g. a string where a number was required, an unknown
474
+ `event_type`, a non-ISO-8601 `created_at`) is rejected by the MCP SDK
475
+ *before* the handler runs. That arrives at the client as an MCP protocol
476
+ error, not as a `status: 'failed'` envelope, because `safeHandler` has
477
+ no opportunity to wrap it.
478
+
479
+ In practice this means MCP clients need two error-handling paths:
480
+
481
+ - check `response.status === 'failed'` first to detect tool-level errors;
482
+ - catch transport / protocol errors as a separate branch for schema
483
+ rejection and SDK-internal failures.
484
+
485
+ The split is intentional. Loosening schemas to let everything reach the
486
+ handler would weaken the input contract and force every tool to
487
+ re-implement validation. Documenting the split here so clients aren't
488
+ surprised.
489
+
490
+ ---
491
+
492
+ <a id="envelope"></a>
493
+ ## Response envelope
494
+
495
+ Every MCP tool returns a standard envelope merged with tool-specific fields.
496
+
497
+ ```jsonc
498
+ {
499
+ "status": "success | warning | failed",
500
+ "agent_id": "research_agent_01",
501
+ "correlation_id": "cor-...",
502
+ "ojas_score_delta": 0,
503
+ "affected_modules": ["aahar", "raksha"],
504
+ "events_created": ["evt_..."],
505
+ "recommended_next_actions": ["compress_context"],
506
+ "requires_human_review": false
507
+ }
508
+ ```
509
+
510
+ `ojas_ingest_trace` also returns these three orthogonal booleans for
511
+ unambiguous client branching (previously folded into `accepted`):
512
+
513
+ ```jsonc
514
+ {
515
+ "event_recorded": true, // trace landed in the store
516
+ "policy_accepted": true, // health-contract / allowed_tools gates passed
517
+ "telemetry_accepted": true // every reported token / cost field parsed cleanly
518
+ }
519
+ ```
520
+
521
+ `accepted` is preserved for backward compatibility and equals
522
+ `policy_accepted` — new code should branch on the three explicit fields.
523
+
524
+ Tool-specific fields may include:
525
+
526
+ ```text
527
+ ojas_score
528
+ health_state
529
+ fit_to_continue
530
+ required_score
531
+ context_health_score
532
+ injection_detected
533
+ hallucination_detected
534
+ route
535
+ distilled_response
536
+ velocity_stats
537
+ handoff_markdown
538
+ recommended_repair
539
+ recovery_actions
540
+ memory_write_decision
541
+ provenance // 'full' | 'degraded' on consolidated memories
542
+ ```
543
+
544
+ ---
545
+
546
+ <a id="usage-loop"></a>
547
+ ## Suggested usage loop
548
+
549
+ ```text
550
+ register
551
+
552
+ ingest_trace
553
+
554
+ score_context / build_context
555
+
556
+ scan_for_injection on retrieval or tool outputs
557
+
558
+ recommend_model_route before model choice
559
+
560
+ detect_hallucination / distill_response after generation
561
+
562
+ record_task_outcome after completion
563
+
564
+ is_agent_fit_to_continue
565
+
566
+ if unhealthy: diagnose_failure → run_recovery
567
+
568
+ consolidate_memory
569
+
570
+ audit_memory
571
+
572
+ generate_handoff / generate_health_report
573
+ ```
574
+
575
+ Recommended call points:
576
+
577
+ - **Before retrieval-augmented work**: call `ojas_score_context` and `ojas_build_context`
578
+ - **After every tool call**: call `ojas_ingest_trace`
579
+ - **Before risky actions**: call `ojas_is_agent_fit_to_continue`
580
+ - **Before model choice**: call `ojas_recommend_model_route`
581
+ - **After generation**: call `ojas_detect_hallucination` and optionally `ojas_distill_response`
582
+ - **After task completion**: call `ojas_record_task_outcome`
583
+ - **Before memory writes**: call `ojas_validate_memory_write`
584
+ - **After failures**: call `ojas_diagnose_failure`, then `ojas_run_recovery`
585
+ - **At session end**: call `ojas_consolidate_memory`, `ojas_generate_handoff`, and `ojas_generate_health_report`
586
+
587
+ ---
588
+
589
+ <a id="verification"></a>
590
+ ## Verification
591
+
592
+ After registering the server, restart your IDE and ask the assistant:
593
+
594
+ ```text
595
+ Call ojas_register_agent with agent_id=my-coding-agent, then call ojas_is_agent_fit_to_continue with task_risk_level=high.
596
+ ```
597
+
598
+ Expected response:
599
+
600
+ ```json
601
+ {
602
+ "fit_to_continue": true,
603
+ "confidence": 0.91,
604
+ "health_state": "healthy",
605
+ "ojas_score": 91,
606
+ "required_score": 80,
607
+ "risks": [],
608
+ "required_actions_before_continue": [],
609
+ "safe_mode_required": false,
610
+ "status": "success",
611
+ "agent_id": "my-coding-agent",
612
+ "correlation_id": "cor-..."
613
+ }
614
+ ```