@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
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ /**
3
+ * ToolFaultProxy — environmental fault injection for stress testing.
4
+ *
5
+ * Why this exists
6
+ * ---------------
7
+ * Vyayam's `latency_spike` and `tool_failure` scenarios used to be
8
+ * *prompt-level* only — they modified the prompt or context to describe
9
+ * a failure, but the bound agent's environment was unchanged. So
10
+ * "passed = 8/8" really meant "the agent produced acceptable output
11
+ * when *told* to imagine a tool failure", not "the agent demonstrably
12
+ * handled a real tool failure". That made the resilience suite a
13
+ * known-honest-limitation in `docs/KNOWN_FAILURES.md`.
14
+ *
15
+ * `ToolFaultProxy` closes that gap by wrapping any `AgentAdapter` and
16
+ * injecting *real* environmental faults around `process()`:
17
+ *
18
+ * - **`injectLatencyMs`** — adds a synthetic delay before delegating
19
+ * to the inner agent. Lets us probe how Vyayam's
20
+ * `maxScenarioDurationMs` reacts to a genuinely slow tool.
21
+ * - **`failureProbability`** — on each `process()` call, with the
22
+ * configured probability, *either* throws a synthetic error (mode
23
+ * `'throw'`) *or* returns a low-confidence error response (mode
24
+ * `'degraded'`). The probability is driven by an injectable PRNG so
25
+ * scenarios are reproducible.
26
+ *
27
+ * The proxy preserves `AgentAdapter` exactly: `id`, `getState`,
28
+ * `injectMemory`, and any extra methods on the inner agent are
29
+ * delegated through. Callers can pass a `ToolFaultProxy` anywhere an
30
+ * `AgentAdapter` is expected (e.g. straight into
31
+ * `vyayam.executeStressTest(proxy, scenario)`).
32
+ *
33
+ * What it does NOT do
34
+ * -------------------
35
+ * - Does not actually call out to the agent's tool registry — Ojas
36
+ * does not model individual tools as first-class objects. It
37
+ * simulates the *result* an agent sees: latency, failure, or both.
38
+ * - Respects `AbortSignal` when provided: injected latency is
39
+ * cancelled immediately, and the inner agent receives the signal
40
+ * so it can abort in-flight work.
41
+ * - Does not magically make existing scenarios environmental — only
42
+ * `latency_spike` and `tool_failure` are routed through here. Other
43
+ * stress types remain prompt-level *by design* (e.g.
44
+ * `prompt_injection`, `adversarial_input`, `conflicting_instructions`
45
+ * are inherently prompt-level).
46
+ */
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.ToolFaultProxy = void 0;
49
+ exports.faultPolicyForScenarioType = faultPolicyForScenarioType;
50
+ const DEFAULT_FAILURE_MESSAGE = 'tool fault injected by ToolFaultProxy';
51
+ /**
52
+ * Wrap an `AgentAdapter` with environmental fault injection. The returned
53
+ * proxy behaves like the inner agent on the happy path and injects a
54
+ * latency / failure on faulted calls.
55
+ *
56
+ * Counter-intuitive but intentional: `id` is preserved verbatim from the
57
+ * inner agent, so Vyayam, Pulse, Raksha etc. attribute traces / events
58
+ * to the *real* agent. The fault injection should look like the agent's
59
+ * environment failing, not like a new agent.
60
+ */
61
+ class ToolFaultProxy {
62
+ id;
63
+ /** Number of times `process()` returned a faulted result, for tests / telemetry. */
64
+ faultsInjected = 0;
65
+ inner;
66
+ policy;
67
+ constructor(inner, policy = {}) {
68
+ this.inner = inner;
69
+ this.id = inner.id;
70
+ this.policy = {
71
+ injectLatencyMs: Math.max(0, policy.injectLatencyMs ?? 0),
72
+ failureProbability: clamp01(policy.failureProbability ?? 0),
73
+ failureMode: policy.failureMode ?? 'degraded',
74
+ failureMessage: policy.failureMessage ?? DEFAULT_FAILURE_MESSAGE,
75
+ rng: policy.rng ?? Math.random,
76
+ };
77
+ }
78
+ async process(input, context, signal) {
79
+ if (signal?.aborted) {
80
+ throw new DOMException('Aborted', 'AbortError');
81
+ }
82
+ if (this.policy.injectLatencyMs > 0) {
83
+ await abortableDelay(this.policy.injectLatencyMs, signal);
84
+ }
85
+ const fault = this.policy.failureProbability > 0 && this.policy.rng() < this.policy.failureProbability;
86
+ if (fault) {
87
+ this.faultsInjected += 1;
88
+ if (this.policy.failureMode === 'throw') {
89
+ throw new Error(this.policy.failureMessage);
90
+ }
91
+ return {
92
+ output: `[tool_fault] ${this.policy.failureMessage}`,
93
+ tokensUsed: 0,
94
+ reasoning: 'ToolFaultProxy: fault injected (degraded mode)',
95
+ confidence: 0.05,
96
+ durationMs: this.policy.injectLatencyMs,
97
+ };
98
+ }
99
+ return this.inner.process(input, context, signal);
100
+ }
101
+ async getState() {
102
+ return this.inner.getState();
103
+ }
104
+ async injectMemory(memory) {
105
+ return this.inner.injectMemory(memory);
106
+ }
107
+ /** Defensive copy of the effective policy. */
108
+ getPolicy() {
109
+ return { ...this.policy };
110
+ }
111
+ }
112
+ exports.ToolFaultProxy = ToolFaultProxy;
113
+ function clamp01(v) {
114
+ if (!Number.isFinite(v))
115
+ return 0;
116
+ return Math.max(0, Math.min(1, v));
117
+ }
118
+ function abortableDelay(ms, signal) {
119
+ if (signal?.aborted)
120
+ return Promise.reject(new DOMException('Aborted', 'AbortError'));
121
+ return new Promise((resolve, reject) => {
122
+ const timer = setTimeout(resolve, ms);
123
+ if (!signal)
124
+ return;
125
+ const onAbort = () => {
126
+ clearTimeout(timer);
127
+ reject(new DOMException('Aborted', 'AbortError'));
128
+ };
129
+ signal.addEventListener('abort', onAbort, { once: true });
130
+ });
131
+ }
132
+ /**
133
+ * Translate a `latency_spike` / `tool_failure` Vyayam scenario into a
134
+ * `ToolFaultPolicy`. Returns `null` for scenarios that should stay
135
+ * prompt-level (so Vyayam keeps the existing behaviour for those).
136
+ *
137
+ * Mapping (intensity in [0, 1]):
138
+ * - latency_spike → injectLatencyMs = intensity * MAX_INJECTED_LATENCY_MS,
139
+ * no failure probability.
140
+ * - tool_failure → failureProbability = intensity,
141
+ * failureMode = 'degraded',
142
+ * latency = 0.
143
+ *
144
+ * `MAX_INJECTED_LATENCY_MS` is intentionally small (250ms) so test suites
145
+ * stay fast. Operators benchmarking real timeouts should construct
146
+ * `ToolFaultProxy` directly with their own latency.
147
+ */
148
+ const MAX_INJECTED_LATENCY_MS = 250;
149
+ function faultPolicyForScenarioType(scenarioType, intensity, opts = {}) {
150
+ const i = clamp01(intensity);
151
+ switch (scenarioType) {
152
+ case 'latency_spike':
153
+ return {
154
+ injectLatencyMs: Math.round(i * MAX_INJECTED_LATENCY_MS),
155
+ failureProbability: 0,
156
+ rng: opts.rng,
157
+ };
158
+ case 'tool_failure':
159
+ return {
160
+ injectLatencyMs: 0,
161
+ failureProbability: i,
162
+ failureMode: 'degraded',
163
+ failureMessage: 'simulated tool returned 5xx',
164
+ rng: opts.rng,
165
+ };
166
+ default:
167
+ return null;
168
+ }
169
+ }
170
+ //# sourceMappingURL=tool-fault-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-fault-proxy.js","sourceRoot":"","sources":["../../src/vyayam/tool-fault-proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,162 @@
1
+ # Architecture
2
+
3
+ Ojas wraps the agent runtime in a four-phase health cycle. Signals flow from execution into observation, then learning + healing, then back into intake — so every new task starts with cleaner context, healed memory, and a calibrated reasoning budget. A gate between cycles (`ojas_is_agent_fit_to_continue`) decides whether the agent proceeds or pauses for repair first.
4
+
5
+ | Section | What's inside |
6
+ |---|---|
7
+ | [The four-phase cycle](#cycle) | Mermaid diagram + plain-text fallback |
8
+ | [Design principles](#principles) | Eight non-negotiables behind Ojas |
9
+ | [Measurement philosophy](#measurement) | What Ojas measures (and what it deliberately doesn't) |
10
+ | [Closing philosophy](#philosophy) | Why "agent health" is the right frame |
11
+
12
+ ---
13
+
14
+ <a id="cycle"></a>
15
+ ## The four-phase cycle
16
+
17
+ ```mermaid
18
+ flowchart TB
19
+ user(["user / environment"])
20
+
21
+ subgraph intake [" ① INTAKE "]
22
+ direction TB
23
+ raksha["Raksha<br/>threat detection"]
24
+ aahar["Aahar<br/>context nutrition"]
25
+ raksha --> aahar
26
+ end
27
+
28
+ execute["② EXECUTE<br/>agent runtime<br/>reasoning · planning · tool use"]
29
+
30
+ subgraph observe [" ③ OBSERVE "]
31
+ direction LR
32
+ pulse["Pulse<br/>vital signs"]
33
+ agni["Agni<br/>metabolism"]
34
+ end
35
+
36
+ subgraph heal [" ④ HEAL "]
37
+ direction LR
38
+ nidra["Nidra<br/>consolidation"]
39
+ vyayam["Vyayam<br/>stress testing"]
40
+ chikitsa["Chikitsa<br/>repair protocols"]
41
+ end
42
+
43
+ gate{"ojas_is_agent_<br/>fit_to_continue?"}
44
+
45
+ consumers[/"MCP clients · dashboards · CI gates"/]
46
+
47
+ user -->|raw context| intake
48
+ intake -->|healthy context| execute
49
+ execute -->|traces · events · state| observe
50
+ observe -->|degradation signals| heal
51
+ heal -->|memories · repair plans · policies| gate
52
+ gate -->|fit| intake
53
+ gate -->|unfit| heal
54
+
55
+ observe -.->|health events| consumers
56
+ gate -.->|scores + reports| consumers
57
+ ```
58
+
59
+ <details>
60
+ <summary>Plain-text view (if the diagram above doesn't render)</summary>
61
+
62
+ ```text
63
+ user / env
64
+
65
+ ▼ raw context
66
+ ① INTAKE Raksha → Aahar
67
+ threat scan · nutrition
68
+
69
+ ▼ healthy context
70
+ ② EXECUTE agent runtime
71
+ reasoning · planning · tool use
72
+
73
+ ▼ traces · events · state
74
+ ③ OBSERVE Pulse · Agni
75
+ vital signs · metabolism · degradation
76
+
77
+ ▼ degradation signals
78
+ ④ HEAL Nidra · Vyayam · Chikitsa
79
+ consolidate · stress-test · diagnose + repair
80
+
81
+ ▼ memories · repair plans · hardened policies
82
+ ┌──────────────────────────────────────────────┐
83
+ │ GATE: ojas_is_agent_fit_to_continue(...) │
84
+ │ fit → next cycle (back into ①) │
85
+ │ unfit → diagnose_failure + run_recovery │
86
+ └──────────────────────────────────────────────┘
87
+
88
+ Consumers (MCP clients · dashboards · CI gates) receive:
89
+ · structured health events from Pulse
90
+ · per-module 0–100 scores from healthCheck() / reports
91
+ ```
92
+
93
+ </details>
94
+
95
+ Ojas can be used as:
96
+
97
+ - a TypeScript SDK
98
+ - an agent health runtime
99
+ - an MCP server for IDE agents (Claude Code, Cursor, Windsurf, …)
100
+ - a telemetry and recovery layer
101
+ - a stress-testing harness for agent reliability
102
+
103
+ ---
104
+
105
+ <a id="principles"></a>
106
+ ## Design principles
107
+
108
+ - **Health before performance.** A high-performing agent that degrades unpredictably is not production-ready.
109
+ - **Context is nutrition.** Agents become what they consume.
110
+ - **Memory must be curated.** Memory is a living organ, not raw storage.
111
+ - **Recovery is first-class.** Agents should recover, reflect, and improve instead of simply failing and restarting.
112
+ - **Stress builds resilience.** Agents should be tested under controlled adversity before production.
113
+ - **Health must be measurable.** Cognitive stability should be scored, trended, and improved.
114
+ - **Autonomy requires immunity.** Tool-using agents need protection from malicious cognitive inputs.
115
+ - **Interventions should be safe by default.** Destructive or irreversible actions should require explicit approval.
116
+
117
+ ---
118
+
119
+ <a id="measurement"></a>
120
+ ## Measurement philosophy
121
+
122
+ Ojas does not try to measure intelligence directly.
123
+
124
+ It measures observable health signals:
125
+
126
+ - context pollution
127
+ - tool thrashing
128
+ - repeated failures
129
+ - hallucination risk
130
+ - memory conflict
131
+ - goal drift
132
+ - prompt-injection exposure
133
+ - recovery time
134
+ - token efficiency
135
+ - degradation risk
136
+
137
+ These signals can be aggregated into an overall health score.
138
+
139
+ The long-term goal is to improve metrics such as:
140
+
141
+ - task completion rate
142
+ - mean time to cognitive failure
143
+ - cognitive recovery time
144
+ - hallucination rate
145
+ - memory health over time
146
+ - resilience under stress
147
+ - tokens per successful task
148
+
149
+ Ojas is useful only if agents perform better with it than without it — which is why every claim in the README is backed by the reproducible A/B harness in `benchmarks/` and the auto-generated [`EVIDENCE.md`](./EVIDENCE.md).
150
+
151
+ ---
152
+
153
+ <a id="philosophy"></a>
154
+ ## Closing philosophy
155
+
156
+ Human intelligence is sustained by nutrition, sleep, exercise, recovery, immune defense, metabolism, and rehabilitation.
157
+
158
+ AI agents currently lack these mechanisms.
159
+
160
+ Ojas treats agents not merely as software processes, but as evolving cognitive systems whose health must be continuously maintained, optimized, and strengthened.
161
+
162
+ **Ojas is the health layer for autonomous intelligence.**
@@ -0,0 +1,342 @@
1
+ # Backlog
2
+
3
+ Items called out in code review that are **not yet fixed**, with a note on
4
+ why they're deferred and what they'd take to do properly.
5
+
6
+ ## Recently closed
7
+
8
+ Items that previously sat in this backlog are now landed in `src/`:
9
+
10
+ - **Pluggable hallucination classifier (Raksha).** `HallucinationDetector`
11
+ interface + `BestOfNInconsistencyDetector`, `ClaimLevelDetector`,
12
+ `AbstentionDetector`, `EnsembleHallucinationDetector`. Wired into
13
+ `Raksha.detectHallucination()` with Pulse `hallucination_detected`
14
+ emission.
15
+ - **Model routing (Agni).** `ModelRouter` + `ConfidenceRoutingTable`
16
+ with Wilson 95% CI, fail-closed defaults, configurable safety classes.
17
+ - **Response distillation (Agni).** `ResponseDistiller` +
18
+ `defaultResponseDistiller` at three intensities; preserves fenced
19
+ code blocks; tracks lifetime tokens saved.
20
+ - **Memory temperature + cursor delta sync + typed nodes (Nidra).**
21
+ `touchMemory`, `getMemoryTemperature`, `detectColdMemories`,
22
+ `getMemoryCursor`, `getMemoryDelta`, `ConsolidatedMemory.nodeType`.
23
+ - **Tiered loading + omission visibility + adaptive compression (Aahar).**
24
+ `ContextItem.tier`, `AaharFilterOptions.emitOmissionMarker`,
25
+ `recordRetrieval`, `getEffectiveThreshold`. Tier-aware sort precedes
26
+ budget enforcement.
27
+ - **Context-budget milestones + cold-memory events (Pulse).**
28
+ `recordContextBudgetUtilisation` (50/75/90/95% latched per agent)
29
+ and `recordColdMemories` are wired through `Ojas.healthCheck()`.
30
+ - **Handoff + velocity tracking (Chikitsa).** `recordTaskOutcome`,
31
+ `getVelocityStats` (median, p90, tasks/hour), Markdown
32
+ `generateHandoff` for cross-session resume.
33
+ - **Pulse latency percentiles + heartbeat + event subscription.**
34
+ `recordLatency(agentId, taskClass, ms)` with windowed p50/p95/max
35
+ and one-shot `latency_breach` events at a configured SLO budget.
36
+ `heartbeat(agentId)` + `detectStuckAgents(maxIdleMs?)` with one-shot
37
+ `agent_stuck` events. `subscribe(handler)` for push consumers
38
+ (live dashboards, log streamers) that doesn't require polling.
39
+ - **Closed-loop repair (Chikitsa).** `RepairExecutor` /
40
+ `RepairVerifier` pluggable interfaces and `executeProtocol(p, e, v?)`
41
+ which runs execute → verify → optional rollback. Per-protocol-id
42
+ idempotency, full `executionHistory()` audit log, status =
43
+ `verified | applied | unverified | rolled-back | failed |
44
+ already-applied`. Closes detect → diagnose → repair → **verify**.
45
+ - **Lazy / on-demand context (Aahar).** Optional
46
+ `ContextItem.resolveContent: () => string | Promise<string>` plus
47
+ `aahar.materialise(filtered)` so expensive content is resolved
48
+ only after the budget has admitted the item. Rejecting items don't
49
+ pay the resolution cost.
50
+
51
+ - **MCP structured audit logging.** `src/mcp/audit.ts` emits structured
52
+ JSONL to stderr when `OJAS_AUDIT=1`. Covers agent registration, policy
53
+ changes, safe-mode toggles, health checks, quarantine events.
54
+ - **Prompt-injection classifier plugin.** `PromptInjectionClassifier`
55
+ interface + `OnnxPromptInjectionClassifier` (lazy ONNX) +
56
+ `HttpPromptInjectionClassifier` (external API). Scores merged with
57
+ rule-based detection via max probability.
58
+ - **AbortSignal cancellation.** Optional `signal?: AbortSignal` on
59
+ `AgentAdapter.process()`. Vyayam aborts on timeout. `ToolFaultProxy`
60
+ respects abort immediately.
61
+ - **SQLite backup/restore + encryption.** `store.backup()` /
62
+ `store.restore()` + `encryptionKey` option for SQLCipher `PRAGMA key`.
63
+ - **Ablation + flaky-tool benchmarks.** `benchmarks/suites/ablation.ts`
64
+ and `benchmarks/suites/flaky-tool.ts`.
65
+ - **Adversarial corpus expanded to 32 items + 55 noisy docs.**
66
+ - **L3 evidence pipeline.** `benchmarks/l3-runner.ts` +
67
+ `--real-tokenizer` + `--store-transcripts` flags.
68
+ - **Calibration model serialization.** `serializeCalibrationModel` /
69
+ `deserializeCalibrationModel` + `OjasConfig.calibrationModel`.
70
+ - **Integration adapters.** `examples/langchain-adapter.ts`,
71
+ `openai-agents-adapter.ts`, `vercel-ai-adapter.ts`,
72
+ `mcp-client-workflow.ts`.
73
+ - **`crypto.randomUUID()` for all IDs.** `src/util/id.ts` wraps
74
+ `randomUUID()`. All module ID generators migrated.
75
+ - **Readonly getter return types.** All public getters return
76
+ `readonly Readonly<T>[]`.
77
+
78
+ What's still open is below.
79
+
80
+ <a id="trust-roadmap"></a>
81
+ ## Trust roadmap
82
+
83
+ The single biggest open question for this project is *"does it actually
84
+ help an agent?"*. v0.2 ships at evidence level L2 / L2.5 (synthetic
85
+ reproducible benchmarks); the roadmap below moves it toward L3 (realistic
86
+ agent tasks) and L4 (production telemetry). Each phase is independently
87
+ landable.
88
+
89
+ ### Closed in this round — limitations reduced in code
90
+
91
+ | Limitation (was in `docs/KNOWN_FAILURES.md`) | How it was closed | Repro / source |
92
+ |---|---|---|
93
+ | **Raksha bypass: Unicode homoglyphs** | `normalizeForScan` adds NFKC + Cyrillic/Greek homoglyph fold | `src/raksha/index.ts`; `test/raksha.test.ts` |
94
+ | **Raksha bypass: zero-width insertions** | `normalizeForScan` strips `\u200B`-class chars | same |
95
+ | **Raksha bypass: full-width Latin** | `normalizeForScan` applies NFKC compatibility decomposition | same |
96
+ | **Raksha bypass: base64 payloads** | `expandBase64` decodes once, bounded, printable-only, rescans | same |
97
+ | **Raksha bypass: policy laundering** | Added pluggable prompt-injection detector stack with deterministic semantic-intent detector | `src/raksha/prompt-injection-detectors.ts`; `test/prompt-injection-detectors.test.ts` |
98
+ | **Benign control set too small (n=2)** | Expanded to **30 items** across 5 benign categories; `false_positive_rate` metric added to suite 1 | `benchmarks/scenarios/injection-prompts.ts`; `benchmarks/suites/injection.ts` |
99
+ | **Char/4 token estimator is hard-coded** | Introduced `TokenEstimator` interface and `createTiktokenEstimator(...)` adapter (optional `tiktoken` peer dep) | `src/types.ts`, `src/agni/tiktoken-adapter.ts` |
100
+ | **Vyayam `latency_spike` / `tool_failure` are prompt-level** | `ToolFaultProxy<AgentAdapter>` wraps the agent and injects real synthetic latency / 5xx responses | `src/vyayam/tool-fault-proxy.ts` |
101
+ | **Health-score calibration: weights designed, untested** | New calibration suite (9): 500 synthetic instances, Spearman ρ = −0.31, monotonicity holds; aggregate calibration widens observed score range to [0.31, 0.87] | `benchmarks/suites/calibration.ts`; `src/index.ts` |
102
+ | **SQLite persistence and real sessions** | `AgentRegistry` now partitions by `session_id` and persists snapshots when `OJAS_DB_PATH` is set | `src/mcp/registry.ts`; `src/persistence/sqlite.ts`; `test/persistence-sessions.test.ts` |
103
+ | **Seeded PRNG / bootstrap CIs / raw JSONL rows** | `benchmarks/util/prng.ts`, `benchmarks/util/stats.ts`; raw rows written on `--write` | runner + util |
104
+ | **No before/after demo** | `npm run demo:before-after` runs a deterministic side-by-side | `examples/before-after.ts` |
105
+
106
+ ### Phase 3 — calibration + ablation (closed)
107
+
108
+ All items in this phase have been shipped:
109
+
110
+ - ✅ **Ablation matrix**: `benchmarks/suites/ablation.ts` measures each
111
+ module's contribution by disabling raksha / aahar individually.
112
+ - ✅ **Tool-loop benchmark**: `benchmarks/suites/flaky-tool.ts` uses
113
+ `ToolFaultProxy` with non-deterministic fault profiles (intermittent
114
+ 500s, variable latency, connection resets).
115
+ - ✅ **Calibration model serialization**: `src/util/calibration.ts`
116
+ provides `serializeCalibrationModel` / `deserializeCalibrationModel`
117
+ for JSON round-trip. `OjasConfig.calibrationModel` applies empirical
118
+ isotonic calibration in `healthCheck()`.
119
+ - **Memory-poisoning benchmark** with ~100 benign controls and
120
+ explicit false-positive / false-negative rates per axis — *still open*.
121
+
122
+ ### Phase 4 — realism + claim hygiene (mostly closed)
123
+
124
+ - ✅ **Framework adapters**: `examples/langchain-adapter.ts`,
125
+ `openai-agents-adapter.ts`, `vercel-ai-adapter.ts`,
126
+ `mcp-client-workflow.ts`.
127
+ - ✅ **`npm run verify:evidence`**: CI step in
128
+ `benchmarks/verify-evidence.ts`, including L3 freshness checks.
129
+ - ✅ **`AbortSignal` propagation**: optional `signal?: AbortSignal` on
130
+ `AgentAdapter.process()`, backward-compatible. Vyayam creates an
131
+ `AbortController` per iteration and aborts on timeout.
132
+ `ToolFaultProxy` respects abort immediately.
133
+ - **Memory-corruption fault injection**: extend `ToolFaultProxy` (or
134
+ add a sibling) to corrupt the memory store passed via `injectMemory`,
135
+ so Vyayam's `memory_corruption` scenario becomes environmental too —
136
+ *still open*.
137
+
138
+ ### Phase 5 — L3 / L4 (path established)
139
+
140
+ - ✅ **Real-LLM benchmark mode**: `OJAS_BENCH_LLM=1`,
141
+ `OJAS_BENCH_JUDGE=1`, `--real-tokenizer`, `--store-transcripts` flags
142
+ all shipped. `benchmarks/l3-runner.ts` orchestrates a full L3 run.
143
+ - ✅ **L3 evidence verification**: `benchmarks/verify-evidence.ts`
144
+ checks for recent L3 run directories and transcript files.
145
+ - ⬜ **Recurring CI runs**: requires `OPENAI_API_KEY` secret +
146
+ GitHub Actions workflow update. Documented in `l3-runner.ts`.
147
+ - **Production telemetry capture**: hook for users running Ojas in
148
+ anger to opt-in to anonymised aggregate stats (failure detection
149
+ precision / recall, score-vs-incident correlation) — *still open*.
150
+
151
+ ## Reframed as a v0.2 non-goal, not deferred work
152
+
153
+ ### MCP authentication / authorization on the boundary
154
+ Multiple review rounds flagged "no caller authentication" as critical /
155
+ deferred. After scoping discussion this is **not deferred work** in v0.3 —
156
+ it's an intentional **non-goal**. Ojas v0.3 is stdio-only, and stdio MCP
157
+ has no portable per-call identity to authenticate against; the MCP host
158
+ that launches the server *is* the caller, so the security boundary is
159
+ the OS / process boundary that runs it. See:
160
+
161
+ - The trust-model banner and "MCP trust model" section in [`../README.md`](../README.md)
162
+ - ["MCP authentication"](./SECURITY.md#mcp-authentication) and
163
+ ["Security non-goals for v0.3"](./SECURITY.md#security-non-goals-for-v03)
164
+ in `./SECURITY.md`
165
+ - ["Local-only stdio trust boundary"](./MCP.md#trust-boundary) in `./MCP.md`
166
+
167
+ What v0.3 *does* ship as mitigations (none of which are authentication):
168
+ `OJAS_TRUSTED_SINGLE_TENANT`, `OJAS_DISABLE_AUTO_REGISTER`,
169
+ `OJAS_ALLOWED_AGENT_IDS`, `OJAS_MAX_AGENTS`, `OJAS_ALLOW_REPLACE_EXISTING`
170
+ off by default, scope-aware startup warning, classified error envelopes,
171
+ and typed `OjasError` throws at the registry boundary.
172
+
173
+ If a future Ojas version ships a non-stdio transport (HTTP, SSE,
174
+ streamable-HTTP), **that transport must fail closed unless an auth model
175
+ is configured** — that gate is the future work, not auth on stdio.
176
+
177
+ ## Round-4 review — partially addressed in this round
178
+
179
+ ### Stress scenarios partly environmental (round-4 #9)
180
+ Partially closed: `latency_spike` and `tool_failure` now wrap the
181
+ agent with `ToolFaultProxy`, so they inject real synthetic delay /
182
+ failure before the inner `process()` call. `memory_corruption` remains
183
+ prompt-level until a memory-store mutation adapter exists.
184
+
185
+ ### Schema-validation errors remain MCP-protocol-level (round-4 #3)
186
+ `safeHandler` wraps the **handler body**. Zod schema rejection happens
187
+ inside the MCP SDK *before* the handler runs, so those failures surface
188
+ as transport errors rather than `status: 'failed'` envelopes. Documented
189
+ explicitly in [`docs/MCP.md`](./MCP.md#error-semantics) so clients are
190
+ not surprised. Loosening schemas to push validation into handlers would
191
+ weaken the input contract and is not planned.
192
+
193
+ ### HealthScore lacks basis / confidence / coverage (round-4 #10)
194
+ Still deferred to a future major. Default 0.5 and measured 0.5 remain
195
+ indistinguishable to callers.
196
+
197
+ ### ~~IDs still use `Math.random()` (round-4 #11)~~ — CLOSED
198
+ ✅ All ID generators now use `crypto.randomUUID()` via `src/util/id.ts`.
199
+ Only non-ID `Math.random()` usage remains (test fixture data, demo jitter).
200
+
201
+ ### ~~Getter methods return shallow copies (round-4 #12)~~ — CLOSED
202
+ ✅ All public getters (`getEvents`, `getTraces`, `getMemories`,
203
+ `getProtocols`, `getResults`, `getAssessments`, `getHistory`,
204
+ `getHealthHistory`) now return `readonly Readonly<T>[]`. TypeScript
205
+ prevents caller mutation at compile time. Runtime copies already existed
206
+ via spread or `structuredClone`.
207
+
208
+ ### Memory audit prune recommendations and human-review (round-4 #13)
209
+ Round-4 did not change audit behaviour. `audit_basis: 'heuristic'`
210
+ remains advertised, but prune recommendations don't yet escalate to
211
+ `requires_human_review: true`. Worth doing alongside the
212
+ crypto.randomUUID migration when we touch this area again.
213
+
214
+ ## Round-3 review — partially addressed in this round
215
+
216
+ These are tracked here so the next reviewer can tell what was structural
217
+ work vs what was honestly deferred.
218
+
219
+ ### ~~Vyayam timeout still does not cancel agent work (round-3 #7)~~ — CLOSED
220
+ ✅ `AgentAdapter.process()` now accepts optional `signal?: AbortSignal`.
221
+ Vyayam creates an `AbortController` per stress iteration and aborts on
222
+ timeout. `ToolFaultProxy` also respects abort. Backward-compatible
223
+ (optional parameter).
224
+
225
+ ### Stress scenarios are partially environmental (round-3 #8)
226
+ `latency_spike` and `tool_failure` are now environmental through
227
+ `ToolFaultProxy`. `memory_corruption` is still prompt-level because the
228
+ proxy cannot yet mutate the bound agent's memory store.
229
+
230
+ ### HealthScore lacks basis / confidence / coverage (round-3 #13)
231
+ Today every `HealthScore` is `{ value, timestamp, source }`. Adding
232
+ `basis: 'observed' | 'heuristic' | 'default'` + `confidence` is a
233
+ breaking schema change; queued for a future major version. Until
234
+ then, default 0.5 and measured 0.5 are indistinguishable to callers.
235
+
236
+ ### ~~IDs still use `Math.random()` (round-3 #14)~~ — CLOSED
237
+ ✅ See round-4 #11 above.
238
+
239
+ ### ~~Getter methods return shallow copies (round-3 #15)~~ — CLOSED
240
+ ✅ See round-4 #12 above.
241
+
242
+ ### Memory audit still heuristic (round-3 #16)
243
+ Already surfaced via `audit_basis: 'heuristic'` in the response, so
244
+ clients can't claim it's authoritative — but the heuristics themselves
245
+ (prefix-match duplicates, regex conflicts) are still shallow.
246
+
247
+ ## Critical — not architecturally addressed yet
248
+
249
+ ### Stress scenarios are partially synthetic (round-2 review #7)
250
+ `Vyayam` enforces `maxScenarioDurationMs`, so hung agents become failed
251
+ scenarios. `latency_spike` and `tool_failure` now inject real synthetic
252
+ delay / 5xx responses through `ToolFaultProxy`; `memory_corruption`
253
+ remains synthetic prompt/context injection until Ojas can mutate the
254
+ agent's memory store safely.
255
+
256
+ ### ~~Vyayam timeout does not cancel the underlying agent work (round-2 review #6)~~ — CLOSED
257
+ ✅ `AgentAdapter.process()` now accepts optional `signal?: AbortSignal`.
258
+ See round-3 #7 above.
259
+
260
+ ## Critical / High — partially addressed
261
+
262
+ ### ~~Real prompt-injection defense beyond regex (review #1, #5)~~ — CLOSED
263
+ ✅ `PromptInjectionClassifier` plugin interface shipped. Two reference
264
+ implementations: `OnnxPromptInjectionClassifier` (local ONNX) and
265
+ `HttpPromptInjectionClassifier` (external API). Rule-based + classifier
266
+ scores merged (max wins). Adversarial corpus expanded to 32 items
267
+ covering recursive/nested, roleplay, multi-doc, tool-output injection.
268
+
269
+ ### Real stress-scenario simulation (review #14, #31, #32) — mostly closed
270
+ `latency_spike` and `tool_failure` are environmental via `ToolFaultProxy`.
271
+ `AbortSignal` cancellation is shipped. Remaining: `memory_corruption`
272
+ fault injection (needs memory-store mutation adapter).
273
+
274
+ ## Medium
275
+
276
+ ### Server split (review #26, #29) — mostly closed
277
+ `src/mcp/server.ts` was split: tool registrations are now in
278
+ `src/mcp/tools/` (agent, context, memory, output, recovery, report),
279
+ with a shared registrar and audit logger. The server file is now the
280
+ wiring entrypoint only.
281
+
282
+ ### Risk-aware recommendation severity (review #13)
283
+ `Vyayam.recommend()` and `Chikitsa.recommend()` emit `info` for absence-of-
284
+ evidence regardless of agent risk level. For a `critical`-risk agent,
285
+ never-stress-tested should arguably be `critical` itself. Right shape:
286
+ pass the contract risk level down (or compute severity in the MCP wrapper).
287
+ Today the fitness gate (`ojas_is_agent_fit_to_continue`) compensates by
288
+ raising the required score for high/critical tasks.
289
+
290
+ ### Deterministic preview IDs (review #24)
291
+ `Chikitsa.previewDiagnose` and `Nidra.previewRecoveryCycle` generate fresh
292
+ random IDs each call. Preview/commit produce semantically-identical
293
+ protocols and memories, but their IDs change. Acceptable for now; if
294
+ operators want to diff or dedupe preview outputs, switch to deterministic
295
+ hashes of `(failure.type, failure.message, agentId)` for preview IDs.
296
+
297
+ ### Allowed-tools enforcement (review #27)
298
+ Contracts can declare `allowed_tools`, but `ojas_ingest_trace` does not
299
+ validate `data.tool_name` against the list. Needs: at ingestion time, if a
300
+ tool name is present and the contract sets `allowed_tools`, emit a
301
+ `policy_violation` event when the tool is not in the list.
302
+
303
+ ### Health-score confidence + coverage (review #20, #35)
304
+ Every score in `AgentHealthReport.moduleScores` is a number with no
305
+ uncertainty signal. Real ops dashboards want `{ score, confidence, basis:
306
+ 'observed' | 'heuristic' | 'default' }`. Worth adding before publishing
307
+ benchmarks externally.
308
+
309
+ ### Recovery actions are advisory (review #34)
310
+ `run_recovery` apply-mode marks actions like `compress_context`,
311
+ `reset_plan`, `generate_fallback_response` as "applied", but they're
312
+ really instructions for the calling agent to enact. We should rename
313
+ `applied_actions` to `instructions_issued` for those, and only call
314
+ something `applied` when Ojas actually mutated state (e.g. `enter_safe_mode`,
315
+ `freeze_memory_writes` on the registry).
316
+
317
+ ### Memory audit beyond string heuristics (review #33)
318
+ `ojas_audit_memory`'s conflict detection is `/never|always.*never|prefer.*dislike/`
319
+ on memory text, and duplicate detection is a prefix substring match. Needs
320
+ structured memory metadata (categories, applicability, parsed assertions)
321
+ to produce a credible audit.
322
+
323
+ ## Low / Nitpick
324
+
325
+ ### ~~`crypto.randomUUID` for durable IDs (review #37)~~ — CLOSED
326
+ ✅ All module ID generators now use `newId()` from `src/util/id.ts`
327
+ which wraps `crypto.randomUUID()`.
328
+
329
+ ### ~~Deep-clone returns (review #38)~~ — CLOSED
330
+ ✅ All public getters return `readonly Readonly<T>[]`. Runtime copies
331
+ via spread or `structuredClone` already existed; now enforced at
332
+ compile time.
333
+
334
+ ### Rename `ContextItem.freshness` (review #39)
335
+ The field is a Unix-seconds timestamp, not a 0–1 score. Should be
336
+ `createdAtEpochSec` in a future breaking release. Add as alias first,
337
+ then deprecate the old name.
338
+
339
+ ### ~~Vyayam `executeStressTest` timeout (review #15, #31)~~ — CLOSED
340
+ ✅ `maxScenarioDurationMs` timeout with `AbortSignal` cancellation.
341
+ Agents that respect the signal stop immediately; others get a timeout
342
+ result.