@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.
- package/LICENSE +21 -0
- package/README.md +308 -0
- package/dist/aahar/index.d.ts +179 -0
- package/dist/aahar/index.d.ts.map +1 -0
- package/dist/aahar/index.js +657 -0
- package/dist/aahar/index.js.map +1 -0
- package/dist/aahar/scoring.d.ts +85 -0
- package/dist/aahar/scoring.d.ts.map +1 -0
- package/dist/aahar/scoring.js +268 -0
- package/dist/aahar/scoring.js.map +1 -0
- package/dist/agni/index.d.ts +113 -0
- package/dist/agni/index.d.ts.map +1 -0
- package/dist/agni/index.js +328 -0
- package/dist/agni/index.js.map +1 -0
- package/dist/agni/model-router.d.ts +77 -0
- package/dist/agni/model-router.d.ts.map +1 -0
- package/dist/agni/model-router.js +163 -0
- package/dist/agni/model-router.js.map +1 -0
- package/dist/agni/response-distiller.d.ts +37 -0
- package/dist/agni/response-distiller.d.ts.map +1 -0
- package/dist/agni/response-distiller.js +193 -0
- package/dist/agni/response-distiller.js.map +1 -0
- package/dist/agni/tiktoken-adapter.d.ts +55 -0
- package/dist/agni/tiktoken-adapter.d.ts.map +1 -0
- package/dist/agni/tiktoken-adapter.js +113 -0
- package/dist/agni/tiktoken-adapter.js.map +1 -0
- package/dist/chikitsa/index.d.ts +130 -0
- package/dist/chikitsa/index.d.ts.map +1 -0
- package/dist/chikitsa/index.js +565 -0
- package/dist/chikitsa/index.js.map +1 -0
- package/dist/demo.d.ts +15 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +278 -0
- package/dist/demo.js.map +1 -0
- package/dist/index.d.ts +201 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +588 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/audit.d.ts +39 -0
- package/dist/mcp/audit.d.ts.map +1 -0
- package/dist/mcp/audit.js +73 -0
- package/dist/mcp/audit.js.map +1 -0
- package/dist/mcp/contracts.d.ts +76 -0
- package/dist/mcp/contracts.d.ts.map +1 -0
- package/dist/mcp/contracts.js +44 -0
- package/dist/mcp/contracts.js.map +1 -0
- package/dist/mcp/envelope.d.ts +107 -0
- package/dist/mcp/envelope.d.ts.map +1 -0
- package/dist/mcp/envelope.js +162 -0
- package/dist/mcp/envelope.js.map +1 -0
- package/dist/mcp/registry.d.ts +110 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +258 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/server.d.ts +26 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +107 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/agent.d.ts +4 -0
- package/dist/mcp/tools/agent.d.ts.map +1 -0
- package/dist/mcp/tools/agent.js +300 -0
- package/dist/mcp/tools/agent.js.map +1 -0
- package/dist/mcp/tools/context.d.ts +4 -0
- package/dist/mcp/tools/context.d.ts.map +1 -0
- package/dist/mcp/tools/context.js +261 -0
- package/dist/mcp/tools/context.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +5 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/index.js +20 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/memory.d.ts +4 -0
- package/dist/mcp/tools/memory.d.ts.map +1 -0
- package/dist/mcp/tools/memory.js +220 -0
- package/dist/mcp/tools/memory.js.map +1 -0
- package/dist/mcp/tools/output.d.ts +4 -0
- package/dist/mcp/tools/output.d.ts.map +1 -0
- package/dist/mcp/tools/output.js +206 -0
- package/dist/mcp/tools/output.js.map +1 -0
- package/dist/mcp/tools/recovery.d.ts +4 -0
- package/dist/mcp/tools/recovery.d.ts.map +1 -0
- package/dist/mcp/tools/recovery.js +165 -0
- package/dist/mcp/tools/recovery.js.map +1 -0
- package/dist/mcp/tools/registrar.d.ts +4 -0
- package/dist/mcp/tools/registrar.d.ts.map +1 -0
- package/dist/mcp/tools/registrar.js +17 -0
- package/dist/mcp/tools/registrar.js.map +1 -0
- package/dist/mcp/tools/report.d.ts +4 -0
- package/dist/mcp/tools/report.d.ts.map +1 -0
- package/dist/mcp/tools/report.js +68 -0
- package/dist/mcp/tools/report.js.map +1 -0
- package/dist/mcp/tools/shared.d.ts +37 -0
- package/dist/mcp/tools/shared.d.ts.map +1 -0
- package/dist/mcp/tools/shared.js +214 -0
- package/dist/mcp/tools/shared.js.map +1 -0
- package/dist/mcp/trace.d.ts +47 -0
- package/dist/mcp/trace.d.ts.map +1 -0
- package/dist/mcp/trace.js +216 -0
- package/dist/mcp/trace.js.map +1 -0
- package/dist/nidra/index.d.ts +275 -0
- package/dist/nidra/index.d.ts.map +1 -0
- package/dist/nidra/index.js +889 -0
- package/dist/nidra/index.js.map +1 -0
- package/dist/persistence/migrations.d.ts +10 -0
- package/dist/persistence/migrations.d.ts.map +1 -0
- package/dist/persistence/migrations.js +77 -0
- package/dist/persistence/migrations.js.map +1 -0
- package/dist/persistence/sqlite.d.ts +30 -0
- package/dist/persistence/sqlite.d.ts.map +1 -0
- package/dist/persistence/sqlite.js +209 -0
- package/dist/persistence/sqlite.js.map +1 -0
- package/dist/persistence/types.d.ts +104 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +5 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/pulse/index.d.ts +144 -0
- package/dist/pulse/index.d.ts.map +1 -0
- package/dist/pulse/index.js +453 -0
- package/dist/pulse/index.js.map +1 -0
- package/dist/raksha/classifiers/http-classifier.d.ts +26 -0
- package/dist/raksha/classifiers/http-classifier.d.ts.map +1 -0
- package/dist/raksha/classifiers/http-classifier.js +62 -0
- package/dist/raksha/classifiers/http-classifier.js.map +1 -0
- package/dist/raksha/classifiers/index.d.ts +5 -0
- package/dist/raksha/classifiers/index.d.ts.map +1 -0
- package/dist/raksha/classifiers/index.js +8 -0
- package/dist/raksha/classifiers/index.js.map +1 -0
- package/dist/raksha/classifiers/onnx-classifier.d.ts +41 -0
- package/dist/raksha/classifiers/onnx-classifier.d.ts.map +1 -0
- package/dist/raksha/classifiers/onnx-classifier.js +99 -0
- package/dist/raksha/classifiers/onnx-classifier.js.map +1 -0
- package/dist/raksha/hallucination-detectors.d.ts +106 -0
- package/dist/raksha/hallucination-detectors.d.ts.map +1 -0
- package/dist/raksha/hallucination-detectors.js +327 -0
- package/dist/raksha/hallucination-detectors.js.map +1 -0
- package/dist/raksha/index.d.ts +168 -0
- package/dist/raksha/index.d.ts.map +1 -0
- package/dist/raksha/index.js +597 -0
- package/dist/raksha/index.js.map +1 -0
- package/dist/raksha/prompt-injection-detectors.d.ts +30 -0
- package/dist/raksha/prompt-injection-detectors.d.ts.map +1 -0
- package/dist/raksha/prompt-injection-detectors.js +153 -0
- package/dist/raksha/prompt-injection-detectors.js.map +1 -0
- package/dist/types.d.ts +1115 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +71 -0
- package/dist/types.js.map +1 -0
- package/dist/util/calibration.d.ts +32 -0
- package/dist/util/calibration.d.ts.map +1 -0
- package/dist/util/calibration.js +108 -0
- package/dist/util/calibration.js.map +1 -0
- package/dist/util/id.d.ts +2 -0
- package/dist/util/id.d.ts.map +1 -0
- package/dist/util/id.js +9 -0
- package/dist/util/id.js.map +1 -0
- package/dist/vyayam/index.d.ts +76 -0
- package/dist/vyayam/index.d.ts.map +1 -0
- package/dist/vyayam/index.js +528 -0
- package/dist/vyayam/index.js.map +1 -0
- package/dist/vyayam/tool-fault-proxy.d.ts +95 -0
- package/dist/vyayam/tool-fault-proxy.d.ts.map +1 -0
- package/dist/vyayam/tool-fault-proxy.js +170 -0
- package/dist/vyayam/tool-fault-proxy.js.map +1 -0
- package/docs/ARCHITECTURE.md +162 -0
- package/docs/BACKLOG.md +342 -0
- package/docs/CONFIGURATION.md +305 -0
- package/docs/EVIDENCE.md +232 -0
- package/docs/EVIDENCE_MATRIX.md +293 -0
- package/docs/KNOWN_FAILURES.md +367 -0
- package/docs/MCP.md +614 -0
- package/docs/MODULES.md +368 -0
- package/docs/SECURITY.md +251 -0
- package/docs/TRUST.md +88 -0
- package/docs/assets/ojas-hero.png +0 -0
- 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.**
|
package/docs/BACKLOG.md
ADDED
|
@@ -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.
|