@agentlensai/server 0.7.0 → 0.8.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 (44) hide show
  1. package/dist/db/guardrail-store.d.ts +34 -0
  2. package/dist/db/guardrail-store.d.ts.map +1 -0
  3. package/dist/db/guardrail-store.js +221 -0
  4. package/dist/db/guardrail-store.js.map +1 -0
  5. package/dist/db/migrate.d.ts.map +1 -1
  6. package/dist/db/migrate.js +64 -0
  7. package/dist/db/migrate.js.map +1 -1
  8. package/dist/db/schema.sqlite.d.ts +58 -1
  9. package/dist/db/schema.sqlite.d.ts.map +1 -1
  10. package/dist/db/schema.sqlite.js +6 -0
  11. package/dist/db/schema.sqlite.js.map +1 -1
  12. package/dist/db/sqlite-store.d.ts +12 -0
  13. package/dist/db/sqlite-store.d.ts.map +1 -1
  14. package/dist/db/sqlite-store.js +47 -0
  15. package/dist/db/sqlite-store.js.map +1 -1
  16. package/dist/db/tenant-scoped-store.d.ts +1 -0
  17. package/dist/db/tenant-scoped-store.d.ts.map +1 -1
  18. package/dist/db/tenant-scoped-store.js +3 -0
  19. package/dist/db/tenant-scoped-store.js.map +1 -1
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +20 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/lib/guardrails/actions.d.ts +28 -0
  25. package/dist/lib/guardrails/actions.d.ts.map +1 -0
  26. package/dist/lib/guardrails/actions.js +126 -0
  27. package/dist/lib/guardrails/actions.js.map +1 -0
  28. package/dist/lib/guardrails/conditions.d.ts +13 -0
  29. package/dist/lib/guardrails/conditions.d.ts.map +1 -0
  30. package/dist/lib/guardrails/conditions.js +188 -0
  31. package/dist/lib/guardrails/conditions.js.map +1 -0
  32. package/dist/lib/guardrails/engine.d.ts +24 -0
  33. package/dist/lib/guardrails/engine.d.ts.map +1 -0
  34. package/dist/lib/guardrails/engine.js +122 -0
  35. package/dist/lib/guardrails/engine.js.map +1 -0
  36. package/dist/routes/agents.d.ts +4 -3
  37. package/dist/routes/agents.d.ts.map +1 -1
  38. package/dist/routes/agents.js +37 -12
  39. package/dist/routes/agents.js.map +1 -1
  40. package/dist/routes/guardrails.d.ts +18 -0
  41. package/dist/routes/guardrails.d.ts.map +1 -0
  42. package/dist/routes/guardrails.js +184 -0
  43. package/dist/routes/guardrails.js.map +1 -0
  44. package/package.json +2 -2
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Guardrail Condition Evaluators (v0.8.0 — Story 1.4)
3
+ *
4
+ * Each condition type has an evaluator that queries the store
5
+ * and returns whether the condition is triggered.
6
+ */
7
+ import { DEFAULT_HEALTH_WEIGHTS } from '@agentlensai/core';
8
+ import { HealthComputer } from '../health/computer.js';
9
+ export async function evaluateErrorRateThreshold(store, rule, agentId) {
10
+ const config = rule.conditionConfig;
11
+ const threshold = config.threshold ?? 30;
12
+ const windowMinutes = config.windowMinutes ?? 5;
13
+ const now = new Date();
14
+ const from = new Date(now.getTime() - windowMinutes * 60 * 1000).toISOString();
15
+ const totalCount = await store.countEvents({ agentId, from, to: now.toISOString() });
16
+ if (totalCount === 0) {
17
+ return { triggered: false, currentValue: 0, threshold, message: 'No events in window' };
18
+ }
19
+ // Architecture §3.4: count error, critical severity AND tool_error event types
20
+ const errorCount = await store.countEvents({ agentId, from, to: now.toISOString(), severity: 'error' });
21
+ const criticalCount = await store.countEvents({ agentId, from, to: now.toISOString(), severity: 'critical' });
22
+ const toolErrorCount = await store.countEvents({ agentId, from, to: now.toISOString(), eventType: 'tool_error' });
23
+ // Deduplicate: tool_error events with error/critical severity are already counted above,
24
+ // but store.countEvents filters by a single field, so we take the max of the combined count and totalCount
25
+ const combinedErrors = Math.min(errorCount + criticalCount + toolErrorCount, totalCount);
26
+ const errorRate = (combinedErrors / totalCount) * 100;
27
+ return {
28
+ triggered: errorRate >= threshold,
29
+ currentValue: Math.round(errorRate * 100) / 100,
30
+ threshold,
31
+ message: errorRate >= threshold
32
+ ? `Error rate ${errorRate.toFixed(1)}% exceeds threshold ${threshold}%`
33
+ : `Error rate ${errorRate.toFixed(1)}% within threshold ${threshold}%`,
34
+ };
35
+ }
36
+ export async function evaluateCostLimit(store, rule, agentId, sessionId) {
37
+ const config = rule.conditionConfig;
38
+ const maxCostUsd = config.maxCostUsd ?? 10;
39
+ const scope = config.scope ?? 'daily';
40
+ if (scope === 'session' && sessionId) {
41
+ const session = await store.getSession(sessionId);
42
+ const currentCost = session?.totalCostUsd ?? 0;
43
+ return {
44
+ triggered: currentCost >= maxCostUsd,
45
+ currentValue: Math.round(currentCost * 10000) / 10000,
46
+ threshold: maxCostUsd,
47
+ message: currentCost >= maxCostUsd
48
+ ? `Session cost $${currentCost.toFixed(4)} exceeds limit $${maxCostUsd}`
49
+ : `Session cost $${currentCost.toFixed(4)} within limit $${maxCostUsd}`,
50
+ };
51
+ }
52
+ const todayStart = new Date();
53
+ todayStart.setUTCHours(0, 0, 0, 0);
54
+ const { sessions } = await store.querySessions({ agentId, from: todayStart.toISOString(), limit: 10000 });
55
+ const dailyCost = sessions.reduce((sum, s) => sum + (s.totalCostUsd || 0), 0);
56
+ return {
57
+ triggered: dailyCost >= maxCostUsd,
58
+ currentValue: Math.round(dailyCost * 10000) / 10000,
59
+ threshold: maxCostUsd,
60
+ message: dailyCost >= maxCostUsd
61
+ ? `Daily cost $${dailyCost.toFixed(4)} exceeds limit $${maxCostUsd}`
62
+ : `Daily cost $${dailyCost.toFixed(4)} within limit $${maxCostUsd}`,
63
+ };
64
+ }
65
+ export async function evaluateHealthScoreThreshold(store, rule, agentId) {
66
+ const config = rule.conditionConfig;
67
+ const minScore = config.minScore ?? 50;
68
+ const windowDays = config.windowDays ?? 7;
69
+ try {
70
+ const computer = new HealthComputer(DEFAULT_HEALTH_WEIGHTS);
71
+ const score = await computer.compute(store, agentId, windowDays);
72
+ if (!score) {
73
+ return { triggered: false, currentValue: 0, threshold: minScore, message: 'No sessions found for health computation' };
74
+ }
75
+ return {
76
+ triggered: score.overallScore < minScore,
77
+ currentValue: Math.round(score.overallScore * 100) / 100,
78
+ threshold: minScore,
79
+ message: score.overallScore < minScore
80
+ ? `Health score ${score.overallScore.toFixed(0)} below minimum ${minScore}`
81
+ : `Health score ${score.overallScore.toFixed(0)} above minimum ${minScore}`,
82
+ };
83
+ }
84
+ catch {
85
+ return { triggered: false, currentValue: 0, threshold: minScore, message: 'Failed to compute health score' };
86
+ }
87
+ }
88
+ /**
89
+ * Extract a value from a nested object using dot-notation key path.
90
+ * e.g. getByKeyPath({ a: { b: 3 } }, 'a.b') → 3
91
+ */
92
+ function getByKeyPath(obj, keyPath) {
93
+ const parts = keyPath.split('.');
94
+ let current = obj;
95
+ for (const part of parts) {
96
+ if (current == null || typeof current !== 'object')
97
+ return undefined;
98
+ current = current[part];
99
+ }
100
+ return current;
101
+ }
102
+ function compareMetric(currentValue, operator, targetValue) {
103
+ switch (operator) {
104
+ case 'gt': return currentValue > targetValue;
105
+ case 'gte': return currentValue >= targetValue;
106
+ case 'lt': return currentValue < targetValue;
107
+ case 'lte': return currentValue <= targetValue;
108
+ case 'eq': return currentValue === targetValue;
109
+ default: return false;
110
+ }
111
+ }
112
+ export async function evaluateCustomMetric(store, rule, agentId) {
113
+ const config = rule.conditionConfig;
114
+ const metricKeyPath = config.metricKeyPath;
115
+ const operator = config.operator ?? 'gt';
116
+ const targetValue = config.value ?? 0;
117
+ const windowMinutes = config.windowMinutes ?? 60;
118
+ const now = new Date();
119
+ const from = new Date(now.getTime() - windowMinutes * 60 * 1000).toISOString();
120
+ // Architecture §3.4: Use metricKeyPath to extract values from event metadata
121
+ if (metricKeyPath) {
122
+ const eventsResult = await store.queryEvents({ agentId, from, to: now.toISOString(), limit: 10000 });
123
+ const values = [];
124
+ for (const event of eventsResult.events) {
125
+ const md = (event.metadata ?? {});
126
+ const raw = getByKeyPath(md, metricKeyPath);
127
+ if (typeof raw === 'number')
128
+ values.push(raw);
129
+ }
130
+ // (events returned newest-first by default query order)
131
+ if (values.length === 0) {
132
+ return {
133
+ triggered: false,
134
+ currentValue: 0,
135
+ threshold: targetValue,
136
+ message: `No events with metadata key "${metricKeyPath}" in window`,
137
+ };
138
+ }
139
+ // Use the latest value for comparison (events are returned newest-first, so first element is latest)
140
+ const currentValue = values[0];
141
+ const triggered = compareMetric(currentValue, operator, targetValue);
142
+ return {
143
+ triggered,
144
+ currentValue,
145
+ threshold: targetValue,
146
+ message: triggered
147
+ ? `${metricKeyPath} (${currentValue}) ${operator} ${targetValue} = triggered`
148
+ : `${metricKeyPath} (${currentValue}) ${operator} ${targetValue} = not triggered`,
149
+ };
150
+ }
151
+ // Legacy fallback: metricName-based evaluation for backward compatibility
152
+ const metricName = config.metricName ?? 'event_count';
153
+ let currentValue = 0;
154
+ switch (metricName) {
155
+ case 'event_count':
156
+ currentValue = await store.countEvents({ agentId, from, to: now.toISOString() });
157
+ break;
158
+ case 'error_count':
159
+ currentValue = await store.countEvents({ agentId, from, to: now.toISOString(), severity: 'error' });
160
+ break;
161
+ case 'session_count': {
162
+ const { total } = await store.querySessions({ agentId, from });
163
+ currentValue = total;
164
+ break;
165
+ }
166
+ default:
167
+ return { triggered: false, currentValue: 0, threshold: targetValue, message: `Unknown metric: ${metricName}` };
168
+ }
169
+ const triggered = compareMetric(currentValue, operator, targetValue);
170
+ return {
171
+ triggered,
172
+ currentValue,
173
+ threshold: targetValue,
174
+ message: triggered
175
+ ? `${metricName} (${currentValue}) ${operator} ${targetValue} = triggered`
176
+ : `${metricName} (${currentValue}) ${operator} ${targetValue} = not triggered`,
177
+ };
178
+ }
179
+ export async function evaluateCondition(store, rule, agentId, sessionId) {
180
+ switch (rule.conditionType) {
181
+ case 'error_rate_threshold': return evaluateErrorRateThreshold(store, rule, agentId);
182
+ case 'cost_limit': return evaluateCostLimit(store, rule, agentId, sessionId);
183
+ case 'health_score_threshold': return evaluateHealthScoreThreshold(store, rule, agentId);
184
+ case 'custom_metric': return evaluateCustomMetric(store, rule, agentId);
185
+ default: return { triggered: false, currentValue: 0, threshold: 0, message: `Unknown condition type: ${rule.conditionType}` };
186
+ }
187
+ }
188
+ //# sourceMappingURL=conditions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conditions.js","sourceRoot":"","sources":["../../../src/lib/guardrails/conditions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Guardrail Evaluation Engine (v0.8.0 — Story 1.3)
3
+ *
4
+ * Subscribes to EventBus and evaluates guardrail rules asynchronously.
5
+ * Never blocks the event POST response.
6
+ */
7
+ import type { AgentLensEvent } from '@agentlensai/core';
8
+ import type { IEventStore } from '@agentlensai/core';
9
+ import type { SqliteDb } from '../../db/index.js';
10
+ import { GuardrailStore } from '../../db/guardrail-store.js';
11
+ export declare class GuardrailEngine {
12
+ private store;
13
+ private eventStore;
14
+ private listener;
15
+ private started;
16
+ constructor(eventStore: IEventStore, db: SqliteDb);
17
+ start(): void;
18
+ stop(): void;
19
+ getStore(): GuardrailStore;
20
+ evaluateEvent(event: AgentLensEvent): Promise<void>;
21
+ private evaluateRule;
22
+ private isInCooldown;
23
+ }
24
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/lib/guardrails/engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAiC,MAAM,mBAAmB,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAK7D,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,OAAO,CAAS;gBAEZ,UAAU,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ;IAKjD,KAAK,IAAI,IAAI;IAab,IAAI,IAAI,IAAI;IAQZ,QAAQ,IAAI,cAAc;IAIpB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;YAa3C,YAAY;IA+D1B,OAAO,CAAC,YAAY;CAOrB"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Guardrail Evaluation Engine (v0.8.0 — Story 1.3)
3
+ *
4
+ * Subscribes to EventBus and evaluates guardrail rules asynchronously.
5
+ * Never blocks the event POST response.
6
+ */
7
+ import { ulid } from 'ulid';
8
+ import { GuardrailStore } from '../../db/guardrail-store.js';
9
+ import { evaluateCondition } from './conditions.js';
10
+ import { executeAction } from './actions.js';
11
+ import { eventBus } from '../event-bus.js';
12
+ export class GuardrailEngine {
13
+ store;
14
+ eventStore;
15
+ listener = null;
16
+ started = false;
17
+ constructor(eventStore, db) {
18
+ this.eventStore = eventStore;
19
+ this.store = new GuardrailStore(db);
20
+ }
21
+ start() {
22
+ if (this.started)
23
+ return;
24
+ this.listener = (busEvent) => {
25
+ if (busEvent.type === 'event_ingested') {
26
+ this.evaluateEvent(busEvent.event).catch((err) => {
27
+ console.error('[guardrail-engine] evaluation error:', err instanceof Error ? err.message : err);
28
+ });
29
+ }
30
+ };
31
+ eventBus.on('event_ingested', this.listener);
32
+ this.started = true;
33
+ }
34
+ stop() {
35
+ if (this.listener) {
36
+ eventBus.off('event_ingested', this.listener);
37
+ this.listener = null;
38
+ }
39
+ this.started = false;
40
+ }
41
+ getStore() {
42
+ return this.store;
43
+ }
44
+ async evaluateEvent(event) {
45
+ const rules = this.store.listEnabledRules(event.tenantId, event.agentId);
46
+ if (rules.length === 0)
47
+ return;
48
+ for (const rule of rules) {
49
+ try {
50
+ await this.evaluateRule(rule, event);
51
+ }
52
+ catch (err) {
53
+ console.error(`[guardrail-engine] rule ${rule.id} error:`, err instanceof Error ? err.message : err);
54
+ }
55
+ }
56
+ }
57
+ async evaluateRule(rule, event) {
58
+ const now = new Date();
59
+ // 0. Check enabled (defense-in-depth — listEnabledRules already filters, but verify)
60
+ if (!rule.enabled)
61
+ return;
62
+ // 1. Check cooldown
63
+ if (this.isInCooldown(rule, now))
64
+ return;
65
+ // 2. Evaluate condition
66
+ const conditionResult = await evaluateCondition(this.eventStore, rule, event.agentId, event.sessionId);
67
+ // 3. Update state
68
+ const existingState = this.store.getState(rule.tenantId, rule.id);
69
+ const newState = {
70
+ ruleId: rule.id,
71
+ tenantId: rule.tenantId,
72
+ lastEvaluatedAt: now.toISOString(),
73
+ currentValue: conditionResult.currentValue,
74
+ triggerCount: existingState?.triggerCount ?? 0,
75
+ lastTriggeredAt: existingState?.lastTriggeredAt,
76
+ };
77
+ if (!conditionResult.triggered) {
78
+ this.store.upsertState(newState);
79
+ return;
80
+ }
81
+ // 4. Execute action (or dry-run)
82
+ let actionResult = 'dry_run';
83
+ let actionExecuted = false;
84
+ if (!rule.dryRun) {
85
+ const result = await executeAction(rule, conditionResult, event.agentId);
86
+ actionResult = result.result;
87
+ actionExecuted = result.success;
88
+ }
89
+ // 5. Record trigger history
90
+ this.store.insertTrigger({
91
+ id: ulid(),
92
+ ruleId: rule.id,
93
+ tenantId: rule.tenantId,
94
+ triggeredAt: now.toISOString(),
95
+ conditionValue: conditionResult.currentValue,
96
+ conditionThreshold: conditionResult.threshold,
97
+ actionExecuted,
98
+ actionResult,
99
+ metadata: {
100
+ agentId: event.agentId,
101
+ sessionId: event.sessionId,
102
+ eventId: event.id,
103
+ dryRun: rule.dryRun,
104
+ message: conditionResult.message,
105
+ },
106
+ });
107
+ // 6. Update state
108
+ newState.triggerCount = (existingState?.triggerCount ?? 0) + 1;
109
+ newState.lastTriggeredAt = now.toISOString();
110
+ this.store.upsertState(newState);
111
+ }
112
+ isInCooldown(rule, now) {
113
+ if (rule.cooldownMinutes <= 0)
114
+ return false;
115
+ const state = this.store.getState(rule.tenantId, rule.id);
116
+ if (!state?.lastTriggeredAt)
117
+ return false;
118
+ const lastTriggered = new Date(state.lastTriggeredAt);
119
+ return now.getTime() - lastTriggered.getTime() < rule.cooldownMinutes * 60 * 1000;
120
+ }
121
+ }
122
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/lib/guardrails/engine.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Agent Endpoints (Story 4.7)
2
+ * Agent Endpoints (Story 4.7 + B1 — Story 1.2)
3
3
  *
4
- * GET /api/agents — list all agents (with error rate from sessions)
5
- * GET /api/agents/:id — single agent
4
+ * GET /api/agents — list all agents (with error rate from sessions)
5
+ * GET /api/agents/:id — single agent
6
+ * PUT /api/agents/:id/unpause — unpause an agent (clear paused_at, pause_reason, optionally model_override)
6
7
  */
7
8
  import { Hono } from 'hono';
8
9
  import type { IEventStore } from '@agentlensai/core';
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW;eACX,aAAa;0CAmChD"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW;eACX,aAAa;0CA4DhD"}
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Agent Endpoints (Story 4.7)
2
+ * Agent Endpoints (Story 4.7 + B1 — Story 1.2)
3
3
  *
4
- * GET /api/agents — list all agents (with error rate from sessions)
5
- * GET /api/agents/:id — single agent
4
+ * GET /api/agents — list all agents (with error rate from sessions)
5
+ * GET /api/agents/:id — single agent
6
+ * PUT /api/agents/:id/unpause — unpause an agent (clear paused_at, pause_reason, optionally model_override)
6
7
  */
7
8
  import { Hono } from 'hono';
8
9
  import { getTenantStore } from './tenant-helper.js';
@@ -12,15 +13,39 @@ export function agentsRoutes(store) {
12
13
  app.get('/', async (c) => {
13
14
  const tenantStore = getTenantStore(store, c);
14
15
  const agents = await tenantStore.listAgents();
15
- // Enrich agents with error rate computed from their sessions
16
- const enriched = await Promise.all(agents.map(async (agent) => {
17
- const { sessions } = await tenantStore.querySessions({ agentId: agent.id, limit: 10000 });
18
- const totalErrors = sessions.reduce((sum, s) => sum + s.errorCount, 0);
19
- const totalEvents = sessions.reduce((sum, s) => sum + s.eventCount, 0);
20
- const errorRate = totalEvents > 0 ? totalErrors / totalEvents : 0;
21
- return { ...agent, errorRate };
22
- }));
23
- return c.json({ agents: enriched });
16
+ return c.json({ agents });
17
+ });
18
+ // PUT /api/agents/:id/unpause Unpause an agent (B1 Story 1.2)
19
+ app.put('/:id/unpause', async (c) => {
20
+ const tenantStore = getTenantStore(store, c);
21
+ const id = c.req.param('id');
22
+ // Verify agent exists (tenant-scoped)
23
+ const agent = await tenantStore.getAgent(id);
24
+ if (!agent) {
25
+ return c.json({ error: 'Agent not found', status: 404 }, 404);
26
+ }
27
+ // Parse optional body for clearModelOverride
28
+ let clearModelOverride = false;
29
+ try {
30
+ const body = await c.req.json().catch(() => ({}));
31
+ clearModelOverride = body?.clearModelOverride === true;
32
+ }
33
+ catch {
34
+ // No body or invalid JSON — that's fine
35
+ }
36
+ // Unpause by clearing pause fields
37
+ const updates = {
38
+ id,
39
+ pausedAt: undefined,
40
+ pauseReason: undefined,
41
+ };
42
+ if (clearModelOverride) {
43
+ updates.modelOverride = undefined;
44
+ }
45
+ await tenantStore.upsertAgent(updates);
46
+ // Return the updated agent
47
+ const updated = await tenantStore.getAgent(id);
48
+ return c.json(updated);
24
49
  });
25
50
  // GET /api/agents/:id — single agent
26
51
  app.get('/:id', async (c) => {
@@ -1 +1 @@
1
- {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAgC,CAAC;IAErD,8DAA8D;IAC9D,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,CAAC;QAE9C,6DAA6D;QAC7D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACzB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,WAAW,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1F,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YACvE,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YACvE,MAAM,SAAS,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,OAAO,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,CAAC;QACjC,CAAC,CAAC,CACH,CAAC;QAEF,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,qCAAqC;IACrC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAE7C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAgC,CAAC;IAErD,8DAA8D;IAC9D,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,CAAC;QAE9C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,kEAAkE;IAClE,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE7B,sCAAsC;QACtC,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QAChE,CAAC;QAED,6CAA6C;QAC7C,IAAI,kBAAkB,GAAG,KAAK,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAClD,kBAAkB,GAAG,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;QAC1C,CAAC;QAED,mCAAmC;QACnC,MAAM,OAAO,GAAgE;YAC3E,EAAE;YACF,QAAQ,EAAE,SAAS;YACnB,WAAW,EAAE,SAAS;SACvB,CAAC;QACF,IAAI,kBAAkB,EAAE,CAAC;YACvB,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;QACpC,CAAC;QACD,MAAM,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAEvC,2BAA2B;QAC3B,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC/C,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,qCAAqC;IACrC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAE7C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Guardrail REST API Routes (v0.8.0 — Stories 2.1, 2.2)
3
+ *
4
+ * POST /api/guardrails — Create rule
5
+ * GET /api/guardrails — List rules
6
+ * GET /api/guardrails/:id — Get rule
7
+ * PUT /api/guardrails/:id — Update rule
8
+ * DELETE /api/guardrails/:id — Delete rule
9
+ * GET /api/guardrails/:id/status — Get rule status + recent triggers
10
+ * GET /api/guardrails/history — List trigger history
11
+ */
12
+ import { Hono } from 'hono';
13
+ import type { AuthVariables } from '../middleware/auth.js';
14
+ import type { GuardrailStore } from '../db/guardrail-store.js';
15
+ export declare function guardrailRoutes(guardrailStore: GuardrailStore): Hono<{
16
+ Variables: AuthVariables;
17
+ }, import("hono/types").BlankSchema, "/">;
18
+ //# sourceMappingURL=guardrails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/routes/guardrails.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAsC/D,wBAAgB,eAAe,CAAC,cAAc,EAAE,cAAc;eAC1B,aAAa;0CA2JhD"}