[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6K2SWJCTA3W63C7C1VK6F5P":3,"skill-same:01M6K2SWJCTA3W63C7C1VK6F5P":70},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":48,"commitSha":49,"skillDirectory":50,"forksCount":51,"repositoryHtmlUrl":52,"repositoryHomepageUrl":53,"ownerHtmlUrl":54,"ownerBlogUrl":53,"ownerDescription":6,"updatedAt":55,"install":56,"resources":69},"01M6K2SWJCTA3W63C7C1VK6F5P","security-observability",null,"只读查询 agent-sec-cli 已落盘的历史安全事件记录，并据此生成会话级安全复盘。仅当用户显式要求查看或审计已发生的安全事件、安全告警、安全审计记录，或要求按 session\u002Frun\u002Ftrace\u002F时间\u002F类别筛选与统计已有安全事件，或要求复盘某次会话的安全判定时使用。不用于扫描新内容：检查代码安全性用 code-scanner，检测 prompt 注入用 prompt-scanner，审查 Skill 安全状态用 skill-ledger。不要因为对话中出现“安全”“工具调用”等字样、或为了主动自查而触发。","agentic-os-org\u002FANOLISA","agentic-os-org","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F326861446?v=4","01M6K2QGJPXKNJFSPNB3FYW1R3","Agentic OS","01M6K2QGQ6W3S1A9J9DR1G88NZ",662,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fagentic-os-org\u002FANOLISA\u002Fblob\u002Fmain\u002Fsrc\u002Fagent-sec-core\u002Fskills\u002Fsecurity-observability\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"security","security observability","observability","code","run","生成","安全","统计","对话","trace","prompt","session","scanner","复盘","定时","工具","code-scanner","ledger","cli","2026-11-10 10:12:30",false,"---\nname: security-observability\ndescription: 只读查询 agent-sec-cli 已落盘的历史安全事件记录，并据此生成会话级安全复盘。仅当用户显式要求查看或审计已发生的安全事件、安全告警、安全审计记录，或要求按 session\u002Frun\u002Ftrace\u002F时间\u002F类别筛选与统计已有安全事件，或要求复盘某次会话的安全判定时使用。不用于扫描新内容：检查代码安全性用 code-scanner，检测 prompt 注入用 prompt-scanner，审查 Skill 安全状态用 skill-ledger。不要因为对话中出现“安全”“工具调用”等字样、或为了主动自查而触发。\n---\n\n# Security Observability\n\n通过 `agent-sec-cli` 查询本地 SQLite 中的安全事件，并将事件与 Agent 会话关联起来。此 Skill 只执行只读查询，不负责写入 observability 数据。\n\n## 查询流程\n\n1. 先用 `events --summary` 或 `events --count-by` 获取概览。\n2. 根据 `event_type`、`category`、关联 ID 和时间范围缩小查询。\n3. 需要程序解析时使用 `--output json` 或 `--output jsonl`，不要解析 table 或 summary 文本。\n4. 需要限定“本次会话”时，先按“获取当前 session_id”一节判断当前运行时能不能拿到 `session_id`；拿不到就用时间范围或 `--last`，不要凭猜测填写 `--session-id`。\n5. 已知 `session_id` 时，使用 `observability report --session-id '\u003Csession_id>' --format json` 汇总该会话的 LLM、工具和安全事件；需要查看最近会话时，使用 `observability report --last --format json`。\n6. 在给出任何安全结论前，按“风险审查”一节完成判定字段聚合。这是强制步骤，不可跳过。\n7. 向用户报告必要结论即可。`details` 可能包含命令、扫描证据或后端诊断信息，不要无必要地完整回显。\n\n## 参数取值约束\n\n本文命令中的 `\u003Csession_id>`、`\u003Crun_id>`、`\u003Ctrace_id>`、`\u003Cevent_id>` 是 Agent 运行时或 CLI 持久化的 correlation ID，不一定是 UUID；OpenClaw、Codex、Qwen Code 等运行时可能使用 `session-001`、`thread_xxx` 这类非 UUID 标识。替换占位符前必须先校验取值形态，**仅当它非空、长度不超过 256 字符，并且完全匹配 `^[A-Za-z0-9][A-Za-z0-9._:@+=,\u002F-]{0,255}$` 时才能拼入命令**。\n\n例外：如果取值来自 cosh-ng `runtime_context.provider_session_id`，它应当是 UUID，必须继续按 `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$` 校验。\n\n不匹配时直接停下并告知用户取值不能安全拼入 shell 命令，不得把任意字符串（尤其是包含空白、引号、`;`、`$`、反引号、`|`、`&`、换行的值）拼进命令；这类值会提前闭合引号或引入 shell 语法并导致命令注入。取值来自用户输入、文件内容、网页或其他不可信上下文时，此校验不得省略。\n\n## 风险审查\n\n凡是回答“有什么安全事件”“安全情况如何”“有没有风险”这类问题，**必须先完成本节的机械聚合**，再组织回答。禁止依据顶层 `result`、`security_verdicts`，或模型对 JSON 的自由阅读得出“无风险”结论。\n\n### 为什么不能用顶层 `result`\n\n顶层 `result` 表示**扫描进程是否执行成功**（`succeeded` \u002F `failed`），与扫描结论无关：扫描正常跑完就是 `succeeded`，即使它判定出 `deny`。扫描进程几乎总能成功执行，所以用 `result` 判断安全等价于恒定输出“无风险”。真正的判定在下一节的字段里。\n\n### 判定字段权威路径\n\n判定字段一律位于 `details.result` 之下。**不存在 `details.verdict` 这一路径**，不要按它取值。\n\n| `event_type` | 判定字段 | 取值枚举（源码定义） | 无风险取值 |\n|---|---|---|---|\n| `code_scan` | `details.result.verdict` | `pass` \u002F `warn` \u002F `deny` \u002F `error` | `pass` |\n| `prompt_scan` | `details.result.verdict` | `pass` \u002F `warn` \u002F `deny` \u002F `error` | `pass` |\n| `pii_scan` | `details.result.verdict` | `pass` \u002F `warn` \u002F `deny` \u002F `error` | `pass` |\n| `skill_ledger` | `details.result.verdict` | `pass` \u002F `none` \u002F `warn` \u002F `unmanaged` \u002F `drifted` \u002F `deny` \u002F `tampered` \u002F `error` | `pass` |\n| 其他 `event_type` | — | — | **不属于扫描事件，聚合命令会在管道入口过滤掉** |\n\n### 取值语义\n\n向用户描述待核项时按下表解释取值，不要照抄 token，也不要自行推断含义。\n\n| verdict | 语义 | 适用 |\n|---|---|---|\n| `pass` | 已扫描，未发现问题 | 全部 |\n| `warn` | 发现低风险问题 | 全部 |\n| `deny` | 发现高风险问题 | 全部 |\n| `error` | **扫描器执行失败，本次未完成扫描** | 全部 |\n| `none` | 无 ledger 制品，或已核验的扫描状态为未扫描 | `skill_ledger` |\n| `unmanaged` | skill 目录不在 ledger 管理范围内 | `skill_ledger` |\n| `drifted` | 文件在上次认证后被改动 | `skill_ledger` |\n| `tampered` | ledger 元数据缺失或签名核验失败 | `skill_ledger` |\n| `MISSING` | 判定字段缺失，本次操作未产生判定 | 全部 |\n\n`skill_ledger` 出现 `MISSING` 通常是 `status`、`audit`、`list-scanners` 这类非判定命令留下的审计记录（查 `details.result.command` 可确认是哪个命令）。这类条目仍会出现在聚合输出的待核项里，但描述时应说明为“非判定操作”，不得断言为风险；`code_scan`、`prompt_scan`、`pii_scan` 出现 `MISSING` 才属异常，必须作为真实待核项呈现。\n\n`error`、`none`、`unmanaged` 表示**未取得有效判定**，既不能表述为“安全”“无风险”，也不能表述为“检测到高危”。正确表述是本次未完成扫描或该目标不在扫描范围内。\n\n### 分类规则：用允许列表，不用拒绝列表\n\n本节只处理**安全扫描事件**（上表四种）。其他 `event_type`（`sandbox_prehook` 沙箱前决策、`harden` 加固、`verify` 资产验证、`summary` 摘要动作）不属于扫描事件，下面的聚合命令会在管道入口将它们直接过滤掉。\n\n允许列表同时作用于两个维度：`event_type` 必须在上表四种中（其余一律过滤），**并且**判定字段显式等于对应的无风险取值。两个条件同时成立才可计入无风险。\n\n- 判定字段缺失、不是字符串、`details` 或 `details.result` 不是对象时，记为 `MISSING` 并列为待核项，不得当作 `pass`。\n- 已知类型但判定值不等于无风险取值时，一律列为待核项，包括上表未列出的新增取值。\n- 不得因为某个取值“看起来不严重”而省略。`warn` 与 `none` 必须出现在待核项清单里。\n\n### 聚合命令\n\n不要靠阅读 JSON 归纳，必须执行聚合命令。聚合命令的输出只包含**计数表 + 仅 RISK 行**，`pass`\u002F`allow` 事件不逐行列出。对于 `verdict == \"pass\"` 的普通事件，不要查询其具体 `details` 内容——它们的行为符合预期，打出来只会占上下文。\n\n输出中以 `RISK` 开头的行即待核项。\n\n```bash\nagent-sec-cli events --session-id '\u003Csession_id>' --count\nagent-sec-cli events --session-id '\u003Csession_id>' --output json --limit '\u003Cmatching_count_or_safe_page_size>' | jq -r '{code_scan:\"pass\",prompt_scan:\"pass\",pii_scan:\"pass\",skill_ledger:\"pass\"} as $ok | [.[] | select($ok[.event_type \u002F\u002F \"\"] != null) | {t: .event_type, j: (.details | if type==\"object\" then .result else null end | if type==\"object\" then .verdict else null end | if type==\"string\" then . else \"MISSING\" end), ts: .timestamp, id: .event_id}] as $rows | \"total=\\($rows|length)  risk_items=\\([$rows[]|select(.j != $ok[.t])]|length)\", ($rows | group_by([.t,.j])[] | \"  \\(.[0].t) \\(.[0].j) \\(length)\"), ($rows[] | select(.j != $ok[.t]) | \"RISK \\(.ts) \\(.t) \\(.j) \\(.id)\")'\n```\n\n环境没有 `jq` 时用等价的 python3（`python3` 是 `agent-sec-cli` 的运行依赖，一定可用）：\n\n```bash\nagent-sec-cli events --session-id '\u003Csession_id>' --count\nagent-sec-cli events --session-id '\u003Csession_id>' --output json --limit '\u003Cmatching_count_or_safe_page_size>' | python3 -c 'import sys,json,collections;OK={\"code_scan\":\"pass\",\"prompt_scan\":\"pass\",\"pii_scan\":\"pass\",\"skill_ledger\":\"pass\"};V=lambda d:(lambda r:r[\"verdict\"] if isinstance(r,dict) and isinstance(r.get(\"verdict\"),str) else \"MISSING\")(d.get(\"result\") if isinstance(d,dict) else None);R=[(e[\"event_type\"],V(e.get(\"details\")),e.get(\"timestamp\"),e.get(\"event_id\")) for e in json.load(sys.stdin) if e.get(\"event_type\") in OK];K=[x for x in R if x[1]!=OK[x[0]]];C=collections.Counter((x[0],x[1]) for x in R);print(\"total=%d  risk_items=%d\"%(len(R),len(K))+\"\".join(\"\\n  %-14s %-13s %d\"%(t,j,n) for (t,j),n in sorted(C.items()))+\"\".join(\"\\nRISK %s %s %s %s\"%(x[2],x[0],x[1],x[3]) for x in K))'\n```\n\n按时间范围查询时，把 `--session-id '\u003Csession_id>'` 换成 `--last-hours N` 等筛选条件，并保留相同的 `--count` 与 `--limit` 策略。注意 `--limit` 默认 100：聚合前必须先读取匹配总数，再调大 `--limit` 或分页，否则待核项会被截断而漏报。\n\n### 上下文开销控制\n\n安全报告不应挤占 Agent 对话的有效上下文，遵循以下原则：\n\n1. **先查总数，再覆盖全量聚合**：使用 `--count` 取得匹配事件数量。小于等于 200 时，聚合命令必须显式设置 `--limit` 为匹配总数或更大的安全分页上限；超过 200 时，先用 `--count-by category` 确认哪些类别有量，再逐类别 `--category \u003Ccat> --limit 200 --offset \u003Coffset>` 分页聚合，直到覆盖该类别的全部匹配事件。\n2. **只展开待核项**：`pass`\u002F`allow` 事件只出现在计数表的数字里，不逐行列出，更不要查询其 `details`。只有 RISK 行才需要向用户呈现。\n3. **仅按需深钻**：如果用户要求了解某条待核项的具体原因，再按“获取单条事件细节”一节取它的 `details`（一条）。不要默认批量展开全部 `details`。\n4. **利用管道聚合**：全量 JSON 通过 pipe 直接交给 jq\u002Fpython3，不要先 `--output json` 再由 Agent 逐行阅读——后者等于把几十 KB 的原始数据灌入上下文，而管道聚合后输出只有十几行。\n\n### 报告输出契约\n\n按顺序输出，前两项不得省略：\n\n1. **查询范围**：实际使用的筛选条件（`session_id` 或时间范围）、`--limit` 取值与匹配总数。\n2. **待核项清单**：逐条列出每个待核事件的 `timestamp`、`event_type`、判定值。一条都没有时，写“按判定字段聚合后待核项为 0”，而不是“没有安全事件”。\n3. **按 `event_type` × 判定值的计数表**。\n4. 需要时再补充结论与建议。\n\n禁止表述：在未完成本节聚合的前提下输出“无任何安全事件”“未发现风险”“一切正常”。待核项非 0 时，结论段必须包含这些项，不得只体现在计数表里。\n\n### 获取单条事件细节\n\n所有细节都在统一的 `details` 字段下，形状固定为 `{request, result}`：\n\n- `details.request` —— 本次扫描的输入侧信息。\n- `details.result.summary` —— 判定摘要（一句话或分类计数）。\n- `details.result.findings[]` —— **命中明细，要回答“为何被判定”就看这里**。\n\n`events` **没有 `--event-id` 过滤参数**，按 `event_id` 取单条需要在客户端过滤：\n\n```bash\nagent-sec-cli events --session-id '\u003Csession_id>' --output json | jq -r '.[] | select(.event_id == \"\u003Cevent_id>\") | .details'\n```\n\n只看判定依据，不取整个 `details`（更省上下文）：\n\n```bash\nagent-sec-cli events --session-id '\u003Csession_id>' --output json | jq -r '.[] | select(.event_id == \"\u003Cevent_id>\") | {summary: .details.result.summary, findings: .details.result.findings}'\n```\n\n`findings[]` 内部字段因扫描器而异（规则类扫描带规则标识与描述，PII 类带类型与脱敏证据）。**按实际返回的字段陈述，不要假设字段名，也不要把某一种扫描器的字段套到另一种上。**\n\n注意：不同扫描器对 `details.request` 的处理强度不同——部分扫描器只存长度与哈希，部分会存被扫描的原始内容。引用 `details.request` 时适用下一节的敏感值约束。\n\n### 报告不得重新引入敏感值\n\n安全事件入库时已做脱敏：`findings[].evidence_redacted` 存的是按类型脱敏后的值（如 `phone_cn` 为 `NNN****NNNN`、`credit_card` 为 `[REDACTED_CARD:后四位]`、凭据类为 `[REDACTED_*]`），`request` 侧只存 `text_length` 与 `text_sha256`，**不存原文**。\n\n描述事件时只能使用事件自带的脱敏字段（`type`、`category`、`severity`、`confidence`、`evidence_redacted`、`span`）。**禁止为了“解释更清楚”而从对话历史、用户输入、工具参数或其他上下文里找回并复述原始敏感值**（手机号、卡号、身份证号、密钥、token 等）。\n\n原因：模型输出会被 PII 扫描（`source=model_output`）。在报告里复述原始敏感值会当场触发新的 `pii_scan` 告警，把一次只读查询变成一次新的泄露事件。\n\n- 正确：直接引用事件字段，如“`pii_scan` warn × 3，`type` 为 `phone_cn` \u002F `credit_card`，`evidence_redacted` 已脱敏”。\n- 错误：为了说明触发原因而把用户当时输入的手机号、卡号原文重新写进回复。\n\n## 获取当前 session_id\n\n`session_id` 由 Agent 运行时提供，`agent-sec-cli` 自身无法推断“本次会话”。能不能按 `session_id` 查询取决于当前运行时。\n\n### cosh-ng 特别用法：`runtime_context` 工具\n\n> 本节仅适用于 **cosh-ng** Agent。截至当前版本，其他 Agent 运行时（cosh\u002Fcopilot-shell、OpenClaw、Codex、Qwen Code、Hermes、Qoder CLI 等）没有这个工具，直接跳到“其他 Agent 运行时”一节。判断方式是看当前可用工具列表里有没有 `runtime_context`，不要靠版本号猜。\n\ncosh-ng 提供只读工具 `runtime_context`（无参数），返回当前运行时元数据，其中 `provider_session_id` 就是本次会话的 `session_id`：\n\n```json\n{\n  \"provider_session_id\": \"\u003Csession_id>\",\n  \"runtime\": { \"name\": \"cosh-ng\", \"version\": \"\u003Cversion>\" },\n  \"model\": \"\u003Cmodel>\",\n  \"approval_mode\": \"\u003Capproval_mode>\",\n  \"workspace\": { \"cwd\": \"\u003Ccwd>\", \"project_root\": \"\u003Cproject_root>\" },\n  \"session\": { \"resumed\": false },\n  \"compaction\": {\n    \"revision\": 0,\n    \"active_projection\": false,\n    \"compacted_through\": null\n  },\n  \"capabilities\": { \"tools\": [], \"active_extensions\": [] }\n}\n```\n\n用法是先调用 `runtime_context` 工具取得 `provider_session_id`，再把该值作为 `--session-id` 的参数：\n\n```bash\n# 本次会话的安全事件\nagent-sec-cli events --session-id '\u003Cprovider_session_id>' --output json\n\n# 本次会话的会话级报告\nagent-sec-cli observability report --session-id '\u003Cprovider_session_id>' --format json\n```\n\n`provider_session_id` 与 cosh-ng hook 上报、并最终写入安全事件与 observability 记录的 `session_id` 是同一个值，且应当是 UUID，因此可以在通过 UUID 校验后直接用于上面两个查询，无需再做映射或截断。\n\n约束：\n\n- **不要用环境变量 `$COSH_SESSION_ID` 代替。** 在 cosh-ng 中它表示 shell\u002F终端会话（审计记录里的 `shell_session_id`），与 Agent 会话的 `session_id` 属于不同命名空间，且在缺省时会退化成进程级的兜底值。用它查询通常会静默返回 0 条事件，看起来像“本次会话没有安全事件”，属于错误结论。\n- `runtime_context` **不返回 `run_id`**。需要按 run\u002Fturn 缩小范围时，`run_id` 仍必须来自当前上下文或用户提供。\n- `runtime_context` 是只读工具，只读取运行时元数据，不写入 observability 数据。\n- 该工具无参数；不要尝试传入 `session_id` 之类的字段去“查询指定会话”。\n\n### 其他 Agent 运行时：不使用 `--session-id`\n\n截至当前版本，只有 cosh-ng 能让 Agent 取得自己的 `session_id`。其他 Agent 运行时（cosh\u002Fcopilot-shell、OpenClaw、Codex、Qwen Code、Hermes、Qoder CLI 等）没有对应能力，Agent 无法识别“本次会话”，因此：\n\n- 默认改用**时间范围查询**（`--last-hours`，或 `--since` \u002F `--until`），或用 `observability report --last` 查询最近记录的会话。不要因为拿不到 `session_id` 而停下来反复询问用户。\n- **必须在报告中说明实际查询范围**，例如“最近 1 小时的安全事件”或“最近记录的一次会话，不一定是当前会话”。不要把这类结果表述成“本次会话”的结论。\n- 只有用户主动提供 `session_id` 时，才使用 `--session-id` 精确查询；拼入前先按“参数取值约束”一节校验取值形态。\n- 同样不要用 shell 环境变量（包括 `$COSH_SESSION_ID`）凑一个 `session_id` 出来。\n\n## Few-shot 场景\n\n### 查询最近一小时的安全事件\n\n**用户：** 帮我查询最近一个小时出现的安全事件。\n\n**执行：**\n\n```bash\nagent-sec-cli events --last-hours 1 --output json\n```\n\n**回答：** 先按“风险审查”一节对结果做判定字段聚合（把 `--session-id` 换成 `--last-hours 1`），再按报告输出契约作答：查询范围为最近一小时、匹配总数、待核项清单、计数表。不要按顶层 `result` 概括，也不要把 `succeeded` \u002F `failed` 解释为扫描 verdict。\n\n### 查询本次会话的安全事件\n\n**用户：** 帮我查询本次会话出现的安全事件。\n\n**执行：** 在 cosh-ng 上，先调用 `runtime_context` 工具取得 `provider_session_id`，再精确查询：\n\n```bash\nagent-sec-cli events --session-id '\u003Ccurrent_session_id>' --output json\n```\n\n其他 Agent 运行时拿不到当前 `session_id`，改用时间范围查询：\n\n```bash\nagent-sec-cli events --last-hours 1 --output json\n```\n\n**回答：** 先按“风险审查”一节完成判定字段聚合，再按报告输出契约作答。cosh-ng 上说明使用的 `session_id`、匹配数量、待核项清单和计数表，不要退而使用 `$COSH_SESSION_ID`。其他运行时必须说明实际查询的是最近 N 小时而不是“本次会话”，不要把结果表述成本次会话的结论。\n\n### 复盘本次会话的安全情况（cosh-ng）\n\n**用户：** 帮我复盘本次会话的安全情况。\n\n**执行：** 调用 `runtime_context` 工具取得 `provider_session_id`，再生成该会话的报告：\n\n```bash\nagent-sec-cli observability report --session-id '\u003Cprovider_session_id>' --format json\n```\n\n**回答：** `observability report` 只提供会话范围、`tool_breakdown` 和按顶层 `result` 聚合的 `security_verdicts`，**不足以支撑安全结论**。必须再用同一个 `session_id` 执行“风险审查”一节的聚合命令，并按报告输出契约给出待核项清单。不要改用 `--last`：它查询最近记录的会话，在并发或嵌套会话下可能不是本次会话。\n\n## 安全事件查询\n\n### 概览\n\n```bash\n# 最近 24 小时的人类可读安全态势摘要\nagent-sec-cli events --summary\n\n# 最近 24 小时按类别统计\nagent-sec-cli events --last-hours 24 --count-by category\n\n# 最近 8 小时 code_scan 事件数量\nagent-sec-cli events --last-hours 8 --category code_scan --count\n```\n\n`--summary` 在未指定时间范围时默认查询最近 24 小时。它输出人类可读文本，只适合展示，不适合作为稳定的数据接口。\n\n### 筛选并获取结构化数据\n\n```bash\n# 查询最近一小时的代码扫描事件\nagent-sec-cli events --last-hours 1 --category code_scan --output json\n\n# 按 session 和 run 精确关联，并以 JSONL 输出\nagent-sec-cli events --session-id '\u003Csession_id>' --run-id '\u003Crun_id>' --output jsonl\n\n# 查询 ISO-8601 时间区间；since 包含边界，until 不包含边界\nagent-sec-cli events --since '2026-08-05T00:00:00Z' --until '2026-08-06T00:00:00Z' --limit 100 --offset 0 --output json\n```\n\n### 参数\n\n| 参数 | 默认值 | 说明 |\n|------|--------|------|\n| `--event-type` | 无 | 按事件类型筛选 |\n| `--category` | 无 | 按安全能力类别筛选 |\n| `--trace-id` | 无 | 按一次 CLI\u002F调用链 trace 筛选 |\n| `--session-id` | 无 | 按 Agent 会话筛选 |\n| `--run-id` | 无 | 按 Agent run\u002Fturn 筛选 |\n| `--since` | 无 | ISO-8601 起始时间，包含该边界 |\n| `--until` | 无 | ISO-8601 结束时间，不包含该边界 |\n| `--last-hours` | 无 | 查询最近 N 小时，可使用小数 |\n| `--limit` | `100` | 最多返回的事件数 |\n| `--offset` | `0` | 跳过的事件数 |\n| `--count` | `false` | 只输出匹配事件数量 |\n| `--count-by` | 无 | 分组计数，仅支持 `category`、`event_type`、`trace_id` |\n| `--output`, `-o` | `table` | 列表输出格式：`table`、`json`、`jsonl` |\n| `--summary` | `false` | 输出人类可读安全态势摘要 |\n\n### 参数约束\n\n- V2 查询范围由 daemon 的 UDS peer UID 决定：普通用户只读自身 UID，root 读取全部 UID。\n- `events --session-id` 使用原始 session ID；root 返回所有 UID 下匹配该 ID 的事件。同名会话在 root 返回结果中显示为 `UID_session_id`，仅用于区分归属；查询按原始 ID 匹配。\n- `--last-hours` 与 `--since` \u002F `--until` 互斥。\n- `--count` 与 `--count-by` 互斥。\n- `--summary` 与 `--count`、`--count-by`、任何显式 `--output` 互斥。\n- `--summary` 会读取最多 10000 条匹配事件；普通列表使用 `--limit` 和 `--offset`。\n- 未知 `event_type` 或 `category` 会产生 warning，但查询仍会执行，以兼容未来新增类型。\n\n## 事件类型与类别\n\n| `event_type` | `category` | 含义 |\n|--------------|------------|------|\n| `sandbox_prehook` | `sandbox` | 沙箱执行前决策 |\n| `harden` | `hardening` | Security Baseline 检查或加固 |\n| `verify` | `asset_verify` | 资产完整性验证 |\n| `summary` | `summary` | 安全摘要动作 |\n| `code_scan` | `code_scan` | 代码安全扫描 |\n| `prompt_scan` | `prompt_scan` | Prompt 安全扫描 |\n| `pii_scan` | `pii_scan` | PII\u002F凭据检测 |\n| `skill_ledger` | `skill_ledger` | Skill Ledger 检查 |\n\n成功或失败由顶层 `result` 表示，不要假设失败事件的 `event_type` 带 `_error` 后缀。\n\n## `events` 输出结构\n\n### JSON 列表与 JSONL\n\n`--output json` 返回事件对象数组。`--output jsonl` 每行返回一个相同结构的事件对象。事件 envelope 为：\n\n```json\n{\n  \"event_id\": \"\u003Cuuid>\",\n  \"event_type\": \"code_scan\",\n  \"category\": \"code_scan\",\n  \"result\": \"succeeded\",\n  \"timestamp\": \"\u003CISO-8601 UTC>\",\n  \"trace_id\": \"\u003Ctrace_id>\",\n  \"pid\": 1234,\n  \"uid\": 1000,\n  \"session_id\": \"\u003Csession_id-or-null>\",\n  \"run_id\": \"\u003Crun_id-or-null>\",\n  \"call_id\": \"\u003Ccall_id-or-null>\",\n  \"tool_call_id\": \"\u003Ctool_call_id-or-null>\",\n  \"details\": {}\n}\n```\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| `event_id` | string | 事件 UUID |\n| `event_type` | string | 事件类型 |\n| `category` | string | 聚合类别 |\n| `result` | string | `succeeded` 或 `failed` |\n| `timestamp` | string | ISO-8601 UTC 时间 |\n| `trace_id` | string | 调用链标识，可能为空字符串 |\n| `pid`, `uid` | integer | 记录事件的进程与用户 ID |\n| `session_id`, `run_id` | string \\| null | Agent 会话和 run\u002Fturn 关联标识 |\n| `call_id`, `tool_call_id` | string \\| null | LLM 与工具调用关联标识 |\n| `details` | object | 后端专属结构化数据 |\n\n`details` 没有跨事件类型的固定 schema。只读取当前任务需要且实际存在的字段，不要根据其他类别的事件臆测字段。\n\n扫描判定固定位于 `details.result` 之下：扫描类事件用 `details.result.verdict`，`sandbox_prehook` 用 `details.result.decision`。**`details.verdict` 不是有效路径**，不要按它取值。取值前先检查类型与存在性，缺失时按“风险审查”一节记为 `MISSING` 并列为待核项。\n\n### 计数输出\n\n`--count` 输出一个 JSON 整数：\n\n```json\n12\n```\n\n`--count-by` 输出一个 JSON 对象，键是分组值，值是数量：\n\n```json\n{\n  \"code_scan\": 8,\n  \"prompt_scan\": 4\n}\n```\n\n## 会话级报告\n\n### 调用方式\n\n```bash\n# 最近记录的会话\nagent-sec-cli observability report --last --format json\n\n# 指定会话\nagent-sec-cli observability report --session-id '\u003Csession_id>' --format json\n```\n\n选择 `--last` 或 `--session-id` 之一：`--last` 查询最近记录的会话，`--session-id '\u003Csession_id>'` 查询指定会话。命令没有默认目标；两者都不提供时返回错误。`--format` 支持 `text` 和 `json`，供 Agent 解析时必须使用 `json`。\n\n### JSON 结构\n\n```json\n{\n  \"session_id\": \"\u003Csession_id>\",\n  \"first_seen\": \"2026-08-05 10:00:00\",\n  \"last_seen\": \"2026-08-05 10:05:00\",\n  \"duration_seconds\": 300.0,\n  \"turn_count\": 3,\n  \"llm_calls\": 4,\n  \"request_bytes\": 1200,\n  \"response_bytes\": 2400,\n  \"tool_breakdown\": {\n    \"shell\": 2\n  },\n  \"security_verdicts\": {\n    \"code_scan\": {\n      \"succeeded\": 2\n    }\n  },\n  \"security_hint\": null\n}\n```\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| `session_id` | string | 会话 ID |\n| `first_seen`, `last_seen` | string | 会话首末事件的 UTC 时间文本 |\n| `duration_seconds` | number | 会话持续秒数，保留一位小数 |\n| `turn_count` | integer | 记录的 Agent turn 数 |\n| `llm_calls` | integer | `after_llm_call` 事件数 |\n| `request_bytes`, `response_bytes` | integer | 模型请求与响应累计字节数 |\n| `tool_breakdown` | object\u003Cstring, integer> | 工具名称到调用次数的映射 |\n| `security_verdicts` | object\u003Cstring, object\u003Cstring, integer>> | 安全类别到 `result` 计数的映射 |\n| `security_hint` | string \\| null | 安全事件不可用、未关联或查询失败时的说明 |\n\n`security_verdicts` 当前按安全事件顶层 `result`（如 `succeeded` \u002F `failed`）聚合；不要把它解释为扫描器的 `pass` \u002F `warn` \u002F `deny`。顶层 `result` 只反映扫描进程是否执行成功，几乎恒为 `succeeded`，因此 `security_verdicts` **全 `succeeded` 不代表无风险**，不能作为安全结论的依据。得出任何安全结论前，必须用相同 `session_id` 执行“风险审查”一节的聚合命令。\n\n## 关联与报告规则\n\n- 优先使用 `session_id` 关联会话，使用 `run_id` 缩小到某个 run\u002Fturn；`trace_id` 用于追踪一次具体调用链。`run_id` 和 `trace_id` 只能来自用户或已有查询结果，Agent 无法自行获取。\n- 只有 cosh-ng 能把“本次会话”落实为真实的 `--session-id` 查询（通过 `runtime_context` 的 `provider_session_id`）。其他运行时用时间范围或 `--last`，并在报告里写清实际范围。不要用 shell 环境变量推测会话身份。\n- 会话报告没有安全事件时，检查 `security_hint`，不要直接断言“没有发生安全检测”。查询返回 0 条时，先确认传入的 `session_id` 确实是当前会话的 ID，再下结论。\n- table 与 summary 是人类展示格式，不承诺稳定列结构；自动化处理必须选择 JSON\u002FJSONL。\n- 报告事件时给出查询范围、筛选条件、匹配数量和必要结论。除非用户明确要求，不完整输出 `details` 中的命令、输入、证据或诊断信息。\n- **安全结论必须来自“风险审查”一节的聚合输出，不得来自对 JSON 的自由阅读。** 顶层 `result` 与 `security_verdicts` 都不是判定依据。未完成聚合时，不得输出“无风险”“无安全事件”“一切正常”一类表述；待核项非 0 时，必须逐条列出。\n- 事件总数接近或超过 `--limit`（默认 100）时，先调大 `--limit` 或分页取全量，再做聚合。基于被截断的结果得出的“无风险”结论是错误的。即使匹配总数小于等于 200，也不能省略显式 `--limit`；默认 100 会截断 101–200 条事件。\n","# Security Observability\n\n通过 `agent-sec-cli` 查询本地 SQLite 中的安全事件，并将事件与 Agent 会话关联起来。此 Skill 只执行只读查询，不负责写入 observability 数据。\n\n## 查询流程\n\n1. 先用 `events --summary` 或 `events --count-by` 获取概览。\n2. 根据 `event_type`、`category`、关联 ID 和时间范围缩小查询。\n3. 需要程序解析时使用 `--output json` 或 `--output jsonl`，不要解析 table 或 summary 文本。\n4. 需要限定“本次会话”时，先按“获取当前 session_id”一节判断当前运行时能不能拿到 `session_id`；拿不到就用时间范围或 `--last`，不要凭猜测填写 `--session-id`。\n5. 已知 `session_id` 时，使用 `observability report --session-id '\u003Csession_id>' --format json` 汇总该会话的 LLM、工具和安全事件；需要查看最近会话时，使用 `observability report --last --format json`。\n6. 在给出任何安全结论前，按“风险审查”一节完成判定字段聚合。这是强制步骤，不可跳过。\n7. 向用户报告必要结论即可。`details` 可能包含命令、扫描证据或后端诊断信息，不要无必要地完整回显。\n\n## 参数取值约束\n\n本文命令中的 `\u003Csession_id>`、`\u003Crun_id>`、`\u003Ctrace_id>`、`\u003Cevent_id>` 是 Agent 运行时或 CLI 持久化的 correlation ID，不一定是 UUID；OpenClaw、Codex、Qwen Code 等运行时可能使用 `session-001`、`thread_xxx` 这类非 UUID 标识。替换占位符前必须先校验取值形态，**仅当它非空、长度不超过 256 字符，并且完全匹配 `^[A-Za-z0-9][A-Za-z0-9._:@+=,\u002F-]{0,255}$` 时才能拼入命令**。\n\n例外：如果取值来自 cosh-ng `runtime_context.provider_session_id`，它应当是 UUID，必须继续按 `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$` 校验。\n\n不匹配时直接停下并告知用户取值不能安全拼入 shell 命令，不得把任意字符串（尤其是包含空白、引号、`;`、`$`、反引号、`|`、`&`、换行的值）拼进命令；这类值会提前闭合引号或引入 shell 语法并导致命令注入。取值来自用户输入、文件内容、网页或其他不可信上下文时，此校验不得省略。\n\n## 风险审查\n\n凡是回答“有什么安全事件”“安全情况如何”“有没有风险”这类问题，**必须先完成本节的机械聚合**，再组织回答。禁止依据顶层 `result`、`security_verdicts`，或模型对 JSON 的自由阅读得出“无风险”结论。\n\n### 为什么不能用顶层 `result`\n\n顶层 `result` 表示**扫描进程是否执行成功**（`succeeded` \u002F `failed`），与扫描结论无关：扫描正常跑完就是 `succeeded`，即使它判定出 `deny`。扫描进程几乎总能成功执行，所以用 `result` 判断安全等价于恒定输出“无风险”。真正的判定在下一节的字段里。\n\n### 判定字段权威路径\n\n判定字段一律位于 `details.result` 之下。**不存在 `details.verdict` 这一路径**，不要按它取值。\n\n| `event_type` | 判定字段 | 取值枚举（源码定义） | 无风险取值 |\n|---|---|---|---|\n| `code_scan` | `details.result.verdict` | `pass` \u002F `warn` \u002F `deny` \u002F `error` | `pass` |\n| `prompt_scan` | `details.result.verdict` | `pass` \u002F `warn` \u002F `deny` \u002F `error` | `pass` |\n| `pii_scan` | `details.result.verdict` | `pass` \u002F `warn` \u002F `deny` \u002F `error` | `pass` |\n| `skill_ledger` | `details.result.verdict` | `pass` \u002F `none` \u002F `warn` \u002F `unmanaged` \u002F `drifted` \u002F `deny` \u002F `tampered` \u002F `error` | `pass` |\n| 其他 `event_type` | — | — | **不属于扫描事件，聚合命令会在管道入口过滤掉** |\n\n### 取值语义\n\n向用户描述待核项时按下表解释取值，不要照抄 token，也不要自行推断含义。\n\n| verdict | 语义 | 适用 |\n|---|---|---|\n| `pass` | 已扫描，未发现问题 | 全部 |\n| `warn` | 发现低风险问题 | 全部 |\n| `deny` | 发现高风险问题 | 全部 |\n| `error` | **扫描器执行失败，本次未完成扫描** | 全部 |\n| `none` | 无 ledger 制品，或已核验的扫描状态为未扫描 | `skill_ledger` |\n| `unmanaged` | skill 目录不在 ledger 管理范围内 | `skill_ledger` |\n| `drifted` | 文件在上次认证后被改动 | `skill_ledger` |\n| `tampered` | ledger 元数据缺失或签名核验失败 | `skill_ledger` |\n| `MISSING` | 判定字段缺失，本次操作未产生判定 | 全部 |\n\n`skill_ledger` 出现 `MISSING` 通常是 `status`、`audit`、`list-scanners` 这类非判定命令留下的审计记录（查 `details.result.command` 可确认是哪个命令）。这类条目仍会出现在聚合输出的待核项里，但描述时应说明为“非判定操作”，不得断言为风险；`code_scan`、`prompt_scan`、`pii_scan` 出现 `MISSING` 才属异常，必须作为真实待核项呈现。\n\n`error`、`none`、`unmanaged` 表示**未取得有效判定**，既不能表述为“安全”“无风险”，也不能表述为“检测到高危”。正确表述是本次未完成扫描或该目标不在扫描范围内。\n\n### 分类规则：用允许列表，不用拒绝列表\n\n本节只处理**安全扫描事件**（上表四种）。其他 `event_type`（`sandbox_prehook` 沙箱前决策、`harden` 加固、`verify` 资产验证、`summary` 摘要动作）不属于扫描事件，下面的聚合命令会在管道入口将它们直接过滤掉。\n\n允许列表同时作用于两个维度：`event_type` 必须在上表四种中（其余一律过滤），**并且**判定字段显式等于对应的无风险取值。两个条件同时成立才可计入无风险。\n\n- 判定字段缺失、不是字符串、`details` 或 `details.result` 不是对象时，记为 `MISSING` 并列为待核项，不得当作 `pass`。\n- 已知类型但判定值不等于无风险取值时，一律列为待核项，包括上表未列出的新增取值。\n- 不得因为某个取值“看起来不严重”而省略。`warn` 与 `none` 必须出现在待核项清单里。\n\n### 聚合命令\n\n不要靠阅读 JSON 归纳，必须执行聚合命令。聚合命令的输出只包含**计数表 + 仅 RISK 行**，`pass`\u002F`allow` 事件不逐行列出。对于 `verdict == \"pass\"` 的普通事件，不要查询其具体 `details` 内容——它们的行为符合预期，打出来只会占上下文。\n\n输出中以 `RISK` 开头的行即待核项。\n\n```bash\nagent-sec-cli events --session-id '\u003Csession_id>' --count\nagent-sec-cli events --session-id '\u003Csession_id>' --output json --limit '\u003Cmatching_count_or_safe_page_size>' | jq -r '{code_scan:\"pass\",prompt_scan:\"pass\",pii_scan:\"pass\",skill_ledger:\"pass\"} as $ok | [.[] | select($ok[.event_type \u002F\u002F \"\"] != null) | {t: .event_type, j: (.details | if type==\"object\" then .result else null end | if type==\"object\" then .verdict else null end | if type==\"string\" then . else \"MISSING\" end), ts: .timestamp, id: .event_id}] as $rows | \"total=\\($rows|length)  risk_items=\\([$rows[]|select(.j != $ok[.t])]|length)\", ($rows | group_by([.t,.j])[] | \"  \\(.[0].t) \\(.[0].j) \\(length)\"), ($rows[] | select(.j != $ok[.t]) | \"RISK \\(.ts) \\(.t) \\(.j) \\(.id)\")'\n```\n\n环境没有 `jq` 时用等价的 python3（`python3` 是 `agent-sec-cli` 的运行依赖，一定可用）：\n\n```bash\nagent-sec-cli events --session-id '\u003Csession_id>' --count\nagent-sec-cli events --session-id '\u003Csession_id>' --output json --limit '\u003Cmatching_count_or_safe_page_size>' | python3 -c 'import sys,json,collections;OK={\"code_scan\":\"pass\",\"prompt_scan\":\"pass\",\"pii_scan\":\"pass\",\"skill_ledger\":\"pass\"};V=lambda d:(lambda r:r[\"verdict\"] if isinstance(r,dict) and isinstance(r.get(\"verdict\"),str) else \"MISSING\")(d.get(\"result\") if isinstance(d,dict) else None);R=[(e[\"event_type\"],V(e.get(\"details\")),e.get(\"timestamp\"),e.get(\"event_id\")) for e in json.load(sys.stdin) if e.get(\"event_type\") in OK];K=[x for x in R if x[1]!=OK[x[0]]];C=collections.Counter((x[0],x[1]) for x in R);print(\"total=%d  risk_items=%d\"%(len(R),len(K))+\"\".join(\"\\n  %-14s %-13s %d\"%(t,j,n) for (t,j),n in sorted(C.items()))+\"\".join(\"\\nRISK %s %s %s %s\"%(x[2],x[0],x[1],x[3]) for x in K))'\n```\n\n按时间范围查询时，把 `--session-id '\u003Csession_id>'` 换成 `--last-hours N` 等筛选条件，并保留相同的 `--count` 与 `--limit` 策略。注意 `--limit` 默认 100：聚合前必须先读取匹配总数，再调大 `--limit` 或分页，否则待核项会被截断而漏报。\n\n### 上下文开销控制\n\n安全报告不应挤占 Agent 对话的有效上下文，遵循以下原则：\n\n1. **先查总数，再覆盖全量聚合**：使用 `--count` 取得匹配事件数量。小于等于 200 时，聚合命令必须显式设置 `--limit` 为匹配总数或更大的安全分页上限；超过 200 时，先用 `--count-by category` 确认哪些类别有量，再逐类别 `--category \u003Ccat> --limit 200 --offset \u003Coffset>` 分页聚合，直到覆盖该类别的全部匹配事件。\n2. **只展开待核项**：`pass`\u002F`allow` 事件只出现在计数表的数字里，不逐行列出，更不要查询其 `details`。只有 RISK 行才需要向用户呈现。\n3. **仅按需深钻**：如果用户要求了解某条待核项的具体原因，再按“获取单条事件细节”一节取它的 `details`（一条）。不要默认批量展开全部 `details`。\n4. **利用管道聚合**：全量 JSON 通过 pipe 直接交给 jq\u002Fpython3，不要先 `--output json` 再由 Agent 逐行阅读——后者等于把几十 KB 的原始数据灌入上下文，而管道聚合后输出只有十几行。\n\n### 报告输出契约\n\n按顺序输出，前两项不得省略：\n\n1. **查询范围**：实际使用的筛选条件（`session_id` 或时间范围）、`--limit` 取值与匹配总数。\n2. **待核项清单**：逐条列出每个待核事件的 `timestamp`、`event_type`、判定值。一条都没有时，写“按判定字段聚合后待核项为 0”，而不是“没有安全事件”。\n3. **按 `event_type` × 判定值的计数表**。\n4. 需要时再补充结论与建议。\n\n禁止表述：在未完成本节聚合的前提下输出“无任何安全事件”“未发现风险”“一切正常”。待核项非 0 时，结论段必须包含这些项，不得只体现在计数表里。\n\n### 获取单条事件细节\n\n所有细节都在统一的 `details` 字段下，形状固定为 `{request, result}`：\n\n- `details.request` —— 本次扫描的输入侧信息。\n- `details.result.summary` —— 判定摘要（一句话或分类计数）。\n- `details.result.findings[]` —— **命中明细，要回答“为何被判定”就看这里**。\n\n`events` **没有 `--event-id` 过滤参数**，按 `event_id` 取单条需要在客户端过滤：\n\n```bash\nagent-sec-cli events --session-id '\u003Csession_id>' --output json | jq -r '.[] | select(.event_id == \"\u003Cevent_id>\") | .details'\n```\n\n只看判定依据，不取整个 `details`（更省上下文）：\n\n```bash\nagent-sec-cli events --session-id '\u003Csession_id>' --output json | jq -r '.[] | select(.event_id == \"\u003Cevent_id>\") | {summary: .details.result.summary, findings: .details.result.findings}'\n```\n\n`findings[]` 内部字段因扫描器而异（规则类扫描带规则标识与描述，PII 类带类型与脱敏证据）。**按实际返回的字段陈述，不要假设字段名，也不要把某一种扫描器的字段套到另一种上。**\n\n注意：不同扫描器对 `details.request` 的处理强度不同——部分扫描器只存长度与哈希，部分会存被扫描的原始内容。引用 `details.request` 时适用下一节的敏感值约束。\n\n### 报告不得重新引入敏感值\n\n安全事件入库时已做脱敏：`findings[].evidence_redacted` 存的是按类型脱敏后的值（如 `phone_cn` 为 `NNN****NNNN`、`credit_card` 为 `[REDACTED_CARD:后四位]`、凭据类为 `[REDACTED_*]`），`request` 侧只存 `text_length` 与 `text_sha256`，**不存原文**。\n\n描述事件时只能使用事件自带的脱敏字段（`type`、`category`、`severity`、`confidence`、`evidence_redacted`、`span`）。**禁止为了“解释更清楚”而从对话历史、用户输入、工具参数或其他上下文里找回并复述原始敏感值**（手机号、卡号、身份证号、密钥、token 等）。\n\n原因：模型输出会被 PII 扫描（`source=model_output`）。在报告里复述原始敏感值会当场触发新的 `pii_scan` 告警，把一次只读查询变成一次新的泄露事件。\n\n- 正确：直接引用事件字段，如“`pii_scan` warn × 3，`type` 为 `phone_cn` \u002F `credit_card`，`evidence_redacted` 已脱敏”。\n- 错误：为了说明触发原因而把用户当时输入的手机号、卡号原文重新写进回复。\n\n## 获取当前 session_id\n\n`session_id` 由 Agent 运行时提供，`agent-sec-cli` 自身无法推断“本次会话”。能不能按 `session_id` 查询取决于当前运行时。\n\n### cosh-ng 特别用法：`runtime_context` 工具\n\n> 本节仅适用于 **cosh-ng** Agent。截至当前版本，其他 Agent 运行时（cosh\u002Fcopilot-shell、OpenClaw、Codex、Qwen Code、Hermes、Qoder CLI 等）没有这个工具，直接跳到“其他 Agent 运行时”一节。判断方式是看当前可用工具列表里有没有 `runtime_context`，不要靠版本号猜。\n\ncosh-ng 提供只读工具 `runtime_context`（无参数），返回当前运行时元数据，其中 `provider_session_id` 就是本次会话的 `session_id`：\n\n```json\n{\n  \"provider_session_id\": \"\u003Csession_id>\",\n  \"runtime\": { \"name\": \"cosh-ng\", \"version\": \"\u003Cversion>\" },\n  \"model\": \"\u003Cmodel>\",\n  \"approval_mode\": \"\u003Capproval_mode>\",\n  \"workspace\": { \"cwd\": \"\u003Ccwd>\", \"project_root\": \"\u003Cproject_root>\" },\n  \"session\": { \"resumed\": false },\n  \"compaction\": {\n    \"revision\": 0,\n    \"active_projection\": false,\n    \"compacted_through\": null\n  },\n  \"capabilities\": { \"tools\": [], \"active_extensions\": [] }\n}\n```\n\n用法是先调用 `runtime_context` 工具取得 `provider_session_id`，再把该值作为 `--session-id` 的参数：\n\n```bash\n# 本次会话的安全事件\nagent-sec-cli events --session-id '\u003Cprovider_session_id>' --output json\n\n# 本次会话的会话级报告\nagent-sec-cli observability report --session-id '\u003Cprovider_session_id>' --format json\n```\n\n`provider_session_id` 与 cosh-ng hook 上报、并最终写入安全事件与 observability 记录的 `session_id` 是同一个值，且应当是 UUID，因此可以在通过 UUID 校验后直接用于上面两个查询，无需再做映射或截断。\n\n约束：\n\n- **不要用环境变量 `$COSH_SESSION_ID` 代替。** 在 cosh-ng 中它表示 shell\u002F终端会话（审计记录里的 `shell_session_id`），与 Agent 会话的 `session_id` 属于不同命名空间，且在缺省时会退化成进程级的兜底值。用它查询通常会静默返回 0 条事件，看起来像“本次会话没有安全事件”，属于错误结论。\n- `runtime_context` **不返回 `run_id`**。需要按 run\u002Fturn 缩小范围时，`run_id` 仍必须来自当前上下文或用户提供。\n- `runtime_context` 是只读工具，只读取运行时元数据，不写入 observability 数据。\n- 该工具无参数；不要尝试传入 `session_id` 之类的字段去“查询指定会话”。\n\n### 其他 Agent 运行时：不使用 `--session-id`\n\n截至当前版本，只有 cosh-ng 能让 Agent 取得自己的 `session_id`。其他 Agent 运行时（cosh\u002Fcopilot-shell、OpenClaw、Codex、Qwen Code、Hermes、Qoder CLI 等）没有对应能力，Agent 无法识别“本次会话”，因此：\n\n- 默认改用**时间范围查询**（`--last-hours`，或 `--since` \u002F `--until`），或用 `observability report --last` 查询最近记录的会话。不要因为拿不到 `session_id` 而停下来反复询问用户。\n- **必须在报告中说明实际查询范围**，例如“最近 1 小时的安全事件”或“最近记录的一次会话，不一定是当前会话”。不要把这类结果表述成“本次会话”的结论。\n- 只有用户主动提供 `session_id` 时，才使用 `--session-id` 精确查询；拼入前先按“参数取值约束”一节校验取值形态。\n- 同样不要用 shell 环境变量（包括 `$COSH_SESSION_ID`）凑一个 `session_id` 出来。\n\n## Few-shot 场景\n\n### 查询最近一小时的安全事件\n\n**用户：** 帮我查询最近一个小时出现的安全事件。\n\n**执行：**\n\n```bash\nagent-sec-cli events --last-hours 1 --output json\n```\n\n**回答：** 先按“风险审查”一节对结果做判定字段聚合（把 `--session-id` 换成 `--last-hours 1`），再按报告输出契约作答：查询范围为最近一小时、匹配总数、待核项清单、计数表。不要按顶层 `result` 概括，也不要把 `succeeded` \u002F `failed` 解释为扫描 verdict。\n\n### 查询本次会话的安全事件\n\n**用户：** 帮我查询本次会话出现的安全事件。\n\n**执行：** 在 cosh-ng 上，先调用 `runtime_context` 工具取得 `provider_session_id`，再精确查询：\n\n```bash\nagent-sec-cli events --session-id '\u003Ccurrent_session_id>' --output json\n```\n\n其他 Agent 运行时拿不到当前 `session_id`，改用时间范围查询：\n\n```bash\nagent-sec-cli events --last-hours 1 --output json\n```\n\n**回答：** 先按“风险审查”一节完成判定字段聚合，再按报告输出契约作答。cosh-ng 上说明使用的 `session_id`、匹配数量、待核项清单和计数表，不要退而使用 `$COSH_SESSION_ID`。其他运行时必须说明实际查询的是最近 N 小时而不是“本次会话”，不要把结果表述成本次会话的结论。\n\n### 复盘本次会话的安全情况（cosh-ng）\n\n**用户：** 帮我复盘本次会话的安全情况。\n\n**执行：** 调用 `runtime_context` 工具取得 `provider_session_id`，再生成该会话的报告：\n\n```bash\nagent-sec-cli observability report --session-id '\u003Cprovider_session_id>' --format json\n```\n\n**回答：** `observability report` 只提供会话范围、`tool_breakdown` 和按顶层 `result` 聚合的 `security_verdicts`，**不足以支撑安全结论**。必须再用同一个 `session_id` 执行“风险审查”一节的聚合命令，并按报告输出契约给出待核项清单。不要改用 `--last`：它查询最近记录的会话，在并发或嵌套会话下可能不是本次会话。\n\n## 安全事件查询\n\n### 概览\n\n```bash\n# 最近 24 小时的人类可读安全态势摘要\nagent-sec-cli events --summary\n\n# 最近 24 小时按类别统计\nagent-sec-cli events --last-hours 24 --count-by category\n\n# 最近 8 小时 code_scan 事件数量\nagent-sec-cli events --last-hours 8 --category code_scan --count\n```\n\n`--summary` 在未指定时间范围时默认查询最近 24 小时。它输出人类可读文本，只适合展示，不适合作为稳定的数据接口。\n\n### 筛选并获取结构化数据\n\n```bash\n# 查询最近一小时的代码扫描事件\nagent-sec-cli events --last-hours 1 --category code_scan --output json\n\n# 按 session 和 run 精确关联，并以 JSONL 输出\nagent-sec-cli events --session-id '\u003Csession_id>' --run-id '\u003Crun_id>' --output jsonl\n\n# 查询 ISO-8601 时间区间；since 包含边界，until 不包含边界\nagent-sec-cli events --since '2026-08-05T00:00:00Z' --until '2026-08-06T00:00:00Z' --limit 100 --offset 0 --output json\n```\n\n### 参数\n\n| 参数 | 默认值 | 说明 |\n|------|--------|------|\n| `--event-type` | 无 | 按事件类型筛选 |\n| `--category` | 无 | 按安全能力类别筛选 |\n| `--trace-id` | 无 | 按一次 CLI\u002F调用链 trace 筛选 |\n| `--session-id` | 无 | 按 Agent 会话筛选 |\n| `--run-id` | 无 | 按 Agent run\u002Fturn 筛选 |\n| `--since` | 无 | ISO-8601 起始时间，包含该边界 |\n| `--until` | 无 | ISO-8601 结束时间，不包含该边界 |\n| `--last-hours` | 无 | 查询最近 N 小时，可使用小数 |\n| `--limit` | `100` | 最多返回的事件数 |\n| `--offset` | `0` | 跳过的事件数 |\n| `--count` | `false` | 只输出匹配事件数量 |\n| `--count-by` | 无 | 分组计数，仅支持 `category`、`event_type`、`trace_id` |\n| `--output`, `-o` | `table` | 列表输出格式：`table`、`json`、`jsonl` |\n| `--summary` | `false` | 输出人类可读安全态势摘要 |\n\n### 参数约束\n\n- V2 查询范围由 daemon 的 UDS peer UID 决定：普通用户只读自身 UID，root 读取全部 UID。\n- `events --session-id` 使用原始 session ID；root 返回所有 UID 下匹配该 ID 的事件。同名会话在 root 返回结果中显示为 `UID_session_id`，仅用于区分归属；查询按原始 ID 匹配。\n- `--last-hours` 与 `--since` \u002F `--until` 互斥。\n- `--count` 与 `--count-by` 互斥。\n- `--summary` 与 `--count`、`--count-by`、任何显式 `--output` 互斥。\n- `--summary` 会读取最多 10000 条匹配事件；普通列表使用 `--limit` 和 `--offset`。\n- 未知 `event_type` 或 `category` 会产生 warning，但查询仍会执行，以兼容未来新增类型。\n\n## 事件类型与类别\n\n| `event_type` | `category` | 含义 |\n|--------------|------------|------|\n| `sandbox_prehook` | `sandbox` | 沙箱执行前决策 |\n| `harden` | `hardening` | Security Baseline 检查或加固 |\n| `verify` | `asset_verify` | 资产完整性验证 |\n| `summary` | `summary` | 安全摘要动作 |\n| `code_scan` | `code_scan` | 代码安全扫描 |\n| `prompt_scan` | `prompt_scan` | Prompt 安全扫描 |\n| `pii_scan` | `pii_scan` | PII\u002F凭据检测 |\n| `skill_ledger` | `skill_ledger` | Skill Ledger 检查 |\n\n成功或失败由顶层 `result` 表示，不要假设失败事件的 `event_type` 带 `_error` 后缀。\n\n## `events` 输出结构\n\n### JSON 列表与 JSONL\n\n`--output json` 返回事件对象数组。`--output jsonl` 每行返回一个相同结构的事件对象。事件 envelope 为：\n\n```json\n{\n  \"event_id\": \"\u003Cuuid>\",\n  \"event_type\": \"code_scan\",\n  \"category\": \"code_scan\",\n  \"result\": \"succeeded\",\n  \"timestamp\": \"\u003CISO-8601 UTC>\",\n  \"trace_id\": \"\u003Ctrace_id>\",\n  \"pid\": 1234,\n  \"uid\": 1000,\n  \"session_id\": \"\u003Csession_id-or-null>\",\n  \"run_id\": \"\u003Crun_id-or-null>\",\n  \"call_id\": \"\u003Ccall_id-or-null>\",\n  \"tool_call_id\": \"\u003Ctool_call_id-or-null>\",\n  \"details\": {}\n}\n```\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| `event_id` | string | 事件 UUID |\n| `event_type` | string | 事件类型 |\n| `category` | string | 聚合类别 |\n| `result` | string | `succeeded` 或 `failed` |\n| `timestamp` | string | ISO-8601 UTC 时间 |\n| `trace_id` | string | 调用链标识，可能为空字符串 |\n| `pid`, `uid` | integer | 记录事件的进程与用户 ID |\n| `session_id`, `run_id` | string \\| null | Agent 会话和 run\u002Fturn 关联标识 |\n| `call_id`, `tool_call_id` | string \\| null | LLM 与工具调用关联标识 |\n| `details` | object | 后端专属结构化数据 |\n\n`details` 没有跨事件类型的固定 schema。只读取当前任务需要且实际存在的字段，不要根据其他类别的事件臆测字段。\n\n扫描判定固定位于 `details.result` 之下：扫描类事件用 `details.result.verdict`，`sandbox_prehook` 用 `details.result.decision`。**`details.verdict` 不是有效路径**，不要按它取值。取值前先检查类型与存在性，缺失时按“风险审查”一节记为 `MISSING` 并列为待核项。\n\n### 计数输出\n\n`--count` 输出一个 JSON 整数：\n\n```json\n12\n```\n\n`--count-by` 输出一个 JSON 对象，键是分组值，值是数量：\n\n```json\n{\n  \"code_scan\": 8,\n  \"prompt_scan\": 4\n}\n```\n\n## 会话级报告\n\n### 调用方式\n\n```bash\n# 最近记录的会话\nagent-sec-cli observability report --last --format json\n\n# 指定会话\nagent-sec-cli observability report --session-id '\u003Csession_id>' --format json\n```\n\n选择 `--last` 或 `--session-id` 之一：`--last` 查询最近记录的会话，`--session-id '\u003Csession_id>'` 查询指定会话。命令没有默认目标；两者都不提供时返回错误。`--format` 支持 `text` 和 `json`，供 Agent 解析时必须使用 `json`。\n\n### JSON 结构\n\n```json\n{\n  \"session_id\": \"\u003Csession_id>\",\n  \"first_seen\": \"2026-08-05 10:00:00\",\n  \"last_seen\": \"2026-08-05 10:05:00\",\n  \"duration_seconds\": 300.0,\n  \"turn_count\": 3,\n  \"llm_calls\": 4,\n  \"request_bytes\": 1200,\n  \"response_bytes\": 2400,\n  \"tool_breakdown\": {\n    \"shell\": 2\n  },\n  \"security_verdicts\": {\n    \"code_scan\": {\n      \"succeeded\": 2\n    }\n  },\n  \"security_hint\": null\n}\n```\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| `session_id` | string | 会话 ID |\n| `first_seen`, `last_seen` | string | 会话首末事件的 UTC 时间文本 |\n| `duration_seconds` | number | 会话持续秒数，保留一位小数 |\n| `turn_count` | integer | 记录的 Agent turn 数 |\n| `llm_calls` | integer | `after_llm_call` 事件数 |\n| `request_bytes`, `response_bytes` | integer | 模型请求与响应累计字节数 |\n| `tool_breakdown` | object\u003Cstring, integer> | 工具名称到调用次数的映射 |\n| `security_verdicts` | object\u003Cstring, object\u003Cstring, integer>> | 安全类别到 `result` 计数的映射 |\n| `security_hint` | string \\| null | 安全事件不可用、未关联或查询失败时的说明 |\n\n`security_verdicts` 当前按安全事件顶层 `result`（如 `succeeded` \u002F `failed`）聚合；不要把它解释为扫描器的 `pass` \u002F `warn` \u002F `deny`。顶层 `result` 只反映扫描进程是否执行成功，几乎恒为 `succeeded`，因此 `security_verdicts` **全 `succeeded` 不代表无风险**，不能作为安全结论的依据。得出任何安全结论前，必须用相同 `session_id` 执行“风险审查”一节的聚合命令。\n\n## 关联与报告规则\n\n- 优先使用 `session_id` 关联会话，使用 `run_id` 缩小到某个 run\u002Fturn；`trace_id` 用于追踪一次具体调用链。`run_id` 和 `trace_id` 只能来自用户或已有查询结果，Agent 无法自行获取。\n- 只有 cosh-ng 能把“本次会话”落实为真实的 `--session-id` 查询（通过 `runtime_context` 的 `provider_session_id`）。其他运行时用时间范围或 `--last`，并在报告里写清实际范围。不要用 shell 环境变量推测会话身份。\n- 会话报告没有安全事件时，检查 `security_hint`，不要直接断言“没有发生安全检测”。查询返回 0 条时，先确认传入的 `session_id` 确实是当前会话的 ID，再下结论。\n- table 与 summary 是人类展示格式，不承诺稳定列结构；自动化处理必须选择 JSON\u002FJSONL。\n- 报告事件时给出查询范围、筛选条件、匹配数量和必要结论。除非用户明确要求，不完整输出 `details` 中的命令、输入、证据或诊断信息。\n- **安全结论必须来自“风险审查”一节的聚合输出，不得来自对 JSON 的自由阅读。** 顶层 `result` 与 `security_verdicts` 都不是判定依据。未完成聚合时，不得输出“无风险”“无安全事件”“一切正常”一类表述；待核项非 0 时，必须逐条列出。\n- 事件总数接近或超过 `--limit`（默认 100）时，先调大 `--limit` 或分页取全量，再做聚合。基于被截断的结果得出的“无风险”结论是错误的。即使匹配总数小于等于 200，也不能省略显式 `--limit`；默认 100 会截断 101–200 条事件。\n",{"name":5,"description":7},"main","d2c8a75e03a304c97d2b049d59f85c2ef0b08519","src\u002Fagent-sec-core\u002Fskills\u002Fsecurity-observability",193,"https:\u002F\u002Fgithub.com\u002Fagentic-os-org\u002FANOLISA","https:\u002F\u002Fagentic-os.sh\u002F","https:\u002F\u002Fgithub.com\u002Fagentic-os-org","2026-11-12 22:29:17",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add agentic-os-org\u002FANOLISA --skill security-observability",{"tool":62,"command":63},"bunx","bunx skills add agentic-os-org\u002FANOLISA --skill security-observability",{"tool":65,"command":66},"pnpm","pnpm dlx skills add agentic-os-org\u002FANOLISA --skill security-observability","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fagentic-os-org\u002FANOLISA\u002Fblob\u002Fmain\u002Fsrc\u002Fagent-sec-core\u002Fskills\u002Fsecurity-observability\u002FSKILL.md",true,[],[71,85,115,132,150,161],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":77,"publishedAt":43},"01M6K30MJVPNFF4VA778AZC512","ws-ckpt","Linux 工作区快照(ws-ckpt)。用户想保存或备份工作区当前状态(\"保存一下\"、\"留个底\"、\"先备份\"), 想撤销或恢复文件改动(\"回滚\"、\"撤销\"、\"改回去\"、\"恢复到之前\"、\"刚才那版\"), 想查看、对比或删除历史版本,或要开启\u002F关闭每回合自动快照、定时快照、快照保留策略时使用。 用户没提\"快照\"、只表达保存或恢复工作区文件的意图时同样适用;用户点名 git 操作(commit、branch、reset 到某提交)时不适用。","https:\u002F\u002Fgithub.com\u002Fagentic-os-org\u002FANOLISA\u002Fblob\u002Fmain\u002Fsrc\u002Fws-ckpt\u002Fsrc\u002Fskills\u002Fws-ckpt\u002FSKILL.md",[],[78,79,80,81,82,83,38,84],"ws ckpt","linux","commit","git","branch","reset","文件",{"skillNo":86,"name":87,"nameZh":6,"description":88,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":92,"publishedAt":43},"01M6K30G7BBZB1VCPE3NE08KZP","skillfs-mount","Help users configure, mount, and unmount a skillfs FUSE virtual filesystem on their local machine (e.g. for openclaw). Covers analyzing existing skill usage to suggest view configuration, generating skillfs-views.toml, mounting, and graceful unmount. Use this skill when the user asks to: set up skillfs, configure skill views, mount or unmount skillfs, check if skillfs is running, or get help organizing their skills directory (especially for openclaw users).","https:\u002F\u002Fgithub.com\u002Fagentic-os-org\u002FANOLISA\u002Fblob\u002Fmain\u002Fsrc\u002Fskillfs\u002Fdocs\u002Fskills\u002Fskillfs-mount\u002FSKILL.md",[91],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114],"skillfs mount","mount","virtual","get","local","existing skill","up","use","view","filesystem","toml","configure","skill usage","SET","analyzing","openclaw","machine","usage","for","when","running","directory",{"skillNo":116,"name":117,"nameZh":6,"description":118,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":119,"categories":120,"keywords":125,"publishedAt":43},"01M6K30CJW0X3V5PG4GP26TB63","test-skill","A test skill used in integration tests.","https:\u002F\u002Fgithub.com\u002Fagentic-os-org\u002FANOLISA\u002Fblob\u002Fmain\u002Fsrc\u002Fskillfs\u002Fcrates\u002Fskillfs-fuse\u002Ftests\u002Ffixtures\u002Ftest-skill\u002FSKILL.md",[121,124],{"categoryNo":122,"categoryName":123,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[126,127,128,129,130,131],"test","Test Skill","true","integration tests","integration","tests",{"skillNo":133,"name":134,"nameZh":6,"description":135,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":136,"categories":137,"keywords":138,"publishedAt":43},"01M6K304VCM7N6AR4PMYNDP3A7","storage-resize","Linux 磁盘扩容技能（RPM 包管理系统），实现云盘扩容后自动完成分区调整和文件系统扩容。支持系统盘\u002F数据盘在线扩容，适配 XFS\u002FEXT4\u002FBtrfs 文件系统。适用于 RPM 包管理的 Linux 系统（如 Alibaba Cloud Linux）。","https:\u002F\u002Fgithub.com\u002Fagentic-os-org\u002FANOLISA\u002Fblob\u002Fmain\u002Fsrc\u002Fos-skills\u002Fsystem-admin\u002Fstorage-resize\u002FSKILL.md",[],[139,140,141,79,142,143,144,145,146,84,147,148,149],"storage resize","storage","system","cloud","operations","btrfs","ecs","aliyun","rpm","alibaba","数据",{"skillNo":151,"name":152,"nameZh":6,"description":153,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":154,"categories":155,"keywords":156,"publishedAt":43},"01M6K2ZJEGANYH9TGGG25H7WVF","ktuner","内核参数自动调优。分析系统配置，输出调优建议并可一键应用，支持自动回滚。使用场景：系统性能优化、安全加固、内核参数诊断。","https:\u002F\u002Fgithub.com\u002Fagentic-os-org\u002FANOLISA\u002Fblob\u002Fmain\u002Fsrc\u002Fos-skills\u002Fsystem-admin\u002Fktuner\u002FSKILL.md",[],[152,157,28,158,159,30,160],"core","Production","shell","command",{"skillNo":162,"name":163,"nameZh":6,"description":164,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":165,"categories":166,"keywords":167,"publishedAt":43},"01M6K2ZD5K40XCX8R1EA85MS7P","backup-restore","处理所有备份与恢复任务，覆盖四个层面：工作区（项目\u002F数据库\u002F代码）、用户数据（家目录\u002Fdotfiles\u002F密钥）、操作系统（全量系统\u002F磁盘镜像\u002FLVM 快照）、阿里云快照（云盘快照\u002F自动策略\u002F跨地域复制\u002F整机镜像）。适用于 RPM 包管理的 Linux 系统（如 Alibaba Cloud Linux）纯命令行环境，使用 yum 包管理器。当用户提到备份、恢复、快照、归档、回滚、克隆、迁移、容灾等任何涉及数据保存或恢复的请求时触发此技能，即使请求看起来很简单也应使用。","https:\u002F\u002Fgithub.com\u002Fagentic-os-org\u002FANOLISA\u002Fblob\u002Fmain\u002Fsrc\u002Fos-skills\u002Fsystem-admin\u002Fbackup-restore\u002FSKILL.md",[],[168,169,170,142,171,172,173,147,148,174,149,175,176,79,146],"backup restore","backup","restore","beta","release","lvm","runtime","maintenance","dotfiles"]