@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,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ojas Pulse — AI Health Telemetry System
|
|
3
|
+
*
|
|
4
|
+
* Tracks cognitive vital signs, degradation signals, and health events.
|
|
5
|
+
*/
|
|
6
|
+
import { HealthEvent, HealthRecommendation, LatencyStats, StuckAgentReport, TelemetryHealth, TelemetryPolicy } from '../types';
|
|
7
|
+
import type { PulseStateSnapshot } from '../persistence/types';
|
|
8
|
+
/** Callback invoked for every emitted `HealthEvent`. Subscriber errors are swallowed. */
|
|
9
|
+
export type HealthEventSubscriber = (event: HealthEvent) => void;
|
|
10
|
+
/** Returned by `Pulse.subscribe()` — call to detach. */
|
|
11
|
+
export type Unsubscribe = () => void;
|
|
12
|
+
/**
|
|
13
|
+
* Standard context-budget milestone thresholds. Emits a structured
|
|
14
|
+
* "you've used 50% / 75% / 90% / 95% of your context window" event
|
|
15
|
+
* the first time each milestone is crossed. Latched per-agent so an
|
|
16
|
+
* agent that just crossed 75% doesn't keep getting paged about it.
|
|
17
|
+
*/
|
|
18
|
+
export declare const CONTEXT_BUDGET_MILESTONES: readonly [0.5, 0.75, 0.9, 0.95];
|
|
19
|
+
export type ContextBudgetMilestone = (typeof CONTEXT_BUDGET_MILESTONES)[number];
|
|
20
|
+
export declare class Pulse {
|
|
21
|
+
private policy;
|
|
22
|
+
private events;
|
|
23
|
+
/**
|
|
24
|
+
* Latch tracking which context-budget milestones have already been
|
|
25
|
+
* emitted for each agent. Resets when the agent's utilisation falls
|
|
26
|
+
* back below the milestone (so the next crossing emits again).
|
|
27
|
+
* Key: agentId. Value: set of milestones currently latched.
|
|
28
|
+
*/
|
|
29
|
+
private budgetMilestonesEmitted;
|
|
30
|
+
/**
|
|
31
|
+
* Latch tracking which memory IDs have already produced a
|
|
32
|
+
* `memory_cold` event. Cleared per-memory by `clearColdMemoryLatch`.
|
|
33
|
+
*/
|
|
34
|
+
private coldMemoryEmitted;
|
|
35
|
+
/**
|
|
36
|
+
* Trailing latency samples per `(agentId, taskClass)` key. The key
|
|
37
|
+
* format is `${agentId}::${taskClass}`. Each entry is an array of
|
|
38
|
+
* millisecond durations capped at `policy.latencyWindowSize`.
|
|
39
|
+
*/
|
|
40
|
+
private latencyWindows;
|
|
41
|
+
/**
|
|
42
|
+
* Latch per `(agentId, taskClass)` for `latency_breach` events.
|
|
43
|
+
* Cleared when the windowed p95 falls back below the policy budget
|
|
44
|
+
* so a sustained breach paged once, not on every sample.
|
|
45
|
+
*/
|
|
46
|
+
private latencyBreachLatched;
|
|
47
|
+
/** Last `heartbeat()` ISO timestamp per agentId. */
|
|
48
|
+
private lastHeartbeatAt;
|
|
49
|
+
/**
|
|
50
|
+
* Per-agent latch for `agent_stuck` event. Set when
|
|
51
|
+
* `detectStuckAgents()` flags the agent as stuck, cleared on the
|
|
52
|
+
* next heartbeat. Prevents continuous paging while the agent
|
|
53
|
+
* remains silent.
|
|
54
|
+
*/
|
|
55
|
+
private stuckLatched;
|
|
56
|
+
/** Active push-subscribers. Errors thrown inside handlers are swallowed. */
|
|
57
|
+
private subscribers;
|
|
58
|
+
constructor(policy?: Partial<TelemetryPolicy>);
|
|
59
|
+
/**
|
|
60
|
+
* Record context-budget utilisation for `agentId` and emit a
|
|
61
|
+
* `context_budget_milestone` event for each newly crossed
|
|
62
|
+
* milestone (50/75/90/95 percent). Idempotent — re-recording the
|
|
63
|
+
* same utilisation never re-emits.
|
|
64
|
+
*
|
|
65
|
+
* Returns the list of milestone fractions that emitted on this call
|
|
66
|
+
* (empty when nothing crossed).
|
|
67
|
+
*/
|
|
68
|
+
recordContextBudgetUtilisation(agentId: string, tokensUsed: number, tokensMax: number): ContextBudgetMilestone[];
|
|
69
|
+
/**
|
|
70
|
+
* Emit a `memory_cold` event for each item in `items` that hasn't
|
|
71
|
+
* already been notified. Each item is `{ id, temperature, abstraction }`,
|
|
72
|
+
* matching the shape produced by `Nidra.detectColdMemories()`. Returns
|
|
73
|
+
* the IDs that actually triggered an emission this call.
|
|
74
|
+
*/
|
|
75
|
+
recordColdMemories(agentId: string, items: {
|
|
76
|
+
id: string;
|
|
77
|
+
temperature: number;
|
|
78
|
+
abstraction: string;
|
|
79
|
+
}[]): string[];
|
|
80
|
+
/** Clear the cold-memory latch for `id` (e.g. after the memory was re-warmed or compacted). */
|
|
81
|
+
clearColdMemoryLatch(id: string): void;
|
|
82
|
+
/**
|
|
83
|
+
* Reject malformed TelemetryPolicy fields. `maxRecentEvents` drives the
|
|
84
|
+
* trailing event window used by `emit` and `assess`; a non-positive or
|
|
85
|
+
* non-finite value would either silently disable retention or crash on
|
|
86
|
+
* `Array.slice(-NaN)`.
|
|
87
|
+
*/
|
|
88
|
+
private validatePolicy;
|
|
89
|
+
emit(event: HealthEvent): void;
|
|
90
|
+
emitMany(events: HealthEvent[]): void;
|
|
91
|
+
assess(): TelemetryHealth;
|
|
92
|
+
recommend(): HealthRecommendation[];
|
|
93
|
+
getEvents(): readonly Readonly<HealthEvent>[];
|
|
94
|
+
exportState(): PulseStateSnapshot;
|
|
95
|
+
importState(snapshot: Partial<PulseStateSnapshot> | undefined): void;
|
|
96
|
+
getPolicy(): TelemetryPolicy;
|
|
97
|
+
updatePolicy(updates: Partial<TelemetryPolicy>): void;
|
|
98
|
+
/**
|
|
99
|
+
* Push-subscribe to every emitted `HealthEvent`. The returned function
|
|
100
|
+
* detaches the subscriber. Handler exceptions are swallowed so a
|
|
101
|
+
* buggy subscriber can never corrupt Pulse state. Useful for live
|
|
102
|
+
* dashboards, log streaming, and external alerting integrations.
|
|
103
|
+
*/
|
|
104
|
+
subscribe(handler: HealthEventSubscriber): Unsubscribe;
|
|
105
|
+
/**
|
|
106
|
+
* Record a single observed task latency. Adds the sample to the
|
|
107
|
+
* trailing per-(agentId, taskClass) window, returns the resulting
|
|
108
|
+
* windowed stats, and \u2014 if `policy.latencyP95BreachMs` is configured
|
|
109
|
+
* and the new windowed p95 exceeds it \u2014 emits a one-shot
|
|
110
|
+
* `latency_breach` event. The breach latch clears when p95 drops
|
|
111
|
+
* back below the budget so a recovered system can re-page on a
|
|
112
|
+
* future breach.
|
|
113
|
+
*
|
|
114
|
+
* Rejects malformed input (non-finite or negative `durationMs`,
|
|
115
|
+
* empty strings) at the boundary so a buggy caller can't poison
|
|
116
|
+
* percentile math.
|
|
117
|
+
*/
|
|
118
|
+
recordLatency(agentId: string, taskClass: string, durationMs: number): LatencyStats;
|
|
119
|
+
/**
|
|
120
|
+
* Windowed latency summary for one `(agentId, taskClass)`. Returns a
|
|
121
|
+
* zero-valued struct when no samples have been recorded yet.
|
|
122
|
+
*/
|
|
123
|
+
getLatencyStats(agentId: string, taskClass: string): LatencyStats;
|
|
124
|
+
private computeLatencyStats;
|
|
125
|
+
/**
|
|
126
|
+
* Stamp `agentId` as alive *now*. Clears the per-agent stuck latch
|
|
127
|
+
* so a subsequent silence can re-fire a fresh `agent_stuck` event.
|
|
128
|
+
*/
|
|
129
|
+
heartbeat(agentId: string): void;
|
|
130
|
+
/** Last heartbeat timestamp for `agentId`, or `undefined` if never seen. */
|
|
131
|
+
getLastHeartbeat(agentId: string): string | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* Identify agents whose last `heartbeat()` is older than the
|
|
134
|
+
* configured `stuckAfterMs` budget. For each newly-stuck agent
|
|
135
|
+
* (latched per agentId), emits an `agent_stuck` event. The latch
|
|
136
|
+
* clears on the next heartbeat from that agent.
|
|
137
|
+
*
|
|
138
|
+
* `maxIdleMs` overrides the policy budget for this call. Agents
|
|
139
|
+
* that have never sent a heartbeat are not reported \u2014 they're not
|
|
140
|
+
* "stuck", they're "unknown".
|
|
141
|
+
*/
|
|
142
|
+
detectStuckAgents(maxIdleMs?: number): StuckAgentReport[];
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pulse/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEL,WAAW,EACX,oBAAoB,EAEpB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,eAAe,EAChB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,yFAAyF;AACzF,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;AACjE,wDAAwD;AACxD,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAcrC;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,iCAAkC,CAAC;AAEzE,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhF,qBAAa,KAAK;IAChB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAAqB;IACnC;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB,CAAkD;IACjF;;;OAGG;IACH,OAAO,CAAC,iBAAiB,CAAqB;IAC9C;;;;OAIG;IACH,OAAO,CAAC,cAAc,CAA+B;IACrD;;;;OAIG;IACH,OAAO,CAAC,oBAAoB,CAAqB;IACjD,oDAAoD;IACpD,OAAO,CAAC,eAAe,CAA6B;IACpD;;;;;OAKG;IACH,OAAO,CAAC,YAAY,CAAqB;IACzC,4EAA4E;IAC5E,OAAO,CAAC,WAAW,CAA+B;gBAEtC,MAAM,GAAE,OAAO,CAAC,eAAe,CAAM;IAIjD;;;;;;;;OAQG;IACH,8BAA8B,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,sBAAsB,EAAE;IAwC3B;;;;;OAKG;IACH,kBAAkB,CAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,GAChE,MAAM,EAAE;IAuBX,+FAA+F;IAC/F,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAItC;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAiCtB,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAkB9B,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI;IAIrC,MAAM,IAAI,eAAe;IAgBzB,SAAS,IAAI,oBAAoB,EAAE;IA6BnC,SAAS,IAAI,SAAS,QAAQ,CAAC,WAAW,CAAC,EAAE;IAI7C,WAAW,IAAI,kBAAkB;IAYjC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,SAAS,GAAG,IAAI;IAapE,SAAS,IAAI,eAAe;IAI5B,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IASrD;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,qBAAqB,GAAG,WAAW;IAatD;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY;IAgDnF;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY;IAMjE,OAAO,CAAC,mBAAmB;IAuB3B;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQhC,4EAA4E;IAC5E,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIrD;;;;;;;;;OASG;IACH,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,gBAAgB,EAAE;CA8B1D"}
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Ojas Pulse — AI Health Telemetry System
|
|
4
|
+
*
|
|
5
|
+
* Tracks cognitive vital signs, degradation signals, and health events.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.Pulse = exports.CONTEXT_BUDGET_MILESTONES = void 0;
|
|
9
|
+
const types_1 = require("../types");
|
|
10
|
+
function now() {
|
|
11
|
+
return new Date().toISOString();
|
|
12
|
+
}
|
|
13
|
+
function clamp(v, min = 0, max = 1) {
|
|
14
|
+
return Math.max(min, Math.min(max, v));
|
|
15
|
+
}
|
|
16
|
+
function healthScore(value, source) {
|
|
17
|
+
return { value: clamp(value), timestamp: now(), source };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Standard context-budget milestone thresholds. Emits a structured
|
|
21
|
+
* "you've used 50% / 75% / 90% / 95% of your context window" event
|
|
22
|
+
* the first time each milestone is crossed. Latched per-agent so an
|
|
23
|
+
* agent that just crossed 75% doesn't keep getting paged about it.
|
|
24
|
+
*/
|
|
25
|
+
exports.CONTEXT_BUDGET_MILESTONES = [0.5, 0.75, 0.9, 0.95];
|
|
26
|
+
class Pulse {
|
|
27
|
+
policy;
|
|
28
|
+
events = [];
|
|
29
|
+
/**
|
|
30
|
+
* Latch tracking which context-budget milestones have already been
|
|
31
|
+
* emitted for each agent. Resets when the agent's utilisation falls
|
|
32
|
+
* back below the milestone (so the next crossing emits again).
|
|
33
|
+
* Key: agentId. Value: set of milestones currently latched.
|
|
34
|
+
*/
|
|
35
|
+
budgetMilestonesEmitted = new Map();
|
|
36
|
+
/**
|
|
37
|
+
* Latch tracking which memory IDs have already produced a
|
|
38
|
+
* `memory_cold` event. Cleared per-memory by `clearColdMemoryLatch`.
|
|
39
|
+
*/
|
|
40
|
+
coldMemoryEmitted = new Set();
|
|
41
|
+
/**
|
|
42
|
+
* Trailing latency samples per `(agentId, taskClass)` key. The key
|
|
43
|
+
* format is `${agentId}::${taskClass}`. Each entry is an array of
|
|
44
|
+
* millisecond durations capped at `policy.latencyWindowSize`.
|
|
45
|
+
*/
|
|
46
|
+
latencyWindows = new Map();
|
|
47
|
+
/**
|
|
48
|
+
* Latch per `(agentId, taskClass)` for `latency_breach` events.
|
|
49
|
+
* Cleared when the windowed p95 falls back below the policy budget
|
|
50
|
+
* so a sustained breach paged once, not on every sample.
|
|
51
|
+
*/
|
|
52
|
+
latencyBreachLatched = new Set();
|
|
53
|
+
/** Last `heartbeat()` ISO timestamp per agentId. */
|
|
54
|
+
lastHeartbeatAt = new Map();
|
|
55
|
+
/**
|
|
56
|
+
* Per-agent latch for `agent_stuck` event. Set when
|
|
57
|
+
* `detectStuckAgents()` flags the agent as stuck, cleared on the
|
|
58
|
+
* next heartbeat. Prevents continuous paging while the agent
|
|
59
|
+
* remains silent.
|
|
60
|
+
*/
|
|
61
|
+
stuckLatched = new Set();
|
|
62
|
+
/** Active push-subscribers. Errors thrown inside handlers are swallowed. */
|
|
63
|
+
subscribers = [];
|
|
64
|
+
constructor(policy = {}) {
|
|
65
|
+
this.policy = this.validatePolicy({ ...types_1.DEFAULT_TELEMETRY_POLICY, ...policy });
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Record context-budget utilisation for `agentId` and emit a
|
|
69
|
+
* `context_budget_milestone` event for each newly crossed
|
|
70
|
+
* milestone (50/75/90/95 percent). Idempotent — re-recording the
|
|
71
|
+
* same utilisation never re-emits.
|
|
72
|
+
*
|
|
73
|
+
* Returns the list of milestone fractions that emitted on this call
|
|
74
|
+
* (empty when nothing crossed).
|
|
75
|
+
*/
|
|
76
|
+
recordContextBudgetUtilisation(agentId, tokensUsed, tokensMax) {
|
|
77
|
+
if (!Number.isFinite(tokensUsed) || !Number.isFinite(tokensMax) || tokensMax <= 0) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const ratio = Math.max(0, tokensUsed / tokensMax);
|
|
81
|
+
const latched = this.budgetMilestonesEmitted.get(agentId) ?? new Set();
|
|
82
|
+
const newlyCrossed = [];
|
|
83
|
+
for (const m of exports.CONTEXT_BUDGET_MILESTONES) {
|
|
84
|
+
if (ratio >= m && !latched.has(m)) {
|
|
85
|
+
latched.add(m);
|
|
86
|
+
newlyCrossed.push(m);
|
|
87
|
+
}
|
|
88
|
+
else if (ratio < m && latched.has(m)) {
|
|
89
|
+
// Fell back below — un-latch so the next crossing can emit again.
|
|
90
|
+
latched.delete(m);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (latched.size > 0)
|
|
94
|
+
this.budgetMilestonesEmitted.set(agentId, latched);
|
|
95
|
+
else
|
|
96
|
+
this.budgetMilestonesEmitted.delete(agentId);
|
|
97
|
+
for (const m of newlyCrossed) {
|
|
98
|
+
const severity = m >= 0.95 ? 'critical' : m >= 0.9 ? 'warning' : 'info';
|
|
99
|
+
this.emit({
|
|
100
|
+
id: `pulse-budget-${agentId}-${Math.round(m * 100)}-${Date.now()}`,
|
|
101
|
+
eventType: 'context_budget_milestone',
|
|
102
|
+
severity,
|
|
103
|
+
timestamp: now(),
|
|
104
|
+
agentId,
|
|
105
|
+
detectedBy: 'pulse',
|
|
106
|
+
details: {
|
|
107
|
+
milestone: m,
|
|
108
|
+
tokensUsed,
|
|
109
|
+
tokensMax,
|
|
110
|
+
utilisation: ratio,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return newlyCrossed;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Emit a `memory_cold` event for each item in `items` that hasn't
|
|
118
|
+
* already been notified. Each item is `{ id, temperature, abstraction }`,
|
|
119
|
+
* matching the shape produced by `Nidra.detectColdMemories()`. Returns
|
|
120
|
+
* the IDs that actually triggered an emission this call.
|
|
121
|
+
*/
|
|
122
|
+
recordColdMemories(agentId, items) {
|
|
123
|
+
const emitted = [];
|
|
124
|
+
for (const c of items) {
|
|
125
|
+
if (this.coldMemoryEmitted.has(c.id))
|
|
126
|
+
continue;
|
|
127
|
+
this.coldMemoryEmitted.add(c.id);
|
|
128
|
+
emitted.push(c.id);
|
|
129
|
+
this.emit({
|
|
130
|
+
id: `pulse-cold-${c.id}-${Date.now()}`,
|
|
131
|
+
eventType: 'memory_cold',
|
|
132
|
+
severity: 'info',
|
|
133
|
+
timestamp: now(),
|
|
134
|
+
agentId,
|
|
135
|
+
detectedBy: 'pulse',
|
|
136
|
+
details: {
|
|
137
|
+
memoryId: c.id,
|
|
138
|
+
temperature: c.temperature,
|
|
139
|
+
abstraction: c.abstraction,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return emitted;
|
|
144
|
+
}
|
|
145
|
+
/** Clear the cold-memory latch for `id` (e.g. after the memory was re-warmed or compacted). */
|
|
146
|
+
clearColdMemoryLatch(id) {
|
|
147
|
+
this.coldMemoryEmitted.delete(id);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Reject malformed TelemetryPolicy fields. `maxRecentEvents` drives the
|
|
151
|
+
* trailing event window used by `emit` and `assess`; a non-positive or
|
|
152
|
+
* non-finite value would either silently disable retention or crash on
|
|
153
|
+
* `Array.slice(-NaN)`.
|
|
154
|
+
*/
|
|
155
|
+
validatePolicy(policy) {
|
|
156
|
+
if (!Number.isInteger(policy.maxRecentEvents) || policy.maxRecentEvents <= 0) {
|
|
157
|
+
throw new Error('Pulse: maxRecentEvents must be a positive integer');
|
|
158
|
+
}
|
|
159
|
+
if (typeof policy.degradationThreshold !== 'number' ||
|
|
160
|
+
!Number.isFinite(policy.degradationThreshold) ||
|
|
161
|
+
policy.degradationThreshold < 0 ||
|
|
162
|
+
policy.degradationThreshold > 1) {
|
|
163
|
+
throw new Error('Pulse: degradationThreshold must be a finite number in [0,1]');
|
|
164
|
+
}
|
|
165
|
+
if (policy.latencyWindowSize !== undefined &&
|
|
166
|
+
(!Number.isInteger(policy.latencyWindowSize) || policy.latencyWindowSize <= 0)) {
|
|
167
|
+
throw new Error('Pulse: latencyWindowSize must be a positive integer if set');
|
|
168
|
+
}
|
|
169
|
+
if (policy.latencyP95BreachMs !== undefined &&
|
|
170
|
+
(!Number.isFinite(policy.latencyP95BreachMs) || policy.latencyP95BreachMs <= 0)) {
|
|
171
|
+
throw new Error('Pulse: latencyP95BreachMs must be a positive finite number if set');
|
|
172
|
+
}
|
|
173
|
+
if (policy.stuckAfterMs !== undefined &&
|
|
174
|
+
(!Number.isFinite(policy.stuckAfterMs) || policy.stuckAfterMs <= 0)) {
|
|
175
|
+
throw new Error('Pulse: stuckAfterMs must be a positive finite number if set');
|
|
176
|
+
}
|
|
177
|
+
return policy;
|
|
178
|
+
}
|
|
179
|
+
emit(event) {
|
|
180
|
+
this.events.push(event);
|
|
181
|
+
if (this.events.length > this.policy.maxRecentEvents) {
|
|
182
|
+
this.events = this.events.slice(-this.policy.maxRecentEvents);
|
|
183
|
+
}
|
|
184
|
+
// Push to subscribers. Errors inside handlers are caught so a buggy
|
|
185
|
+
// subscriber can't corrupt Pulse state mid-emit.
|
|
186
|
+
if (this.subscribers.length > 0) {
|
|
187
|
+
for (const sub of this.subscribers) {
|
|
188
|
+
try {
|
|
189
|
+
sub(event);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Intentional: subscriber errors must not break the emit path.
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
emitMany(events) {
|
|
198
|
+
for (const event of events)
|
|
199
|
+
this.emit(event);
|
|
200
|
+
}
|
|
201
|
+
assess() {
|
|
202
|
+
const recent = this.events.slice(-this.policy.maxRecentEvents);
|
|
203
|
+
const critical = recent.filter((event) => event.severity === 'critical').length;
|
|
204
|
+
const warning = recent.filter((event) => event.severity === 'warning').length;
|
|
205
|
+
const activeAlerts = critical + warning;
|
|
206
|
+
const degradationRisk = clamp((critical * 0.2) + (warning * 0.05));
|
|
207
|
+
return {
|
|
208
|
+
vitalSigns: healthScore(1 - degradationRisk, 'pulse.vitalSigns'),
|
|
209
|
+
degradationRisk: healthScore(1 - degradationRisk, 'pulse.degradationRisk'),
|
|
210
|
+
observabilityCoverage: healthScore(recent.length > 0 ? 1 : 0.4, 'pulse.coverage'),
|
|
211
|
+
eventsEmitted: recent.length,
|
|
212
|
+
activeAlerts,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
recommend() {
|
|
216
|
+
const health = this.assess();
|
|
217
|
+
const recs = [];
|
|
218
|
+
if (health.activeAlerts > 0) {
|
|
219
|
+
recs.push({
|
|
220
|
+
module: 'pulse',
|
|
221
|
+
severity: health.degradationRisk.value < 0.5 ? 'critical' : 'warning',
|
|
222
|
+
message: `${health.activeAlerts} active health alerts detected.`,
|
|
223
|
+
action: 'Inspect recent health events and trigger recovery or repair if needed.',
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (health.observabilityCoverage.value < 0.5) {
|
|
227
|
+
recs.push({
|
|
228
|
+
module: 'pulse',
|
|
229
|
+
severity: 'warning',
|
|
230
|
+
message: 'Telemetry coverage is low; no recent health events are available.',
|
|
231
|
+
action: 'Connect Pulse to runtime traces and module events.',
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (recs.length === 0) {
|
|
235
|
+
recs.push({ module: 'pulse', severity: 'info', message: 'Health telemetry is stable.' });
|
|
236
|
+
}
|
|
237
|
+
return recs;
|
|
238
|
+
}
|
|
239
|
+
getEvents() {
|
|
240
|
+
return this.events.map((e) => ({ ...e, details: { ...e.details } }));
|
|
241
|
+
}
|
|
242
|
+
exportState() {
|
|
243
|
+
return {
|
|
244
|
+
events: [...this.getEvents()],
|
|
245
|
+
budgetMilestonesEmitted: Array.from(this.budgetMilestonesEmitted.entries()).map(([k, v]) => [k, Array.from(v)]),
|
|
246
|
+
coldMemoryEmitted: Array.from(this.coldMemoryEmitted),
|
|
247
|
+
latencyWindows: Array.from(this.latencyWindows.entries()).map(([k, v]) => [k, [...v]]),
|
|
248
|
+
latencyBreachLatched: Array.from(this.latencyBreachLatched),
|
|
249
|
+
lastHeartbeatAt: Array.from(this.lastHeartbeatAt.entries()),
|
|
250
|
+
stuckLatched: Array.from(this.stuckLatched),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
importState(snapshot) {
|
|
254
|
+
if (!snapshot)
|
|
255
|
+
return;
|
|
256
|
+
this.events = (snapshot.events ?? []).map((e) => ({ ...e, details: { ...e.details } }));
|
|
257
|
+
this.budgetMilestonesEmitted = new Map((snapshot.budgetMilestonesEmitted ?? []).map(([k, v]) => [k, new Set(v)]));
|
|
258
|
+
this.coldMemoryEmitted = new Set(snapshot.coldMemoryEmitted ?? []);
|
|
259
|
+
this.latencyWindows = new Map((snapshot.latencyWindows ?? []).map(([k, v]) => [k, [...v]]));
|
|
260
|
+
this.latencyBreachLatched = new Set(snapshot.latencyBreachLatched ?? []);
|
|
261
|
+
this.lastHeartbeatAt = new Map(snapshot.lastHeartbeatAt ?? []);
|
|
262
|
+
this.stuckLatched = new Set(snapshot.stuckLatched ?? []);
|
|
263
|
+
}
|
|
264
|
+
getPolicy() {
|
|
265
|
+
return { ...this.policy };
|
|
266
|
+
}
|
|
267
|
+
updatePolicy(updates) {
|
|
268
|
+
this.policy = this.validatePolicy({ ...this.policy, ...updates });
|
|
269
|
+
if (this.events.length > this.policy.maxRecentEvents) {
|
|
270
|
+
this.events = this.events.slice(-this.policy.maxRecentEvents);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// ── Push subscription ────────────────────────────────────────────────────
|
|
274
|
+
/**
|
|
275
|
+
* Push-subscribe to every emitted `HealthEvent`. The returned function
|
|
276
|
+
* detaches the subscriber. Handler exceptions are swallowed so a
|
|
277
|
+
* buggy subscriber can never corrupt Pulse state. Useful for live
|
|
278
|
+
* dashboards, log streaming, and external alerting integrations.
|
|
279
|
+
*/
|
|
280
|
+
subscribe(handler) {
|
|
281
|
+
if (typeof handler !== 'function') {
|
|
282
|
+
throw new Error('Pulse: subscribe requires a function handler');
|
|
283
|
+
}
|
|
284
|
+
this.subscribers.push(handler);
|
|
285
|
+
return () => {
|
|
286
|
+
const idx = this.subscribers.indexOf(handler);
|
|
287
|
+
if (idx !== -1)
|
|
288
|
+
this.subscribers.splice(idx, 1);
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// ── Latency tracking ─────────────────────────────────────────────────────
|
|
292
|
+
/**
|
|
293
|
+
* Record a single observed task latency. Adds the sample to the
|
|
294
|
+
* trailing per-(agentId, taskClass) window, returns the resulting
|
|
295
|
+
* windowed stats, and \u2014 if `policy.latencyP95BreachMs` is configured
|
|
296
|
+
* and the new windowed p95 exceeds it \u2014 emits a one-shot
|
|
297
|
+
* `latency_breach` event. The breach latch clears when p95 drops
|
|
298
|
+
* back below the budget so a recovered system can re-page on a
|
|
299
|
+
* future breach.
|
|
300
|
+
*
|
|
301
|
+
* Rejects malformed input (non-finite or negative `durationMs`,
|
|
302
|
+
* empty strings) at the boundary so a buggy caller can't poison
|
|
303
|
+
* percentile math.
|
|
304
|
+
*/
|
|
305
|
+
recordLatency(agentId, taskClass, durationMs) {
|
|
306
|
+
if (typeof agentId !== 'string' || agentId.length === 0) {
|
|
307
|
+
throw new Error('Pulse: recordLatency requires a non-empty agentId');
|
|
308
|
+
}
|
|
309
|
+
if (typeof taskClass !== 'string' || taskClass.length === 0) {
|
|
310
|
+
throw new Error('Pulse: recordLatency requires a non-empty taskClass');
|
|
311
|
+
}
|
|
312
|
+
if (typeof durationMs !== 'number' || !Number.isFinite(durationMs) || durationMs < 0) {
|
|
313
|
+
throw new Error('Pulse: recordLatency requires a non-negative finite durationMs');
|
|
314
|
+
}
|
|
315
|
+
const key = `${agentId}::${taskClass}`;
|
|
316
|
+
const cap = this.policy.latencyWindowSize ?? 100;
|
|
317
|
+
let window = this.latencyWindows.get(key);
|
|
318
|
+
if (!window) {
|
|
319
|
+
window = [];
|
|
320
|
+
this.latencyWindows.set(key, window);
|
|
321
|
+
}
|
|
322
|
+
window.push(durationMs);
|
|
323
|
+
if (window.length > cap) {
|
|
324
|
+
window.splice(0, window.length - cap);
|
|
325
|
+
}
|
|
326
|
+
const stats = this.computeLatencyStats(agentId, taskClass, window);
|
|
327
|
+
const budget = this.policy.latencyP95BreachMs;
|
|
328
|
+
if (budget !== undefined) {
|
|
329
|
+
if (stats.p95Ms > budget && !this.latencyBreachLatched.has(key)) {
|
|
330
|
+
this.latencyBreachLatched.add(key);
|
|
331
|
+
this.emit({
|
|
332
|
+
id: `pulse-latency-${key}-${Date.now()}`,
|
|
333
|
+
eventType: 'latency_breach',
|
|
334
|
+
severity: 'warning',
|
|
335
|
+
timestamp: now(),
|
|
336
|
+
agentId,
|
|
337
|
+
detectedBy: 'pulse',
|
|
338
|
+
details: {
|
|
339
|
+
taskClass,
|
|
340
|
+
p95Ms: stats.p95Ms,
|
|
341
|
+
budgetMs: budget,
|
|
342
|
+
samples: stats.samples,
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
else if (stats.p95Ms <= budget && this.latencyBreachLatched.has(key)) {
|
|
347
|
+
this.latencyBreachLatched.delete(key);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return stats;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Windowed latency summary for one `(agentId, taskClass)`. Returns a
|
|
354
|
+
* zero-valued struct when no samples have been recorded yet.
|
|
355
|
+
*/
|
|
356
|
+
getLatencyStats(agentId, taskClass) {
|
|
357
|
+
const key = `${agentId}::${taskClass}`;
|
|
358
|
+
const window = this.latencyWindows.get(key);
|
|
359
|
+
return this.computeLatencyStats(agentId, taskClass, window ?? []);
|
|
360
|
+
}
|
|
361
|
+
computeLatencyStats(agentId, taskClass, samples) {
|
|
362
|
+
if (samples.length === 0) {
|
|
363
|
+
return { agentId, taskClass, samples: 0, meanMs: 0, p50Ms: 0, p95Ms: 0, maxMs: 0 };
|
|
364
|
+
}
|
|
365
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
366
|
+
const sum = sorted.reduce((acc, v) => acc + v, 0);
|
|
367
|
+
return {
|
|
368
|
+
agentId,
|
|
369
|
+
taskClass,
|
|
370
|
+
samples: sorted.length,
|
|
371
|
+
meanMs: sum / sorted.length,
|
|
372
|
+
p50Ms: percentileSample(sorted, 0.5),
|
|
373
|
+
p95Ms: percentileSample(sorted, 0.95),
|
|
374
|
+
maxMs: sorted[sorted.length - 1],
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
// ── Heartbeat / stuck-agent detection ────────────────────────────────────
|
|
378
|
+
/**
|
|
379
|
+
* Stamp `agentId` as alive *now*. Clears the per-agent stuck latch
|
|
380
|
+
* so a subsequent silence can re-fire a fresh `agent_stuck` event.
|
|
381
|
+
*/
|
|
382
|
+
heartbeat(agentId) {
|
|
383
|
+
if (typeof agentId !== 'string' || agentId.length === 0) {
|
|
384
|
+
throw new Error('Pulse: heartbeat requires a non-empty agentId');
|
|
385
|
+
}
|
|
386
|
+
this.lastHeartbeatAt.set(agentId, now());
|
|
387
|
+
this.stuckLatched.delete(agentId);
|
|
388
|
+
}
|
|
389
|
+
/** Last heartbeat timestamp for `agentId`, or `undefined` if never seen. */
|
|
390
|
+
getLastHeartbeat(agentId) {
|
|
391
|
+
return this.lastHeartbeatAt.get(agentId);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Identify agents whose last `heartbeat()` is older than the
|
|
395
|
+
* configured `stuckAfterMs` budget. For each newly-stuck agent
|
|
396
|
+
* (latched per agentId), emits an `agent_stuck` event. The latch
|
|
397
|
+
* clears on the next heartbeat from that agent.
|
|
398
|
+
*
|
|
399
|
+
* `maxIdleMs` overrides the policy budget for this call. Agents
|
|
400
|
+
* that have never sent a heartbeat are not reported \u2014 they're not
|
|
401
|
+
* "stuck", they're "unknown".
|
|
402
|
+
*/
|
|
403
|
+
detectStuckAgents(maxIdleMs) {
|
|
404
|
+
const budget = maxIdleMs ?? this.policy.stuckAfterMs ?? 60_000;
|
|
405
|
+
const nowMs = Date.now();
|
|
406
|
+
const out = [];
|
|
407
|
+
for (const [agentId, ts] of this.lastHeartbeatAt) {
|
|
408
|
+
const idleMs = nowMs - Date.parse(ts);
|
|
409
|
+
// `>=`: at the moment the agent's idle reaches the configured
|
|
410
|
+
// budget it counts as stuck. A budget of 0 therefore means "any
|
|
411
|
+
// agent that has ever sent a heartbeat" — useful for tests.
|
|
412
|
+
if (idleMs < budget)
|
|
413
|
+
continue;
|
|
414
|
+
out.push({ agentId, lastHeartbeatAt: ts, idleMs });
|
|
415
|
+
if (!this.stuckLatched.has(agentId)) {
|
|
416
|
+
this.stuckLatched.add(agentId);
|
|
417
|
+
this.emit({
|
|
418
|
+
id: `pulse-stuck-${agentId}-${nowMs}`,
|
|
419
|
+
eventType: 'agent_stuck',
|
|
420
|
+
severity: idleMs > budget * 4 ? 'critical' : 'warning',
|
|
421
|
+
timestamp: now(),
|
|
422
|
+
agentId,
|
|
423
|
+
detectedBy: 'pulse',
|
|
424
|
+
details: {
|
|
425
|
+
lastHeartbeatAt: ts,
|
|
426
|
+
idleMs,
|
|
427
|
+
budgetMs: budget,
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return out;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
exports.Pulse = Pulse;
|
|
436
|
+
/**
|
|
437
|
+
* Linear-interpolated percentile over a sorted-ascending sample.
|
|
438
|
+
* Kept module-private (sibling helper of `Pulse.computeLatencyStats`)
|
|
439
|
+
* so the dependency footprint stays at zero.
|
|
440
|
+
*/
|
|
441
|
+
function percentileSample(sortedAsc, p) {
|
|
442
|
+
if (sortedAsc.length === 0)
|
|
443
|
+
return 0;
|
|
444
|
+
const q = Math.max(0, Math.min(1, p));
|
|
445
|
+
const idx = q * (sortedAsc.length - 1);
|
|
446
|
+
const lo = Math.floor(idx);
|
|
447
|
+
const hi = Math.ceil(idx);
|
|
448
|
+
if (lo === hi)
|
|
449
|
+
return sortedAsc[lo];
|
|
450
|
+
const w = idx - lo;
|
|
451
|
+
return sortedAsc[lo] * (1 - w) + sortedAsc[hi] * w;
|
|
452
|
+
}
|
|
453
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pulse/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP-backed prompt injection classifier — reference implementation.
|
|
3
|
+
*
|
|
4
|
+
* Calls an external HTTP endpoint that accepts `{ text }` and returns
|
|
5
|
+
* `{ injectionProbability, label }`. Works with any service that follows
|
|
6
|
+
* this contract (e.g. a Python FastAPI wrapping a HuggingFace classifier,
|
|
7
|
+
* the OpenAI moderation endpoint behind a thin adapter, Rebuff, etc.).
|
|
8
|
+
*/
|
|
9
|
+
import type { PromptInjectionClassifier, ClassifierResult } from '../../types';
|
|
10
|
+
export interface HttpClassifierOptions {
|
|
11
|
+
/** Full URL of the classification endpoint. */
|
|
12
|
+
url: string;
|
|
13
|
+
/** Optional bearer token for authenticated endpoints. */
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
/** Request timeout in milliseconds. Default: 5000. */
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
/** Custom headers merged into every request. */
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
export declare class HttpPromptInjectionClassifier implements PromptInjectionClassifier {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
private readonly opts;
|
|
23
|
+
constructor(options: HttpClassifierOptions);
|
|
24
|
+
classify(text: string, signal?: AbortSignal): Promise<ClassifierResult>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=http-classifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-classifier.d.ts","sourceRoot":"","sources":["../../../src/raksha/classifiers/http-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/E,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,qBAAa,6BAA8B,YAAW,yBAAyB;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkH;gBAE3H,OAAO,EAAE,qBAAqB;IAUpC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAyC9E"}
|