feat: plan 16-01

This commit is contained in:
thanhnv
2026-07-06 21:47:38 +09:00
parent 4419cd9eae
commit 8c3c5e8bff
30 changed files with 1646 additions and 66 deletions
@@ -194,13 +194,55 @@ cat > "$TRACE_FILE" <<EOF
}
EOF
printf '{"timestamp":"%s","trace_id":"%s","harness":"H6-agentops","agent":"%s","step":"%s","status":"%s","exit_code":%s,"latency_ms":%s,"retry_count":%s,"input_tokens":%s,"output_tokens":%s,"total_tokens":%s,"cost_estimate":%s,"cost_source":"%s","hallucination_signals":%s,"alerts":%s,"input_hash":"%s","output_hash":"%s"}\n' \
"$START_TS" "$TRACE_ID" "$AGENT_NAME" "$STEP_NAME" "$STATUS" "$EXIT_CODE" "$LATENCY_MS" "$RETRY_COUNT" "$INPUT_TOKENS" "$OUTPUT_TOKENS" "$TOTAL_TOKENS" "$COST_ESTIMATE" "$COST_SOURCE" "$HALLUCINATION_SIGNALS" "$ALERTS_JSON" "$INPUT_HASH" "$OUTPUT_HASH" >> "$METRICS_LOG"
# SEC-05 (M-05): serialize via json.dumps so a crafted AGENT_NAME/STEP_NAME cannot
# inject a forged metrics record, and an empty/non-numeric metric can't emit invalid
# JSON. String fields are quoted safely; numeric fields are coerced (fail-safe 0).
CASAN_AM_HALLU="$HALLUCINATION_SIGNALS" CASAN_AM_ALERTS="$ALERTS_JSON" python - "$METRICS_LOG" \
"$START_TS" "$TRACE_ID" "$AGENT_NAME" "$STEP_NAME" "$STATUS" "$EXIT_CODE" "$LATENCY_MS" \
"$RETRY_COUNT" "$INPUT_TOKENS" "$OUTPUT_TOKENS" "$TOTAL_TOKENS" "$COST_ESTIMATE" "$COST_SOURCE" \
"$INPUT_HASH" "$OUTPUT_HASH" <<'PY'
import json, os, sys
(log, ts, trace_id, agent, step, status, exit_code, latency, retry,
in_tok, out_tok, tot_tok, cost, cost_source, in_hash, out_hash) = sys.argv[1:]
def num(x):
try: return int(x)
except (ValueError, TypeError): pass
try: return float(x)
except (ValueError, TypeError): return 0
def jval(s, default):
try: return json.loads(s)
except (ValueError, TypeError): return default
rec = {"timestamp": ts, "trace_id": trace_id, "harness": "H6-agentops",
"agent": agent, "step": step, "status": status,
"exit_code": num(exit_code), "latency_ms": num(latency), "retry_count": num(retry),
"input_tokens": num(in_tok), "output_tokens": num(out_tok), "total_tokens": num(tot_tok),
"cost_estimate": jval(cost, 0), "cost_source": cost_source,
"hallucination_signals": jval(os.environ.get("CASAN_AM_HALLU"), 0),
"alerts": jval(os.environ.get("CASAN_AM_ALERTS"), []),
"input_hash": in_hash, "output_hash": out_hash}
# Compact separators to match the original printf layout (regex-parsed downstream).
open(log, "a", encoding="utf-8").write(json.dumps(rec, separators=(",", ":")) + "\n")
PY
for alert in "${ALERTS[@]:-}"; do
if [[ -n "$alert" ]]; then
ALERT_JSON="$(printf '{"timestamp":"%s","trace_id":"%s","severity":"WARN","resource":{"service.name":"%s","service.version":"1.0.0"},"body":{"message":"Alert triggered: %s","alert.type":"%s","step.name":"%s"},"attributes":{"latency_ms":%s,"status":"%s"}}' \
"$START_TS" "$TRACE_ID" "$AGENT_NAME" "$alert" "$alert" "$STEP_NAME" "$LATENCY_MS" "$STATUS")"
# SEC-05 (M-05): build the alert record with json.dumps (agent/alert/step raw before).
ALERT_JSON="$(python - "$START_TS" "$TRACE_ID" "$AGENT_NAME" "$alert" "$STEP_NAME" "$LATENCY_MS" "$STATUS" <<'PY'
import json, sys
ts, trace_id, agent, alert, step, latency, status = sys.argv[1:]
def num(x):
try: return int(x)
except (ValueError, TypeError):
try: return float(x)
except (ValueError, TypeError): return 0
print(json.dumps({
"timestamp": ts, "trace_id": trace_id, "severity": "WARN",
"resource": {"service.name": agent, "service.version": "1.0.0"},
"body": {"message": f"Alert triggered: {alert}", "alert.type": alert, "step.name": step},
"attributes": {"latency_ms": num(latency), "status": status},
}, separators=(",", ":")))
PY
)"
printf '%s\n' "$ALERT_JSON" >> "$ALERT_LOG"
# Live dispatch (H6-D1): push to the real alert channel when configured.
# Delivery failure is queued to the dead-letter file by alert-dispatch.sh.