4runr-os 2.10.73 → 2.10.74
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/apps/gateway/package-lock.json +103 -88
- package/dist/gateway-observability.d.ts +4 -0
- package/dist/gateway-observability.d.ts.map +1 -1
- package/dist/gateway-observability.js +14 -0
- package/dist/gateway-observability.js.map +1 -1
- package/dist/tui-handlers.js +165 -12
- package/dist/tui-handlers.js.map +1 -1
- package/mk3-tui/binaries/win32-x64/mk3-tui.exe +0 -0
- package/mk3-tui/src/app.rs +54 -0
- package/mk3-tui/src/main.rs +107 -0
- package/mk3-tui/src/ui/layout.rs +12 -0
- package/mk3-tui/src/ui/run_manager.rs +51 -6
- package/package.json +2 -2
- package/scripts/os-tools-smoke.cjs +549 -460
package/mk3-tui/src/main.rs
CHANGED
|
@@ -279,6 +279,58 @@ fn main() -> Result<()> {
|
|
|
279
279
|
app.state.posture_status = posture_str.to_string();
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
|
+
if let Some(sh) =
|
|
283
|
+
obj.get("shield").and_then(|v| v.as_object())
|
|
284
|
+
{
|
|
285
|
+
if let Some(m) =
|
|
286
|
+
sh.get("mode").and_then(|v| v.as_str())
|
|
287
|
+
{
|
|
288
|
+
app.state.shield_mode = m.to_string();
|
|
289
|
+
}
|
|
290
|
+
if let Some(dets) =
|
|
291
|
+
sh.get("detectors").and_then(|v| v.as_array())
|
|
292
|
+
{
|
|
293
|
+
app.state.shield_detectors = dets
|
|
294
|
+
.iter()
|
|
295
|
+
.filter_map(|v| {
|
|
296
|
+
v.as_str().map(|s| s.to_string())
|
|
297
|
+
})
|
|
298
|
+
.collect();
|
|
299
|
+
}
|
|
300
|
+
app.state.shield_blocks_total = sh
|
|
301
|
+
.get("blocksTotal")
|
|
302
|
+
.and_then(|v| v.as_u64())
|
|
303
|
+
.unwrap_or(0);
|
|
304
|
+
app.state.shield_masks_total = sh
|
|
305
|
+
.get("masksTotal")
|
|
306
|
+
.and_then(|v| v.as_u64())
|
|
307
|
+
.unwrap_or(0);
|
|
308
|
+
}
|
|
309
|
+
if let Some(sent) =
|
|
310
|
+
obj.get("sentinel").and_then(|v| v.as_object())
|
|
311
|
+
{
|
|
312
|
+
app.state.sentinel_active_runs = sent
|
|
313
|
+
.get("watchedRuns")
|
|
314
|
+
.and_then(|v| v.as_u64())
|
|
315
|
+
.unwrap_or(0) as usize;
|
|
316
|
+
let enabled = sent
|
|
317
|
+
.get("enabled")
|
|
318
|
+
.and_then(|v| v.as_bool())
|
|
319
|
+
.unwrap_or(false);
|
|
320
|
+
let healthy = sent
|
|
321
|
+
.get("healthy")
|
|
322
|
+
.and_then(|v| v.as_bool())
|
|
323
|
+
.unwrap_or(false);
|
|
324
|
+
app.state.sentinel_state = if !enabled {
|
|
325
|
+
"off".to_string()
|
|
326
|
+
} else if app.state.sentinel_active_runs > 0 {
|
|
327
|
+
"watching".to_string()
|
|
328
|
+
} else if healthy {
|
|
329
|
+
"idle".to_string()
|
|
330
|
+
} else {
|
|
331
|
+
"degraded".to_string()
|
|
332
|
+
};
|
|
333
|
+
}
|
|
282
334
|
app.add_log(format!(
|
|
283
335
|
"✓ [{}] Mode: {}, Posture: {}",
|
|
284
336
|
short_id,
|
|
@@ -288,6 +340,61 @@ fn main() -> Result<()> {
|
|
|
288
340
|
.unwrap_or("?")
|
|
289
341
|
));
|
|
290
342
|
}
|
|
343
|
+
else if obj.get("shieldDemo").and_then(|v| v.as_bool())
|
|
344
|
+
== Some(true)
|
|
345
|
+
{
|
|
346
|
+
let msg = obj
|
|
347
|
+
.get("message")
|
|
348
|
+
.and_then(|v| v.as_str())
|
|
349
|
+
.unwrap_or("Shield demo run started");
|
|
350
|
+
app.add_log(format!("✓ [{}] {}", short_id, msg));
|
|
351
|
+
if let Some(rid) =
|
|
352
|
+
obj.get("runId").and_then(|v| v.as_str())
|
|
353
|
+
{
|
|
354
|
+
app.add_log(format!(
|
|
355
|
+
"[SHIELD] Demo run id: {} — open Run Manager",
|
|
356
|
+
rid
|
|
357
|
+
));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
else if obj.get("shieldProbe").and_then(|v| v.as_bool())
|
|
361
|
+
== Some(true)
|
|
362
|
+
{
|
|
363
|
+
let result = obj.get("result");
|
|
364
|
+
let action = result
|
|
365
|
+
.and_then(|r| r.get("decision"))
|
|
366
|
+
.and_then(|d| d.get("action"))
|
|
367
|
+
.and_then(|a| a.as_str())
|
|
368
|
+
.unwrap_or("?");
|
|
369
|
+
app.add_log(format!(
|
|
370
|
+
"✓ [{}] Shield probe: action={}",
|
|
371
|
+
short_id, action
|
|
372
|
+
));
|
|
373
|
+
}
|
|
374
|
+
else if obj.get("shieldStatus").and_then(|v| v.as_bool())
|
|
375
|
+
== Some(true)
|
|
376
|
+
{
|
|
377
|
+
let metrics = obj.get("metrics").and_then(|v| v.as_object());
|
|
378
|
+
let blocks = metrics
|
|
379
|
+
.and_then(|m| m.get("blocks"))
|
|
380
|
+
.and_then(|v| v.as_u64())
|
|
381
|
+
.unwrap_or(0);
|
|
382
|
+
let masks = metrics
|
|
383
|
+
.and_then(|m| m.get("masks"))
|
|
384
|
+
.and_then(|v| v.as_u64())
|
|
385
|
+
.unwrap_or(0);
|
|
386
|
+
app.state.shield_blocks_total = blocks;
|
|
387
|
+
app.state.shield_masks_total = masks;
|
|
388
|
+
let mode = obj
|
|
389
|
+
.get("health")
|
|
390
|
+
.and_then(|h| h.get("mode"))
|
|
391
|
+
.and_then(|v| v.as_str())
|
|
392
|
+
.unwrap_or("?");
|
|
393
|
+
app.add_log(format!(
|
|
394
|
+
"✓ [{}] Shield {} — {} block(s), {} mask(s)",
|
|
395
|
+
short_id, mode, blocks, masks
|
|
396
|
+
));
|
|
397
|
+
}
|
|
291
398
|
// Handle agent.list response
|
|
292
399
|
else if let Some(agents_data) = obj.get("agents") {
|
|
293
400
|
if let Some(agents_array) = agents_data.as_array() {
|
package/mk3-tui/src/ui/layout.rs
CHANGED
|
@@ -394,6 +394,18 @@ fn render_left_column(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
394
394
|
Style::default().fg(TEXT_DIM),
|
|
395
395
|
),
|
|
396
396
|
]));
|
|
397
|
+
if state.shield_blocks_total > 0 || state.shield_masks_total > 0 {
|
|
398
|
+
lines.push(Line::from(vec![
|
|
399
|
+
Span::raw(" "),
|
|
400
|
+
Span::styled(
|
|
401
|
+
format!(
|
|
402
|
+
"↳ {} block(s) · {} mask(s) (Gateway metrics)",
|
|
403
|
+
state.shield_blocks_total, state.shield_masks_total
|
|
404
|
+
),
|
|
405
|
+
Style::default().fg(TEXT_DIM),
|
|
406
|
+
),
|
|
407
|
+
]));
|
|
408
|
+
}
|
|
397
409
|
|
|
398
410
|
// Sentinel status
|
|
399
411
|
let sentinel_color = match state.sentinel_state.as_str() {
|
|
@@ -67,6 +67,9 @@ pub struct RunInfo {
|
|
|
67
67
|
pub total_cost: Option<f64>,
|
|
68
68
|
pub model: Option<String>,
|
|
69
69
|
pub error_message: Option<String>,
|
|
70
|
+
/// True when Gateway output indicates Shield blocked or masked on the run path.
|
|
71
|
+
pub shield_blocked: bool,
|
|
72
|
+
pub safety_reason: Option<String>,
|
|
70
73
|
pub logs: Vec<String>,
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -213,12 +216,20 @@ pub fn run_info_from_gateway_value(v: &Value) -> Option<RunInfo> {
|
|
|
213
216
|
.collect()
|
|
214
217
|
})
|
|
215
218
|
.unwrap_or_default();
|
|
216
|
-
let
|
|
217
|
-
|
|
218
|
-
.and_then(|out| out.as_object())
|
|
219
|
+
let output_obj = o.get("output").and_then(|out| out.as_object());
|
|
220
|
+
let error_message = output_obj
|
|
219
221
|
.and_then(|oo| oo.get("error"))
|
|
220
222
|
.and_then(|e| e.as_str())
|
|
221
223
|
.map(|s| s.to_string());
|
|
224
|
+
let safety_reason = output_obj
|
|
225
|
+
.and_then(|oo| oo.get("reason"))
|
|
226
|
+
.and_then(|r| r.as_str())
|
|
227
|
+
.map(|s| s.to_string());
|
|
228
|
+
let shield_blocked = error_message
|
|
229
|
+
.as_deref()
|
|
230
|
+
.map(|e| e.to_lowercase().contains("shield"))
|
|
231
|
+
.unwrap_or(false)
|
|
232
|
+
|| logs.iter().any(|l| l.to_lowercase().contains("shield blocked"));
|
|
222
233
|
let model = o
|
|
223
234
|
.get("input")
|
|
224
235
|
.and_then(|i| i.as_object())
|
|
@@ -248,6 +259,8 @@ pub fn run_info_from_gateway_value(v: &Value) -> Option<RunInfo> {
|
|
|
248
259
|
total_cost: None,
|
|
249
260
|
model,
|
|
250
261
|
error_message,
|
|
262
|
+
shield_blocked,
|
|
263
|
+
safety_reason,
|
|
251
264
|
logs,
|
|
252
265
|
})
|
|
253
266
|
}
|
|
@@ -542,7 +555,7 @@ fn render_run_list(f: &mut Frame, area: Rect, state: &RunManagerState) {
|
|
|
542
555
|
let duration_str = run.duration.as_deref().unwrap_or("--");
|
|
543
556
|
|
|
544
557
|
// Build line with colored spans
|
|
545
|
-
let
|
|
558
|
+
let mut spans = vec![
|
|
546
559
|
Span::styled(
|
|
547
560
|
if is_selected { "▶ " } else { " " },
|
|
548
561
|
Style::default().fg(BRAND_PURPLE).bold(),
|
|
@@ -551,6 +564,16 @@ fn render_run_list(f: &mut Frame, area: Rect, state: &RunManagerState) {
|
|
|
551
564
|
format!("{:<10}", run.status.as_str()),
|
|
552
565
|
Style::default().fg(run.status.color()).bold(),
|
|
553
566
|
),
|
|
567
|
+
];
|
|
568
|
+
if run.shield_blocked {
|
|
569
|
+
spans.push(Span::styled(
|
|
570
|
+
" SHIELD ",
|
|
571
|
+
Style::default()
|
|
572
|
+
.fg(Color::Rgb(255, 140, 0))
|
|
573
|
+
.add_modifier(Modifier::BOLD),
|
|
574
|
+
));
|
|
575
|
+
}
|
|
576
|
+
spans.extend([
|
|
554
577
|
Span::styled(" │ ", Style::default().fg(TEXT_DIM)),
|
|
555
578
|
Span::styled(
|
|
556
579
|
format!("{:<20}", run.name),
|
|
@@ -583,6 +606,8 @@ fn render_run_list(f: &mut Frame, area: Rect, state: &RunManagerState) {
|
|
|
583
606
|
),
|
|
584
607
|
]);
|
|
585
608
|
|
|
609
|
+
let line = Line::from(spans);
|
|
610
|
+
|
|
586
611
|
ListItem::new(line)
|
|
587
612
|
})
|
|
588
613
|
.collect();
|
|
@@ -764,8 +789,28 @@ fn render_detail_view(f: &mut Frame, area: Rect, state: &RunManagerState) {
|
|
|
764
789
|
]));
|
|
765
790
|
}
|
|
766
791
|
|
|
767
|
-
|
|
768
|
-
|
|
792
|
+
if run.shield_blocked {
|
|
793
|
+
info_lines.push(Line::from(""));
|
|
794
|
+
info_lines.push(
|
|
795
|
+
Line::from("Safety (Shield):").style(Style::default().fg(Color::Rgb(255, 140, 0)).bold()),
|
|
796
|
+
);
|
|
797
|
+
info_lines.push(Line::from(vec![
|
|
798
|
+
Span::styled(" Action: ", Style::default().fg(TEXT_DIM)),
|
|
799
|
+
Span::styled("BLOCKED", Style::default().fg(Color::Rgb(255, 140, 0)).bold()),
|
|
800
|
+
]));
|
|
801
|
+
if let Some(reason) = &run.safety_reason {
|
|
802
|
+
info_lines.push(Line::from(vec![
|
|
803
|
+
Span::styled(" Reason: ", Style::default().fg(TEXT_DIM)),
|
|
804
|
+
Span::styled(reason, Style::default().fg(TEXT_PRIMARY)),
|
|
805
|
+
]));
|
|
806
|
+
}
|
|
807
|
+
if let Some(error) = &run.error_message {
|
|
808
|
+
info_lines.push(Line::from(vec![
|
|
809
|
+
Span::styled(" Error: ", Style::default().fg(TEXT_DIM)),
|
|
810
|
+
Span::styled(error, Style::default().fg(Color::Rgb(255, 69, 69))),
|
|
811
|
+
]));
|
|
812
|
+
}
|
|
813
|
+
} else if let Some(error) = &run.error_message {
|
|
769
814
|
info_lines.push(Line::from(""));
|
|
770
815
|
info_lines
|
|
771
816
|
.push(Line::from("Error:").style(Style::default().fg(Color::Rgb(255, 69, 69)).bold()));
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "4runr-os",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.74",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.10.
|
|
5
|
+
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.10.74: Shield visible in Run Manager, System Status, monitoring; shield demo/probe/status commands; stronger tools smoke.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"4runr": "dist/index.js",
|