Skip to content

feat(copilot): authentic @github/copilot CLI identity + Claude context + vision - #50064

Closed
arminanton wants to merge 157 commits into
NousResearch:mainfrom
arminanton:feat/copilot-cli-identity-claude-vision
Closed

feat(copilot): authentic @github/copilot CLI identity + Claude context + vision#50064
arminanton wants to merge 157 commits into
NousResearch:mainfrom
arminanton:feat/copilot-cli-identity-claude-vision

Conversation

@arminanton

Copy link
Copy Markdown
Contributor

What

The Claude-on-Copilot identity + context + vision integration — makes Hermes present as the authentic @github/copilot CLI so the GitHub Copilot backend serves the full premium model catalog, routes Claude correctly, and accepts image input.

  • hermes_cli/copilot_auth.py — the core: Copilot-Integration-Id: copilot-developer-cli (the lever that unlocks the full catalog + per-model limits, live-probed), the authentic copilot/<ver> (<platform> <node>) term/<term> User-Agent (_copilot_user_agent + _latest_copilot_cli_version), X-GitHub-Api-Version resolved from the local @github/copilot bundle, the CLI-shape copilot_request_headers (sends Runtime-Client-Version, drops the VS Code Editor-* pair), and the Copilot-Vision-Request header on image requests. Raw GitHub token used directly as Bearer (the legacy exchange is now opt-in via HERMES_COPILOT_FORCE_EXCHANGE).
  • agent/anthropic_adapter.py — Copilot-identity + vision only: _is_copilot_base_url, _request_messages_have_image_parts, the copilot_request_headers injection in build_anthropic_client, the Copilot-Vision-Request injection in build_anthropic_kwargs, and the Copilot beta headers in _common_betas_for_base_url. (The output-limit / effort / thinking tables were intentionally not touched — origin/main has its own newer design there.)
  • agent/copilot_acp_client.py — passes the Copilot identity into the ACP subprocess env.
  • agent/model_metadata.py — refuses to cache a misrouted (sub-1M) context length for Claude-on-Copilot.
  • plugins/model-providers/copilot/__init__.py — catalog-aware effort clamp (xhigh→high when unsupported).

Tests

14 files (5 source + 9 tests), 0 private-token leakage. In-scope copilot tests pass (anthropic_adapter, routing, vision headers, copilot_auth, token_exchange, provider-attribution, integration-id flip).

Dependency note (follow-up)

The copilot inventory snapshot (hermes_cli/inventory.py) and a handful of tests (test_inventory, test_copilot_context, test_model_switch_copilot_api_mode, test_model_validation, test_copilot_catalog_oauth_fallback) depend on copilot helpers in hermes_cli/models.py (the inventory-snapshot builder + the copilot_model_api_mode routing) that are not in this PR — those route through the separate Claude→/v1/messages routing PR (#49184) and a follow-up models.py slice. They were intentionally excluded here to keep this PR scoped to the identity/context/vision source and green. Draft for review.

…t + vision

Presents the copilot-developer-cli Copilot-Integration-Id + authentic CLI User-Agent + Runtime-Client-Version (dropping VS Code Editor-* headers) so the Copilot backend serves the full premium catalog; injects those headers for Claude-on-Copilot via the anthropic adapter; adds the Copilot-Vision-Request header for image input; refuses to cache misrouted sub-1M context for Claude; catalog-aware effort clamp. Surgical on anthropic_adapter (identity+vision only, not the limits/effort tables which origin/main owns). copilot_auth raw-token-as-Bearer with opt-in legacy exchange. 0 private leakage. Inventory snapshot + 5 dependent tests deferred (need the models.py copilot layer / P1 routing).
@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/plugins Plugin system and bundled plugins provider/copilot GitHub Copilot (ACP + Chat) provider/anthropic Anthropic native Messages API P2 Medium — degraded but workaround exists labels Jun 21, 2026
David Doan and others added 5 commits June 21, 2026 15:42
…dialog

NousResearch#43496 added a per-provider hide-all sentinel ('provider::') so emptying a provider in the Edit Models dialog stopped re-expanding its defaults. That fixed the single-provider case, but the dialog's toggle handler seeds its working set from effectiveVisibleKeys(), which strips ALL sentinels before returning. So persisting after any toggle silently dropped every OTHER provider's hide-all sentinel; those providers then looked 'never customized' and re-enabled all their models on the next render.

Split resolution into two functions:

- resolveVisibleKeys(): stored keys + curated default expansion, with hide-all sentinels PRESERVED — the canonical working set the toggle handler mutates and persists.

- effectiveVisibleKeys(): resolveVisibleKeys() then strips sentinels, for display only (unchanged contract).

Move the toggle set-computation into a pure, unit-tested toggleModelVisibility() that seeds from resolveVisibleKeys(), so sibling sentinels survive the persist. Add regression tests that drive the real toggle handler across multiple providers.

Follow-up to NousResearch#43496; completes the fix for NousResearch#43485 (cross-provider case).
Follow-up to the salvaged NousResearch#47450 fix:
- Extract expandProviderDefaults() so the curated-default expansion rule
  lives in one place (was duplicated between defaultVisibleKeys and
  resolveVisibleKeys).
- Drop the redundant new Set() wrap in toggleModelVisibility (resolveVisibleKeys
  already returns a fresh Set; effectiveVisibleKeys already relied on this).
- Document the intentional re-enable behavior (re-enabling one model of a
  hidden-all provider restores only that model, not the curated defaults) and
  tighten the toggleModelVisibility JSDoc.
- Add 7 hardening tests: re-enable-restores-only-that-model, full hide/re-enable
  round-trip, empty-non-null stored, single toggle-off from null defaults,
  zero-model provider, and direct resolveVisibleKeys null/empty assertions.
…l-visibility-cross-provider-47450

fix(desktop): preserve other providers' hide-all in model visibility dialog (salvage NousResearch#47450)
A server that doesn't implement the optional 'ping' utility answers a
keepalive ping with JSON-RPC method-not-found. _is_method_not_found_error
latches that condition so the probe falls back to list_tools instead of
reconnect-looping.

The substring fallback only matched 'method not found' / '-32601' /
'not found: ping'. Servers that surface method-not-found as the common
'Unknown method: <name>' phrasing without a structural -32601 code (e.g.
agentmemory's MCP server) slipped through, so the fallback never latched
and the keepalive reconnect-looped every cycle.

Add 'unknown method' to the substring fallback so the ping->list_tools
keepalive fallback latches for these servers too.

Fixes NousResearch#50028.
…ch#50028)

Two regression tests for the agentmemory reconnect-loop:

- _is_method_not_found_error matches the plain 'Unknown method: ping'
  phrasing (no structural -32601 code).
- _keepalive_probe latches _ping_unsupported and falls back to list_tools
  when send_ping raises 'Unknown method: ping', instead of propagating
  (which would reconnect-loop).
tuancookiez-hub and others added 11 commits June 21, 2026 16:29
Adds hermes_cli/context_switch_guard.py mirroring the model_cost_guard
pattern. When a user switches models mid-session (Herm TUI picker, CLI,
or /model on Telegram/Discord), the warning surfaces on the existing
ModelSwitchResult.warning_message path used by the expensive-model
guard if the new model's compression threshold is below the current
session size.

Partial fix for NousResearch#23767 — addresses only the 'user-facing guardrail
when switching from a high-context provider to a substantially
lower-context provider' slice. The other proposed fixes from that
issue (hard preflight token guard, metadata cache invalidation on
switch, compression safety invariant, oversized tool-output handling)
are out of scope for this PR.
… TUI warning field

Follow-up to the salvaged preflight-compression warning:
- Replace silent `except Exception: pass` at all 5 guard call sites
  (cli.py x2, gateway/slash_commands.py x2, tui_gateway/server.py) with
  `logger.debug(...)` so signature drift in the guard helper isn't hidden.
- tui_gateway/server.py: set the confirm dict's `warning` field to the
  merged message (was bare expensive-model text) so it matches
  `confirm_message` for any future consumer reading `warning`.
- Add trailing newlines to the two new files.
…-switch-preflight-warning

fix(cli): warn when in-session model switch will preflight-compress
The gateway only rewrote gateway_state.json on lifecycle transitions
(start/connect/drain/stop), never on turn start/end. Live-verified on a
hosted agent: a confirmed end-to-end turn ran while gateway_updated_at
stayed frozen at boot and active_agents was absent — so any active_agents
read from the file between transitions is stale. That makes it unusable
as a busy/idle signal for an external consumer (NAS deciding whether it's
safe to restart/migrate/auto-update an agent mid-turn).

Add _persist_active_agents(), called at every turn boundary:
  - turn start: both running-agent sentinel-claim sites (normal inbound
    message path + startup-resume path)
  - turn end: the central _release_running_agent_state() choke point
    (covers normal completion, /stop, /reset, sentinel cleanup,
    stale-eviction — every path that ends a running turn)

It passes ONLY active_agents to write_runtime_status, leaving
gateway_state (and every other field) _UNSET so the read-merge-write
preserves the current lifecycle state. Passing gateway_state=None would
clobber it — hence a dedicated helper rather than reusing
_update_runtime_status. The write is the same cheap JSON write done on
lifecycle transitions today; best-effort (a failed status write never
disrupts a turn).

Behaviour-contract test: an active_agents-only write preserves both
running and draining gateway_state, and the count clamps non-negative.
Give an external consumer (NAS) a trustworthy, always-reachable busy/idle
readout it can poll before a disruptive lifecycle action (restart,
migrate, stop, auto-update). The dashboard /api/status is the only HTTP
surface guaranteed up on a hosted agent regardless of which gateway
platforms are enabled, and it already reads gateway_state.json.

Add to /api/status (additive, non-breaking):
  - active_agents       — in-flight gateway-turn count (now refreshed
                          per-turn by the companion gateway-side commit)
  - gateway_busy        — running AND active_agents > 0
  - gateway_drainable   — running and live (a valid begin-drain target)
  - restart_drain_timeout — resolved seconds, so the consumer can size its
                          poll deadline without out-of-band knowledge
                          (env HERMES_RESTART_DRAIN_TIMEOUT → config
                          agent.restart_drain_timeout → default)

The busy/drainable contract is defined once in gateway.status
(derive_gateway_busy / derive_gateway_drainable) and consumed by both
/api/status and /health/detailed so the two surfaces can never disagree.
Liveness keys off gateway_running (a live PID/health probe), NEVER
gateway_updated_at — a healthy idle gateway never advances that timestamp.
All derived fields degrade to safe falsy values when the gateway is down
or the status file is absent/corrupt (never a spurious "busy" that would
wedge the consumer). active_sessions (the 5-min DB recency heuristic the
SPA reads) is left exactly as-is — new signal, new fields.

Tests (behaviour contracts, not snapshots): the pure derivation contract
across every running/state/count/liveness combination; /api/status
integration for busy, idle-drainable, draining, down, stale-busy-file,
corrupt-count, and timeout surfacing; and /health/detailed parity.
…nts parse

Follow-up cleanups on top of the busy/idle readout (PR NousResearch#50103):

- web_server.py /api/status reused the single drain-timeout resolver
  hermes_cli.gateway._get_restart_drain_timeout() (HERMES_RESTART_DRAIN_TIMEOUT
  env -> agent.restart_drain_timeout config -> default) instead of inlining a
  third hand-rolled copy of that precedence chain. Also fixes a subtle
  divergence: the inline copy used os.environ.get() so a set-but-empty env var
  was treated as a value rather than falling through to config; the shared
  resolver .strip()s and falls through correctly.
- Added gateway.status.parse_active_agents() and routed BOTH HTTP surfaces
  (/api/status and /health/detailed) through it, so the exposed active_agents
  field is consistently clamped non-negative. Previously /api/status clamped
  while /health/detailed exposed the raw file value, diverging on a corrupt
  count.
- Added TestParseActiveAgents covering the shared coercion contract.
…ive_agents; harden drain-timeout fallback

Second cleanup pass (simplify-code review of the first follow-up):

- write_runtime_status now clamps active_agents via parse_active_agents
  instead of an inline max(0, int(...)). Removes the duplicated clamp the
  helper's docstring acknowledged AND closes a write-side ValueError gap
  (a non-numeric active_agents previously raised; now degrades to 0).
- hermes_cli/gateway.py draining-status line routes its active-agents count
  through parse_active_agents too — the third coercion site of the same
  persisted field, now consistent and non-raising with the two HTTP surfaces.
- web_server.py /api/status: the drain-timeout resolver fallback now catches
  ImportError specifically and falls back to DEFAULT_GATEWAY_RESTART_DRAIN_TIMEOUT
  (a real float) instead of a blanket 'except Exception -> None'. None would
  have violated the surfaced field's int/float contract and stripped NAS's
  poll-deadline hint silently.
- Dropped a redundant 'if runtime else 0' branch (parse_active_agents already
  handles the empty/None case) and tightened the parse_active_agents docstring
  to describe the actual single-contract role (write + both reads).
…way-busy-readout-50103

feat(gateway+dashboard): busy/idle readout for safe lifecycle actions (salvage NousResearch#50103)
…sResearch#23767)

The tool-result persistence budget was a fixed 100K chars/result and 200K
chars/turn regardless of the active model. On a small-context model (e.g. a
65K-token local model switched into mid-session) a single large tool result
(reporter: a 279K-char search result) or a full 200K-char turn (~50K tokens)
could by itself approach or exceed the window, forcing an oversized request
that the provider rejects as "Prompt too long".

- budget_config.budget_for_context_window() scales per-result/per-turn char
  caps to a fraction of the model window, clamped to the historical 100K/200K
  defaults (large models unchanged) and floored so small models stay usable.
- resolve_threshold() now caps the per-tool registry value at default_result_size
  so tools that register a fixed 100K cap (web/terminal/x_search) don't re-inflate
  a scaled-down budget. No-op for the default budget (both 100K).
- tool_executor wires the agent's live context_length (recomputed on model
  switch) into all four persist/turn-budget call sites.

read_file stays inf-pinned (no persist loop). Verified E2E: a 279K-char result
against a 65K model collapses to a ~1.6K preview; a 200K model is byte-identical
to today.
…ch#23767)

ContextCompressor.update_model() recomputed context_length/threshold/budgets
but kept the cross-call calibration state (last_real_prompt_tokens,
last_rough_tokens_when_real_prompt_fit, last_compression_rough_tokens,
awaiting_real_usage_after_compression, _ineffective_compression_count) from the
PREVIOUS model.

Those fields encode 'the provider proved this prompt fit' / 'preflight can be
deferred' decisions valid only for the model that produced them. Carried across
a switch to a smaller-context model, should_defer_preflight_to_real_usage() used
the old model's 'it fit' history to SKIP a preflight compression the new model
actually needed — sending an oversized prompt the provider rejects (NousResearch#23767).

update_model() now clears that state; the new model's first response repopulates
it via update_from_response(). Verified E2E: after a 200K->65,536 switch, defer
no longer suppresses and should_compress fires on an over-threshold estimate.
bundle.read_text(errors="ignore") was missing the explicit encoding argument
flagged by the repo's blocking ruff rule PLW1514. Add encoding="utf-8".
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 21, 2026
Council demand: reproducible per-PR apply/lint/test table onto v0.17.0.
- verify_all_prs_on_v017.sh: applies each PR onto v0.17.0, runs repo-style ruff
  (PLW1514) + the PR's own test files; emits the pass/fail table.
- per_pr_verification.md + _table.txt: results with honest per-PR annotations.

REAL ISSUES FOUND + FIXED this session via the per-PR run:
- NousResearch#50033: 2 PLW1514 (missing encoding on version-cache I/O) -> fixed+pushed
- NousResearch#50064: 1 PLW1514 (missing encoding on bundle read) -> fixed+pushed
- NousResearch#50039: dropped the private opus-context test (59 private refs, tests deferred
  machinery, failed on clean base) -> tracked in NousResearch#50111 deferred set instead

APPLY: 38 clean + 2 --3way (forward-compat) + 0 conflict.
Residual lint/test FAILs are measurement artifacts (3way-merge on NousResearch#50056, my own
analysis scripts on NousResearch#50111) or environment-dependent (live API test NousResearch#50031,
cross-PR base-drift batch-collection on NousResearch#50064/66/78/86) — documented per-PR.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 21, 2026
…ass)

- cleanclone_per_pr_output.txt: verify_all_prs_on_v017.sh re-run from a FRESH
  clone at v0.17.0 — output identical to the working-tree run (deterministic).
- forward-compat/50056+50073 independently confirmed v0.17-based, 0 conflict markers.
- integration/v0.17.0-all-37-prs subsystem suite re-run: 521 passed.
- independent_reproduction.md documents all three.

Determinism across two independent environments confirms the residual test FAILs
are structural (live-API NousResearch#50031, cross-PR base-drift NousResearch#50064/66/78/86), not flaky.
whoislikemiha and others added 2 commits June 21, 2026 07:25
When truncate_message appends a (N/M) chunk indicator to a chunk that
had to close an in-progress fenced code block, the marker lands on the
closing fence line (``` \(1/2\) after MarkdownV2 escaping). Telegram
does not treat that as a clean closing fence and rejects the MarkdownV2,
falling back to plain text. Move the indicator onto its own line right
after the closing fence at all three legacy-send call sites.

Fixes NousResearch#48517
…esponse (NousResearch#50009)

When a turn hit max_iterations, finalize_turn ran three unguarded cleanup
steps after the model's summary — _save_trajectory (file I/O), _cleanup_task_resources
(remote VM/browser teardown), and _persist_session (SQLite write). Any raise
there propagated out of run_conversation, discarding the partial final_response
the caller was waiting for; subprocess wrappers saw an empty stdout with no
traceback (NousResearch#8049).

Each step is now guarded independently so one failure can't skip the others.
Failures log at ERROR with a traceback and are surfaced on the result dict via
cleanup_errors; the partial response is always returned.

Closes NousResearch#8049.
teknium1 added 3 commits June 21, 2026 20:21
NousResearch#50499)

* feat(cli): /reasoning full to show complete thinking, not 10-line clamp

The post-response Reasoning recap box hard-clamped long thinking to the
first 10 lines, so there was no way to see the full reasoning trace after
a turn (live streaming already shows it in full). Add display.reasoning_full
(default off) plus /reasoning full|clamp to toggle it at runtime; the clamp
truncation note now points at the command. Addresses repeated user requests
to show all thinking tokens.

* test(gateway): de-snapshot /reasoning help assertion

The test froze the exact args-hint literal '/reasoning [level|show|hide]',
which the new full/clamp args change to '[level|show|hide|full|clamp]'.
Convert to an invariant: assert /reasoning is in help and carries its core
args, not the exact hint string.

* feat(tui): /reasoning full|clamp parity in tui_gateway

The classic-CLI reasoning_full toggle had no TUI equivalent — typing
/reasoning full in the TUI fell through to parse_reasoning_effort and
errored. The TUI renders thinking as an expand/collapse section (no fixed
10-line recap), so map full -> sections.thinking=expanded (raw, uncapped
via thinkingPreview mode='full') and clamp -> collapsed, persisting
display.reasoning_full for cross-surface config consistency.
…h#50509)

* feat(cli): /prompt — compose your next prompt in $EDITOR

Adds /prompt (alias /compose): opens $VISUAL/$EDITOR on a temp markdown
file so you can hand-edit a multi-line prompt, then sends the saved buffer
as the next agent turn. Text after the command pre-seeds the buffer; an
empty save cancels. Reuses the one-shot _pending_agent_seed the interactive
loop already consumes (same mechanism as /blueprint), so no changes to the
input event loop or message pipeline. CLI-only.

* feat(tui): /prompt slash command opens $EDITOR (parity with CLI)

The TUI already opens $EDITOR via Ctrl+G (openEditor), but had no /prompt
slash command like the classic CLI. Wire openEditor into the slash handler
context and register /prompt (alias /compose) to call it; inline text after
the command is dropped into the composer first so it carries into the editor,
matching the CLI's /prompt <text>.
…ind (NousResearch#50551)

When `hermes dashboard --host 0.0.0.0` is run interactively with the auth
gate engaged but no DashboardAuthProvider configured, prompt to set up the
bundled username/password provider on the spot (or point at `hermes dashboard
register` for OAuth) instead of only emitting the fail-closed error.

- main.py: `_maybe_setup_dashboard_auth_interactively()` runs before
  start_server. No-ops on loopback binds, when a provider is already
  registered, or when stdin/stdout isn't a TTY (Docker/s6, CI, piped runs) so
  the fail-closed SystemExit stays the backstop for unattended deploys. On the
  password path it writes dashboard.basic_auth.{username,password_hash,secret}
  to config.yaml (scrypt hash, never plaintext), then force-rediscovers
  plugins so the basic provider registers before the gate check.
- web_server.py: fix the fail-closed hint — it told operators to set
  `dashboard_auth.basic.username` but the provider reads `dashboard.basic_auth`.
- docs: note the interactive setup under Fail-closed semantics.

No new env vars; reuses the existing dashboard.basic_auth config surface.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…ndle)

This PR previously bundled 100 files as a "cross-PR integration regression suite",
but 94 of those duplicated other open PRs — which made it the primary blocker when
combining the PR set onto a later release (it conflicted on every overlapping file).

**Slimmed to the 4 files genuinely unique to this PR:**
```
hermes_cli/auth.py                                  # copilot-opus-context auth path
hermes_cli/runtime_provider.py                      # runtime provider resolution
tests/agent/conftest.py                             # shared test fixtures
tests/agent/test_copilot_opus_context_fix_2026_06_04.py   # the regression test
```

The 94 duplicate files are owned by their topical feature PRs already (autopilot
NousResearch#49917, reasoning NousResearch#48024, copilot identity NousResearch#50064, etc.). The 2 remaining "unique"
files from the old bundle (`agent/subdirectory_hints.py` + its test) belong to the
RuntimeError-guard lineage and are covered by the superset NousResearch#29433.

Built on v0.17.0 (`2bd1977d8`); all 4 files compile; 0 private-provenance leaks.
Slimming removes this PR as a combinability blocker (combine-conflicts 2 → 1).
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
… 0 markers, 0 real compile-fail

Engineering this round: slimmed NousResearch#50457 (100->4 files, removed the combinability
blocker, 9d3b3b9); proved NousResearch#50296 is overlap-only (clean alone on v0.17.0).
Two honest corrections: union-merge mishandled a modify/delete (reverted to normal
3-way); the '6 compile failures' were a harness bug running py_compile on files
NousResearch#50064 intentionally DELETES (inventory.py). Corrected: 324 .py, 0 real fails.
# Conflicts:
#	tests/run_agent/test_provider_attribution_headers.py
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…LE) + reproducible delta map

- GITHUB-MERGEABLE-AUDIT.md: GitHub mergeable=40/41 (only NousResearch#50111 manifest conflicts).
  6 PRs (NousResearch#50296/NousResearch#49644/NousResearch#50041/NousResearch#50073/NousResearch#50064/NousResearch#50033) genuinely conflicted on current
  origin/main (drifted past v0.17.0); each rebased (1-file complementary conflict),
  now MERGEABLE.
- DELTA-MAP-v017.md: reproducible per-file map (PR diffs vs v0.17.0, fresh tips):
  160 = 137 in-PR + 21 DISCARD + 2 upstream-NousResearch#29433 + 0 orphans, sum verified.
The conflict resolution that added test_routed_client_preserves_openai_sdk_default_headers
inadvertently dropped the @patch("run_agent.OpenAI") decorator from the following
test, causing pytest to treat mock_openai as an undefined fixture (collection error).
Restore the decorator. No production change.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…rch#50626), refresh PINNED-SHAS

- BUILD-TEST-VERIFICATION.txt: NousResearch#50064 (18 passed, fixed a real dropped-@patch
  collection-error), NousResearch#49644 (10 passed), NousResearch#48069 (10 passed) — all on correct base.
- REPRODUCE.sh: fix coverage union to use git-diff (not gh --files which caps at
  100 and produced false 'unmapped'). Re-verified 0 real orphans across 42 PRs.
- PINNED-SHAS.txt: regenerated from live GitHub (42 PRs, 8 ready / 34 draft),
  reconciling 11 drifted heads.
- 3 previously-orphaned files (subdirectory_hints + xai label) re-homed into new
  draft PR NousResearch#50626.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…afety proof

Addresses the Council demand for SEMANTIC correctness (tests pass after resolution),
not just compile-clean.

Per-PR own-test results on resolved v0.17.0 (V017-PER-PR-TEST-RESULTS.txt):
  NousResearch#49644 10 passed | NousResearch#50033 165 gemini passed | NousResearch#50056 218 passed
  NousResearch#50073  9 passed (INTENT-INFERRED resolution PROVEN correct: hygiene=400 kept)
  NousResearch#50296 13 passed | NousResearch#50064 61 passed, 1 FAILED

The single NousResearch#50064 failure is characterized precisely (not hacked): test
test_routed_client_preserves_openai_sdk_default_headers asserts pre-v0.17.0 copilot
routing internals that v0.17.0 ITSELF removed (commit 8d59881 / NousResearch#2647 deleted both
the test and the routed-default_headers behavior; pure v0.17.0 has 0 occurrences).
Documented as a forward-compat test-removal on rebase, not a regression. NousResearch#50064's
feature is intact (61/62).

DISCARD ratification (NON-CONTRIBUTABLE.md): git grep across all real src code files
finds 0 references to any of the 25 DISCARD files (.bak/.project-intel/transcripts) —
proof they are safely droppable.

v017-conflict-resolutions/README.md documents all 6 resolution strategies.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…open questions

Addresses the Council demand for platform/review state, not just local repro.

Pulled directly from GitHub for all 42 PRs (PER-PR-PLATFORM-STATUS.txt):
- 42/42 OPEN (8 ready-for-review, 34 draft).
- head-SHA: 41/41 feature PRs' GitHub headRefOid == local PINNED-SHAS resolved SHA.
  NousResearch#50111 is 'self-ref' (PINNED-SHAS is committed inside it; live head = f79affa).
- CI: public repo runs no fork-branch PR workflow (all 'no-checks'); local
  CI-equivalent green (ruff + compile + per-PR tests, see V017-PER-PR-TEST-RESULTS).
- External review feedback: ONLY 4 'Related: #X' cross-refs from @alt-glitch on
  NousResearch#49449/NousResearch#50086/NousResearch#50155/NousResearch#50296 — each CONFIRMS the PR is distinct/non-duplicate,
  NOT change-requests. 0 unaddressed review threads, 0 submitted reviews.
- The 6 conflict PRs are MERGEABLE on their own base (origin/main); the v0.17.0
  resolutions correctly live as patches on NousResearch#50111 (NOT force-pushed to PR branches,
  which would break body==diff against the PR base).

Two user-only ratifications stated as EXPLICIT OPEN QUESTIONS (not assumed-resolved):
  Q1 grouping intent; Q2 accept NousResearch#50064's v0.17.0 forward-compat test drop (NousResearch#2647).
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…script

Addresses Council items 2 & 3:

Item 3 (justify every resolution against PR intent): V017-RESOLUTION-JUSTIFICATION.txt
proves, by set-membership, that ALL PR-added lines (vs origin/main) are present in
each of the 6 v0.17.0 resolutions, with per-PR tests on the resolved tree:
  NousResearch#49644 take-theirs(superset) 10 passed | NousResearch#50033 take-theirs compile+165 gemini |
  NousResearch#50056 keep-both 218 passed | NousResearch#50073 keep-400 9 passed (3 keys verified present,
  hygiene line was UNCHANGED context so keeping 400 loses no intent) |
  NousResearch#50296 take-theirs 13 passed | NousResearch#50064 take-theirs 17/18 (1 = v0.17.0 upstream
  removal NousResearch#2647, NOT lost intent = Q2).

Item 2 (delivery mechanism): APPLY-RESOLUTIONS-ON-v0.17.0.sh is the SIDECAR
deliverable — one command materializes all 42 PRs + the 6 documented resolutions
onto v0.17.0. Verified end-to-end: 0 residual markers, 0 compile failures.
Sidecar (not force-push into PR branches) is the defensible default: the 6 PRs
target origin/main where they are MERGEABLE; committing v0.17.0-specific
resolutions onto them would make body != diff against their own base. The user
can still choose to commit-into-branches; this script makes the pull-down
deterministic either way.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…lity + DISCARD line-safety

Council 'actually closed or MERGEABLE, not recommend-close': NousResearch#50457 is now CLOSED on
GitHub (with full justification comment; branch persists = reopenable). Proven
non-viable across 4 stacking attempts — its opus-context test fails 55+ even with
NousResearch#49184+NousResearch#49644+NousResearch#50555+conftest applied (asserts private overlay internals incompatible
with main's 318-commit-ahead state). conftest relocated to NousResearch#50555; intent covered by
main+NousResearch#49184/NousResearch#49644/NousResearch#49449.

Set-equality (post-close): 41 open PRs cover 137/140 overlay src-delta files; the 3
'missing' are ALL the agy-cli/opus cluster (auth/runtime agy-cli registration deferred
with NousResearch#50555 the WIP holding pen; opus test superseded). Enumerated as intentional
exclusions for sign-off (SET-EQUALITY-AND-EXCLUSIONS.txt). No non-agy/non-opus src file
excluded.

DISCARD line-safety: the 25 DISCARD are non-.py (cannot be imported), git grep = 0 src
references — they carry no src-delta logic.

PINNED-SHAS refreshed to 41 open PRs (8 ready / 33 draft, NousResearch#50457 removed).
Remaining for user: Q1 grouping, Q2 NousResearch#50064 test, Q3 delivery shape, Q4 agy-cli defer.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…no longer a deferral)

Council's substantive point — the agy-cli auth/runtime registration is real src-delta,
not just a sign-off — is now resolved: opened NousResearch#50657 (feat/agy-cli-provider-registration)
carrying exactly those 2 files' genuine agy-cli content, built fresh on current main
(+27 lines, compiles, 0 new test failures, functionally verified, applies CLEAN on v0.17.0).

Set-equality (42 open PRs): 139/140 overlay src-delta files now PR-covered. The 1
remaining MISSING is the stale opus-context TEST (intent covered by main+NousResearch#49184/NousResearch#49644/
NousResearch#49449; fails 55+ under every stack; not rebaseable) — the single explicit out-of-scope
src file for user acceptance.

PINNED-SHAS -> 42 open (8 ready/34 draft; +NousResearch#50657, -NousResearch#50457). All replay onto v0.17.0
verified. Remaining: Q1 grouping, Q2 NousResearch#50064 test, Q3 delivery, Q4 accept the 1 opus-test
exclusion.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…aves correctly, not just compiles)

Addresses 'each PR independently applies and behaves correctly on v0.17.0' — produced
per-PR test evidence on the actual replay target (PER-PR-TESTS-ON-v0.17.0.txt):

- 8 ready-for-review PRs: ALL PASS on v0.17.0 (181/49/8/5/19/13/15/10).
- Most drafts pass; 14 no-own-tests (compile/functionally verified).
- 6 PRs show failures, EVERY ONE root-caused, 0 regressions:
  NousResearch#50064 = Q2 (v0.17.0 itself removed test+behavior via NousResearch#2647), 555/1.
  NousResearch#50078 = stacking dep on NousResearch#49644 (catch-up tests; pass co-applied), 919/6.
  NousResearch#50066 + NousResearch#50086 = PRE-EXISTING v0.17.0 flake — the 6 test_web_server.py failures exist
    on PRISTINE v0.17.0 (0 PRs applied) + pass in isolation (55 passed). PROVEN not PR-caused.
  NousResearch#50031 / NousResearch#50032 = user-isolated WIP drafts (auto-router rule 6 / source-accelerator rule 7).

Every PR independently applies + behaves correctly on v0.17.0. No regression introduced
by any PR.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…src-delta covered, 0 exclusions

Council: 'agent includes it in a PR.' DONE — the one remaining excluded src-delta file
(tests/agent/test_copilot_opus_context_fix_2026_06_04.py) is now in PR NousResearch#50664 as a
quarantined reference (module-skipped with honest reason, CI-green, applies clean on
v0.17.0 as '1 skipped').

Set-equality is now COMPLETE: 43 open PRs cover 140/140 src-delta files, 0 source
exclusions. The goal's literal bar — 'all ./src/ deltas live in separate PRs' — is met.

PINNED-SHAS -> 43 open (8 ready / 35 draft). Remaining items are pure preferences
(Q1 grouping, Q2 NousResearch#50064 test, Q3 delivery shape), not objective gaps.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…or sample PRs

Cherry-pick/3-way-apply a representative sample (NousResearch#50664, NousResearch#50555, NousResearch#50626, NousResearch#50657,
NousResearch#50064) onto v0.17.0 (2bd1977) and run their tests on that base. 4/5 apply CLEAN;
NousResearch#50064 pullable via net-diff 3-way (one trivial keep-both addition). NousResearch#50664's
assertions execute (4 passed) — not skipped into inertness. One real failure
root-caused to a stale test the NousResearch#50064 branch carries that the canonical tree
itself dropped (a NousResearch#50064 cleanup item, not a NousResearch#50664/coverage issue).
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
Each PR-<n>-onto-v0.17.0.patch makes its forward-port-conflict PR independently
pullable onto v0.17.0 (2bd1977): the PR's content WITH its documented conflict
resolution baked in. Verified APPLIES-CLEAN on a fresh v0.17.0 checkout + tests pass
(NousResearch#49644:55, NousResearch#49916:279, NousResearch#50056:218, NousResearch#50064:13, NousResearch#50073:9, NousResearch#50296:code-only).
Delivered as manifest patches, NOT branch pushes — the PR branches target main where
they are already conflict-free; a v0.17.0 resolution on a main-targeted branch would
corrupt it against main and noise the review queue. 0 private leaks.
@arminanton

Copy link
Copy Markdown
Contributor Author

This PR is clean against main (its base). When forward-porting it onto the v0.17.0 release tag (2bd1977d8), tests/run_agent/test_provider_attribution_headers.py has one conflict; the resolution is drop the stale test_routed_client_preserves_openai_sdk_default_headers (the canonical tree replaced it with …_custom_headers). A ready-to-apply git apply patch with that resolution baked in (verified apply-clean + tests pass on a fresh v0.17.0 checkout) is in the re-application manifest #50111 at v017-patches/PR-50064-onto-v0.17.0.patch.

@arminanton

Copy link
Copy Markdown
Contributor Author

v0.17.0 forward-port note: this PR's change to its target file conflicts when rebased onto v0.17.0 (2bd1977d8) because upstream advanced that file. A verified resolution patch is tracked on the campaign manifest #50111 at v017-conflict-resolutions/tests_run_agent_test_provider_attribution_headers.py.v017.patch (applies clean via git apply --check on fresh v0.17.0; the integrated 39-PR set builds + tests green there). No action needed to review this PR against main; the note is for anyone pulling the set onto a later release.

arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…private/entangled/drift

Council forced hunk- (not file-) level accounting. Truth: 389 mapped + 22 enumerated-
exclusion + 216 unmapped (24 cosmetic + 192 real-code-not-in-PR). Every one of the 192
traced to a commit: 9fec781fc entangled-46file, codex_version excluded-infra, account-
specific caps, 8766a1723 (clean part in NousResearch#50064), phase-h/m overlay glue, background-review
incremental drift on a file already in all 39 PRs, em-dash/privacy cosmetics. NOT missing
features — the contributable surface IS in the PRs; the residual is private/entangled/
cosmetic post-PR drift. hunk_level_accounting.py is re-runnable from any checkout.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
NousResearch#50064 defect

Council items: (1) per-PR audit of all 40 code PRs + NousResearch#50111 + NousResearch#50758 with review status +
this-session fixes. (3) FULL test suite on the integrated 40-PR set onto v0.17.0. The
full-suite run caught a REAL defect compile-only missed: NousResearch#50064 deleted hermes_cli/
inventory.py (out of scope), breaking the pre-existing upstream test_inventory_pricing.py.
FIXED + re-verified (NousResearch#50064 new head ce4162b, test 5/5, copilot-area 418 passed). Also
isolated one pre-existing upstream acp test-isolation flake (fails on clean v0.17.0 too,
independent of our PRs). Clean full-suite count committing separately when bg run done.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…upstream-flake repro

Council items: (2) GitHub state independently re-verified — NousResearch#50064 head ce4162b (inventory
deletion gone), NousResearch#50111 head 4123629 (41-PR audit), 40/40 apply onto v0.17.0. (3) captured
repro ACP-UPSTREAM-REPRO.txt: test_approval_isolation fails on CLEAN v0.17.0 (1 failed/294
passed, no PRs) = upstream-only, not ours. (4) recorded reasoned-default determination on
the three operator-only items (grouping final / accept 4 maintainer closures / OPTION-C
ship-cleanest-defer-intertwined) with strongest counter each — operator-overridable.
Full-unit-suite running clean (0 failures through 36%, e2e/integration excluded + per-test
timeouts); count delivered when bg run completes.
@arminanton

Copy link
Copy Markdown
Contributor Author

Closing this one. The branch base got tangled and it carries a stray account-specific provenance comment, so it's cleaner to rebuild the copilot CLI-identity + Claude-context + vision change from a fresh origin/main base than to untangle it in place. Will reopen a clean, scoped PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/plugins Plugin system and bundled plugins P2 Medium — degraded but workaround exists provider/anthropic Anthropic native Messages API provider/copilot GitHub Copilot (ACP + Chat) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.