Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/guides/pipeline-health-monitoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ When a Tier 1 `infrastructure_error` alert reaches the overseer monitor, it is r

### Advisor Gate

Issue [#1962](https://github.com/jwbron/egg/issues/1962) introduces the **advisor strategy** for the overseer's decision tier: Haiku continues to drive every cycle (`max_turns=1`), and an Opus 4.6 advisor is invoked **only when both** of these conditions hold simultaneously:
Issue [#1962](https://github.com/jwbron/egg/issues/1962) introduces the **advisor strategy** for the overseer's decision tier: Haiku continues to drive every cycle (`max_turns=1`), and the configured advisor model (`PipelineConfig.overseer_advisor_model`, defaulting to the `opus` alias) is invoked **only when both** of these conditions hold simultaneously:

1. **Haiku flags an anomaly** with classification confidence ≥ 0.8.
2. **A Tier-1 orchestrator health alert is currently active** (the same intersection precedent shipped in [#2012](https://github.com/jwbron/egg/issues/2012)).
Expand All @@ -431,7 +431,7 @@ The intersection gate keeps the heavy-tier model out of every poll cycle while s
| `alert` | Emit an `OVERSEER_ALERT` carrying the advisor's `alert_summary`, `alert_detail`, and translated `priority`. The advisor returns `priority` as `p0..p3`; `egg_overseer.priority.label_to_alert` maps to the alert verb's `low|medium|high` dimension. |
| `file_issue` | Emit an `OVERSEER_ALERT` whose `recommendation=file_issue` carries a fully composed `issue_title` + `issue_body` + `priority` + `anomaly_signature` in `recommendation_payload`. The CLI verb is **not** invoked here — see [Auto-Issue Filing (Shadow vs Live)](#auto-issue-filing-shadow-vs-live). |

The advisor is exposed to the sandbox as a CLI verb (`egg-orch overseer consult-advisor`); the handler at `sandbox/egg_lib/orch_cli.py::cmd_overseer_consult_advisor` calls `consult_advisor` from `shared/egg_overseer/advisor.py` directly. The underlying `run_agent_async` Opus call therefore runs sandbox-side and stays on the LLM-execution side of the EGG200 boundary documented in [agent-mode-design.md](agent-mode-design.md) — the orchestrator pod never holds Anthropic credentials. The CLI verb reads the keyword arguments (`classification`, `health_alerts`, `progress_events`, `recent_log_lines`) that comprise the executor → advisor prompt contract from a JSON file passed via `--inputs-file`.
The advisor is exposed to the sandbox as a CLI verb (`egg-orch overseer consult-advisor`); the handler at `sandbox/egg_lib/orch_cli.py::cmd_overseer_consult_advisor` calls `consult_advisor` from `shared/egg_overseer/advisor.py` directly. The underlying `run_agent_async` call therefore runs sandbox-side and stays on the LLM-execution side of the EGG200 boundary documented in [agent-mode-design.md](agent-mode-design.md) — the orchestrator pod never holds Anthropic credentials. The model used is resolved from `PipelineConfig.overseer_advisor_model` (read via the orchestrator status endpoint) when a pipeline ID is available; falls back to the `opus` default when absent or the lookup fails. The CLI verb reads the keyword arguments (`classification`, `health_alerts`, `progress_events`, `recent_log_lines`) that comprise the executor → advisor prompt contract from a JSON file passed via `--inputs-file`.

**No advisor cap is enforced in this PR** — the existing `max_llm_cost_per_hour=$5` envelope at `sandbox/agent-config/rules/overseer.md` remains the only budget control. A follow-up issue tracks an `overseer_advisor_max_uses_per_phase` (or equivalent) knob if production data shows the cap is needed.

Expand Down Expand Up @@ -534,7 +534,7 @@ The sandbox-side CLI verb is the one and only way the overseer invokes the advis
| `progress_events` | `list[dict]` | Recent structured progress events |
| `recent_log_lines` | `list[str]` | Tail of agent container log lines |

The handler at `sandbox/egg_lib/orch_cli.py::cmd_overseer_consult_advisor` calls `egg_overseer.advisor.consult_advisor()` directly. Output is the JSON-serialized `AdvisorVerdict` written to `--output-file` (or stdout when omitted). The Opus `run_agent_async` call runs sandbox-side, keeping the LLM call on the LLM-execution side of the EGG200 boundary; the orchestrator pod never holds Anthropic credentials.
The handler at `sandbox/egg_lib/orch_cli.py::cmd_overseer_consult_advisor` calls `egg_overseer.advisor.consult_advisor()` directly. Output is the JSON-serialized `AdvisorVerdict` written to `--output-file` (or stdout when omitted). The `run_agent_async` call runs sandbox-side, keeping the LLM call on the LLM-execution side of the EGG200 boundary; the orchestrator pod never holds Anthropic credentials. The model alias is resolved from `PipelineConfig.overseer_advisor_model` when a pipeline ID is provided (positional arg or `EGG_PIPELINE_ID`), falling back to `opus` when absent or the lookup fails.

**Backwards compatibility — top-level optional fields with omit-when-unset serialization.** The `OVERSEER_ALERT` schema gains three first-class optional fields on the `Message` envelope (`orchestrator/message_store.py`): `recommendation: str | None`, `recommendation_payload: dict | None`, and `schema_version: int = 1`. The `Message.to_dict()` serializer **omits** each of the three fields when they hold their defaults, so legacy callers that don't set them produce JSON byte-identical to the pre-#1962 shape. New consumers branch on the presence of `recommendation` (or, equivalently, `schema_version >= 2`); old consumers see no envelope change. The `egg-orch overseer alert` CLI gains `--recommendation file_issue --recommendation-payload-file /path/to/payload.json` flags that populate the new fields; the sandbox handler at `sandbox/egg_agent_tools/handlers/progress.py::progress_overseer_alert` enforces a 50 KB cap on the payload and validates that `recommendation` is one of the legal values (`file_issue` is currently the only accepted value).

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/orchestrator-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Run `egg-orch --help` for full usage. All commands support `--json` for machine-
| `egg-orch message send [<id>] --to <role\|all> --type <type> --subject "..." --body "..."` | Send directed or broadcast message. Types: `HANDOFF`, `STATUS`, `PROGRESS`, `HEARTBEAT`. (`QUESTION` was removed in [#1897](https://github.com/jwbron/egg/issues/1897).) |
| `egg-orch overseer alert [<id>] --anomaly <type> --priority <low\|medium\|high> --summary "..." [--detail "..."] [--recommend "..."] [--recommendation file_issue] [--recommendation-payload-file <path>]` | Broadcast `OVERSEER_ALERT` to human operator (overseer use only — always sets `message_type=OVERSEER_ALERT` and `to_role=all`). `--recommendation file_issue` attaches a structured advisor verdict; requires `--recommendation-payload-file` (JSON path with `issue_title`/`issue_body`/`priority`/`anomaly_signature`). |
| `egg-orch overseer file-issue [<id>] --anomaly-type <type> --priority <p0\|p1\|p2\|p3> --agent-role <role> --anomaly-signature <hex16> --issue-title-file <path> --issue-body-file <path> [--parent-alert-message-id <id>] [--dry-run] [--json]` | File a GitHub issue from the overseer role (advisor-gated). Checks `filed-issues.jsonl` + `gh issue list` for deduplication; skips filing if a matching open issue exists. Default stdout is plain text (`Filed issue #N (...)` or `Existing issue #N already covers ...`); with `--json`, prints JSON `{"issue_number": int, "filed": bool, "dedup_match": int\|null}`; with `--dry-run` (no `gh` invocation either way): if a dedup match is found, prints the same `--json` shape (with `issue_number`/`dedup_match` populated); otherwise prints `{"issue_number": null, "filed": false, "dedup_match": null, "dry_run": true, "argv": [...], "title": str, "body_bytes": int}`. Requires `EGG_PIPELINE_REPO` env var. |
| `egg-orch overseer consult-advisor [<id>] --inputs-file <path> [--output-file <path>] [--recent-log-bytes-cap <n>] [--json]` | Consult the Opus advisor for a structured `AdvisorVerdict` (sandbox-side LLM call). Reads a JSON inputs file with `classification`, `health_alerts`, `progress_events`, and `recent_log_lines`. Returns `decision` (`alert`\|`file_issue`\|`watch`), `priority`, `alert_summary`, `alert_detail`, `issue_title`, `issue_body`, and `reasoning`. Without `--output-file`, the JSON verdict is written to stdout; with `--output-file`, the verdict is written to that path and stdout shows a confirmation message (pass `--json` to additionally echo the verdict to stdout). `--recent-log-bytes-cap` overrides the `PipelineConfig.overseer_advisor_recent_log_bytes_cap` value (default 256 KiB); oldest lines are dropped first when the block exceeds the cap. `0` disables the cap. |
| `egg-orch overseer consult-advisor [<id>] --inputs-file <path> [--output-file <path>] [--recent-log-bytes-cap <n>] [--json]` | Consult the advisor for a structured `AdvisorVerdict` (sandbox-side LLM call). Reads a JSON inputs file with `classification`, `health_alerts`, `progress_events`, and `recent_log_lines`. Returns `decision` (`alert`\|`file_issue`\|`watch`), `priority`, `alert_summary`, `alert_detail`, `issue_title`, `issue_body`, and `reasoning`. Without `--output-file`, the JSON verdict is written to stdout; with `--output-file`, the verdict is written to that path and stdout shows a confirmation message (pass `--json` to additionally echo the verdict to stdout). When `[<id>]` (or `EGG_PIPELINE_ID`) is set, the verb reads `PipelineConfig.overseer_advisor_model` from the orchestrator status endpoint and passes the configured model alias to `consult_advisor`; falls back to the `opus` default if the pipeline ID is absent or the lookup fails. `--recent-log-bytes-cap` overrides the `PipelineConfig.overseer_advisor_recent_log_bytes_cap` value (default 256 KiB); oldest lines are dropped first when the block exceeds the cap. `0` disables the cap. |
| `egg-orch message poll [<id>] [--since <id>] [--limit <n>]` | Poll for messages from other agents (concurrent mode) |
| `egg-orch message wait [<id>] --for <TYPE>... [--timeout N] [--since <id>]` | Block until a **new** typed BRC event arrives (cursor-less calls start at stream tip — already-seen events are skipped). Pass `--since <id>` for zero-drop send→wait semantics. Exit 0 = matched, 1 = timeout, 2 = transient (retry-safe), 3 = permanent. See [Agent Wait Patterns §3](agent-wait-patterns.md#3-exit-code-contract-for-egg-orch-message-wait) |
| `egg-orch message wait-loop [<id>] --for <TYPE>... [--since <id>]` | **Canonical STAY ALIVE idiom** — loops `message wait` server-side until a new matching event arrives (defaults to stream-tip; pass `--since <id>` for zero-drop send→wait). Do not wrap in an outer shell loop. See [Agent Wait Patterns §1](agent-wait-patterns.md#1-the-canonical-idiom) |
Expand Down
Loading