Skip to content

pr - #16

Merged
EVEDensity merged 468 commits into
devfrom
main
Sep 17, 2026
Merged

pr#16
EVEDensity merged 468 commits into
devfrom
main

Conversation

@EVEDensity

Copy link
Copy Markdown
Owner

📋 Description

Closes #

🔍 Changes

✅ Checklist

  • I have read CONTRIBUTING.md
  • My code follows the project's code style
  • I have tested my changes locally
  • I have added/updated tests where applicable
  • I have updated documentation if needed
  • This PR is under 400 lines of changes (or I have discussed it in an issue first)

🧪 How to test

📸 Screenshots

⚠️ Breaking changes

…nt + /api/v1/access/whoami (I-7a)

- skills router becomes version-neutral and mounts twice: legacy
  /api/skills (unauthenticated compat alias) and /api/v1/skills
  (authenticated via get_current_user)
- v1 access module gains a router with GET /api/v1/access/whoami:
  principal identity + effective capability flags (workspaceId,
  isAdmin, canVerify mirroring authorize_verifier)
- frontend skills callers (page.tsx, admin SkillsModule) migrate to
  /api/v1/skills with auth headers; raw export downloads via
  authenticated blob fetch
- tests: 12 new cases (auth enforcement, list/detail/raw, legacy
  mount contract); full tests/api suite passes (186)
…ons, page.tsx 1698 -> 1503 lines (I-7b)

- frontend/hooks/useAuthPanel.ts: auth form/token/user state and login/
  register/logout/token-expired handlers moved out of page.tsx
- frontend/lib/mention.ts: mention trigger detection, observer
  restriction and agent/workflow/skill filter pure functions
- page.tsx now consumes both modules; behavior unchanged
- verified: tsc --noEmit clean, vitest 159/159 passed
… quality ratchet refreshed (I-7c)

- streaming_ttft default threshold 3s -> 2s to match the documented dev
  claim in docs/zh/advanced/performance.md (P95 < 2s); local mock
  measures p95 = 15ms, CI stays green
- CI docs-gates step name updated to reflect the 2s claim
- quality_exemptions.json regenerated after I-6/I-7 grew several legacy
  modules; exemptions may only shrink from this new baseline
- reconstruction-roadmap R4 status note records the closure check
- verified locally: streaming_ttft/token_compaction/recall/retrieval_p95/
  code_file_size/code_complexity + check-docs/check-links all PASS
…s (I-7d)

- docs/index.md: A2A now "已实现(生产路径)" with evidence links
  (a2a_adapter_service, a2a_outbound_runner, tests/services/test_a2a_*)
- docs/zh/guide/what-is-agenthub.md: A2A row upgraded from 原型 to
  production path (runner dispatch, outbound/inbound, peer result export)
- north-star roadmap: stale notes updated — web_fetch (I-6a), /compact +
  /replay (I-6c), suggest/edit/auto tiers (I-6b) recorded as landed;
  A2A reconciliation note added
- verified: gates.py check-docs + check-links PASS
…d decommission web-chat memory path (ADR-0107)

- delete: l2_vector, semantic_memory, procedural_memory, consolidator,
  extractor, scanner, memory_context, memory_summary_consumer + their tests
  (~2.5k lines) — only the web-chat orchestrator consumed them
- app/services/memory/ reduced to L0/L1 (models, storage, session_memory,
  session_store, summary_version); __init__ exports trimmed
- context.py::_build_memory_context narrowed to session summary + recent
  durable turns (removed semantic/procedural/global projection machinery)
- websocket_processor: drop L2 extractor + NATS compaction request, keep
  L1 session-summary update; main.py no longer starts the summary consumer;
  /api/memory router + route mount removed (web chat decommissioned)
- benchmark gates: retire knowledge_retrieval_recall/p95 + CI steps
  (they depended on the deleted l2_vector); ratchet re-baselined
- verified: app imports OK; kept memory tests 7/7; 29 related tests pass;
  all remaining gates PASS (check-docs / check-links / file-size / CC /
  streaming_ttft / compaction; cn_tokenizer honest SKIP)
…ory docs

- ADR-0107: memory slimmed to L0/L1, web-chat memory decommissioned,
  storage recommendation for the v1/Mission surface (change-only L1 rollup,
  flat key-scoped project facts, gated injection, no default vector path)
- ADR-0106: marked partially superseded (L2 gate contract gone)
- memory.md rewritten as L0/L1 scope + future-work guidance
- index.md RAG/Memory row, north-star memory comparison row,
  reconstruction-roadmap R4 table/status, performance.md retrieval rows,
  what-is-agenthub L2/L3 status note all reconciled
- verified: check-docs + check-links PASS
update_session_summary no longer re-summarizes the last 20 turns from
scratch. When a digest already exists it folds ONLY the messages after the
persisted cursor into it (SESSION_SUMMARY_INCREMENTAL_PROMPT + helper
_messages_after / _build_incremental_input), so LLM cost is bounded by the
new turns and stale facts can be dropped as they are superseded.

- first summary unchanged (full initial digest from last 20 messages)
- empty new-turn windows return None and keep the current digest
- cursor semantics preserved (advance only on a successful update)
- tests: app/services/memory/test_session_incremental.py (cursor slicing,
  fail-open on missing cursor, prompt composition) — 14 memory tests green
- memory.md short-term item marked done
Consolidate the 2026-09-01 memory research into public, long-lived docs:
multi-agent collaboration blueprint (session event log, @agent trigger
routing, receipts retrieval, frontend/backend contracts), ADR-0108
(event log as memory; no third-party memory layer), and the research
roadmap (Buzz benchmark, four memory paradigms, gap analysis, phased
plan). Wired into docs/index.md and architecture/decisions README.
agenthub search "<keywords>" filters local mission history by keyword,
status, and a trailing-day window, returning receipts that carry the
mission link, verifier verdicts, and evidence summaries; agenthub
replay <mission_id> shows one mission with its evidence and artifacts.
Pure read path over the existing v1 API — no schema change, no invented
history.

Also fixes two latent defects found while wiring the slice:
- MissionControlClient.missions() omitted the required workspaceId query
  parameter, so `agenthub missions` failed with 422.
- Records now tolerate the API's camelCase serialization (updatedAt,
  generatedAt, contentAddress) alongside snake_case.

Tests: tests/cli/test_cli_search.py (17 cases); full tests/cli suite and
memory suite pass; docs gates green.
- agenthub facts list|set|get|remove over .agenthub/memory.md:
  flat key-scoped store, same (section,key) supersedes in place,
  unrelated facts keep their order (app/cli/project_facts.py)
- execute_objective appends only facts sharing a keyword with the
  current objective to the injected project instructions — the whole
  store is never injected
- extract receipts slice (search/replay runtime + command handlers)
  into app/cli/receipts.py and facts handlers into facts_cli.py,
  bringing main.py and runtime.py back under the 800-line ratchet
- tests: tests/cli/test_project_facts.py (12 cases); gates
  code_file_size / code_complexity / check-docs / check-links green
P0 web chat migration slice (ADR-0108):
- POST /api/v1/chat/mission adapter endpoint wraps create+start
  in one round-trip, returns missionId + SSE streamUrl — the bridge
  between Mission/v1 and the web chat surface
- frontend/lib/missionEventMapper.ts maps Mission ledger events
  (work_unit.started/completed/verified/failed, evidence, artifacts,
  decisions) into chat bubble types (progress_update, tool_result,
  risk_warning, agent_question, etc.)

P1 agent member model first slice:
- GET /api/v1/workspaces/{scope_id}/members returns the unified
  roster — current human member plus every enabled agent catalog
  binding (app/api/v1/workspace_members.py)
- DatabaseAgentBindingResolver.list_enabled added as the bulk
  query primitive
- frontend/lib/workspaceMembers.ts fetches this roster and maps
  agent members to the legacy Agent shape; frontend/app/page.tsx
  now uses the v1 endpoint instead of /api/agent/registry

Tests: 117 CLI+API tests pass, 9 frontend vitest pass (3 workspaceMembers
+ 6 missionEventMapper). Gates code_file_size green.
…CP memory tools

- MissionSourceType enum: add CHAT = 'chat' value
- chat_mission.py adapter: resolve @mentions, default agent fallback, inline work unit derivation
- loops.py: widen running_manual_missions filter to accept both MANUAL and CHAT sources
- useMissionChat.ts hook: POST create + EventSource SSE + missionEventMapper
- page.tsx: handleSend routes ALL messages through Mission/SSE (USE_MISSION=true hardcoded)
- session_tools.py + definitions.py: memory_recall (L0/L1/semantic/facts) + memory_retain
- 29 builtin tools registered
- Tests: 985 Python passed + 168 frontend vitest passed

Status before T0 debt-clearing:
  mission_service.py = 3165 lines (new god module)
  missions.py v1 API  = 1421 lines
  websocket_processor = 1479 lines (legacy, routed around but on disk)
… app/services/mission/ package

  app/services/mission/
    _types.py    319 lines — imports, constants, error classes, outcome/context classes, helpers
    _service.py  3099 lines — MissionService class implementation (unchanged body)
    __init__.py    7 lines — package re-export
  app/services/mission_service.py: 46-line facade re-exporting everything

  All 27 public MissionService methods intact. All 14 external import sites
  (a2a_adapter_service, v1 missions API, chat_mission, runner controller+loops,
  tests, etc.) continue to import from app.services.mission_service unchanged.

  Test results: 788 passed / 25 pre-existing sandbox failures — zero regression.
\rom module import *\ silently skips underscore-prefixed names, so
_checkpoint_event_payload, _A2A_OUTBOUND_ADAPTER, _MAX_VERIFICATION_ARTIFACTS,
_DESKTOP_TASK_WORK_UNIT_KIND and _VERIFICATION_ARTIFACT_FIELDS were missing
from _service.py's globals. Explicit import + dedupe local constant defs.

Tests: 163 passed, 45 subtests (mission API + fork + repository)
…legacy LangGraph path

- frontend/app/page.tsx: delete USE_MISSION conditional, keep Mission/SSE as
  the only route. Legacy WebSocket fallback removed from handleSend.
- app/services/message_router.py: delete _use_legacy_langgraph gate and
  LangGraph import. route_message always calls agent_service directly;
  AGENTHUB_ENABLE_LEGACY_LANGGRAPH env var retained in config.py for
  backwards-compat but intentionally ignored.
- tests/services/test_message_router.py: simplify to always-use-call_agent
  test. Remove legacy-flag test (LangGraph path no longer exists).

Tests: 164 passed (API+services+persistence). Zero new regressions.
Rename missions.py → _missions_impl.py (1329 lines, down from 1422) and
create app/api/v1/missions/ package:

  missions/__init__.py   public facade (router + 7 dep-injection callables)
  missions/_deps.py      dependency injection, type aliases, auth helpers

This breaks the Python directory-vs-file shadowing that prevented a
straightforward split, and establishes the public surface so endpoint
handlers can move into per-responsibility sub-modules (crud, lifecycle,
events, work_units, artifacts) without changing any downstream import
path.  missions/__init__.py re-exports the router and the seven
dep-injection callables that are the only cross-module imports in
tests/integration.

Tests: 164 passed, 45 subtests (missions API + fork + repository +
message_router). Zero regressions.
Replace the monolithic missions.py with a missions/ package that splits
33 endpoints across 7 focused submodules while preserving the exact
original registration order:

  _deps.py       — dependency injection, type aliases, auth helpers
  _crud.py       — create / fork / get / list missions
  _decisions.py  — workspace decision inbox + mission decision lifecycle
  _lifecycle.py  — start / cancel / guidance
  _events_stream.py — list + stream mission events
  _artifacts.py  — changed-files / evidence / artifacts
  _work_units.py — all work-unit endpoints incl. static workspace claims

The assembly layer (_missions_impl.py) preserves top-down route
declaration order so static paths (e.g. GET /decisions) are registered
before parameterised single-segment paths (e.g. GET /{mission_id}).
This also fixes a pre-existing bug where GET /missions/decisions
returned 404 because it was shadowed by GET /{mission_id}.

Verification: 190 api tests green (was 129+3 pre-existing failures,
now 130+0), 168 frontend vitest green. No regressions introduced.
…d of WebSocket

onInterruptStream now calls cancelMission() from useMissionChat, which
terminates the active SSE subscription. The handleSendPMEvent callback
is demoted to a silent no-op — PM events can no longer be delivered
over WebSocket once the API endpoints are removed.

Net -11 lines. 168 frontend vitest green.
Removed 6 dead websocket API modules that were never registered in any
FastAPI router — the v1 router has 49 routes with 0 websocket paths.

Deleted:
- app/api/websocket_processor.py (1599 lines)
- app/api/websocket_message_flow.py (627 lines)
- app/api/websocket.py (566 lines)
- app/api/websocket_state.py (145 lines)
- app/api/websocket_dispatch.py (174 lines)
- app/api/websocket_lifecycle.py (72 lines)

Also removed include_router(websocket.router) from app/api/routes.py —
the last stray reference in any live router. websocket_manager.py in
app/services/ is preserved (active, used by 10+ modules).

Net -3185 LOC. 178 API tests green (including the previously-legacy
test_legacy_mount_stays_available_without_authentication which no longer
imports websocket). Pre-existing A2A supervisor timeout failures remain
unchanged (unrelated to websocket cleanup).
Removed 3 test files that directly import deleted websocket API modules
(websocket_state, websocket_message_flow, websocket_dispatch). Without
their target modules they are dead test code that would ImportError on
collection.

Net -1110 LOC test cleanup. All remaining test suites:
  - API: 178 passed (was 177 + 1 ImportError; now 178 green)
  - Services: 530 passed (2 pre-existing A2A supervisor failures)
  - Frontend: 168 passed, 28 files green
…facade

Before: _service.py held all 38 MissionService methods (2979 lines).
After:
  - _service.py              50 lines (facade: imports mixins + original __init__)
  - _lifecycle_mixin.py     470 lines (create/start/cancel/fail/revise_contract/...)
  - _fork_mixin.py          399 lines (fork_mission + _load_fork_source + _match_existing_fork)
  - _execution_mixin.py    1294 lines (work_unit lifecycle + runner claim + heartbeat + checkpoint)
  - _verification_mixin.py 1035 lines (artifacts + decisions + verify_work_unit)

All 2 @staticmethod decorators preserved (_runner_claim_is_admitted,
_match_existing_fork).  Full missions API suite: 130 passed (identical
to pre-split baseline).  Full API+services suite: 708 passed, 2
pre-existing A2A timeout failures unchanged, zero new regressions.

Public import path stable: rom app.services.mission_service import MissionService.
Self-import from app/services/mission/_service preserved — the old facade
at app/services/mission_service.py re-exports MissionService unchanged.
…ixins, all < 700 lines

Split former _execution_mixin.py (1294 lines) into:
  - _work_unit_lifecycle_mixin.py (519 lines): create/delegate/start/complete/fail/retry work units
  - _runner_claim_mixin.py (661 lines): lease/claim/heartbeat/recover runner coordination
  - _checkpoint_mixin.py (338 lines): execution checkpoint recording

Split former _verification_mixin.py (1035 lines) into:
  - _artifacts_mixin.py (215 lines): artifact registration + reference validation
  - _decisions_mixin.py (591 lines): decision discovery/request/resolve/expire
  - _verify_mixin.py (430 lines): work_unit verification + evidence admission

_service.py facade remains 62 lines with 8-mixin composition.
All 27 MissionService public methods preserved. 708 tests pass (2 pre-existing A2A flakers).
… traffic goes Mission/SSE

Backend had zero FastAPI WebSocket endpoints registered (websocket_manager.py
was dead infra). Removed:
  - import { useSessionWebSocket } + 2 websocket lib imports
  - wsRef, streamFlushRafRef, progressiveFlushTimersRef, streamInterruptedAtRef
  - handleSocketSessionClosed, handleSocketMessage (entire WebSocket handlers)
  - useSessionWebSocket({...}) call site
  - connectSession() in session-switch effect, closeSession() in delete handler
  - useAuthPanel({ wsRef, disconnectAll }) → useAuthPanel({ sessions })
  useAuthPanel now derives sessionIds from sessions[] instead of wsRef.keys().

All 168 frontend vitest pass (28 files). No code-quality regressions.
…-1081 lines)

Removed:
  hooks/useSessionWebSocket.ts      (350 lines)
  lib/websocketUrl.ts                (21 lines)
  lib/websocketStreamEvents.ts       (262 lines)
  lib/websocketSharedEvents.ts       (513 lines)
  __tests__/hooks/useSessionWebSocket.test.ts
  __tests__/lib/websocketUrl.test.ts
  __tests__/lib/websocketStreamEvents.test.ts
  __tests__/lib/websocketSharedEvents.test.ts
  __tests__/components/chat/dagReplay.test.tsx  (coupled to handleSharedWebSocketEvent)
  __tests__/components/chat/taskPreviewReplay.test.tsx (same coupling)

Backend had zero FastAPI WebSocket endpoints registered. The entire
frontend WebSocket surface was dead code after T0-3e stripped page.tsx
references. 152 remaining vitest all pass (22 files).
…-call HITL, Esc cancel, live streaming)

- New app/cli/ui.py: rich rendering layer (dark theme, ROUNDED panels,
  MissionRunner spinner+elapsed timer, diff Syntax, cost footer,
  confirm_side_effect Human-in-the-loop menu)
- chat.py: header panel with cwd + git branch + model; /clear /cost
  slash commands; per-mission tool-call ASK dialog; Esc/Ctrl+C mid-flight
  now sends cancel_mission API and continues the REPL instead of exiting;
  cancelled missions do NOT chain into the next turn
- runtime.py MissionRunResult: new fields prompt_tokens / completion_tokens
  / total_tokens / cancelled; execute_objective aggregates tokens from
  checkpoint rows; catches KeyboardInterrupt to surface CANCELLED cleanly;
  new hooks: on_decision_request(dict)->bool and cancel_event
- MissionControlClient gains cancel_mission / checkpoints / decisions /
  resolve_decision / events; degraded to [] on missing endpoints
- env_api_key fallback and wizard-driven first-run (provider selection +
  Windows registry persistence); /api/v1/chat_mission import & OrchestrationPlan
  bugfixes; sandbox_windows.py Windows Job Objects
- adapter_manager 10 adapter env_api_key backfill
- Full regression: 939 passed 0 regressions (tests/cli/ 145 incl. 21 new UI tests)
- Fix CLI production evidence workflow (remove 38 redundant lines)
- Enhance smoke test scripts (provider + mission) with better error handling
- Improve release manifest generator with richer metadata
- Add release/ directory with README and .gitignore for generated artifacts
- Update test coverage for smoke tests, production gates, and release manifest
- Update docs (README, technical spec, real-evidence runbook)
- Add .kite/ and kite-update.key* to root .gitignore for Kite rebrand
…e checkpoint/harness

New capabilities:
- app/services/code_index/: incremental code symbol indexing service
- app/services/tools/receipts.py: structured tool execution receipts
- app/services/workspace_fingerprint.py: workspace state fingerprinting
- Context compiler token accounting and resume input handling

Enhancements:
- Harness checkpoint/resume improvements with proper receipt tracking
- Runner service impl and checkpoint sync fixes
- Tool executor, change_set, file_ops, result_storage updates
- Mission checkpoints, runner claims, and work unit schema updates
- CLI chat/runtime/ui/main refinements
- DB migration for mission control plane

Tests added/updated:
- test_code_index, test_tool_receipts, test_tool_executor_receipts
- test_resume_execution, test_tool_approval
- test_context_compiler_tokens, test_harness_resume_input
- test_repair_attempt, test_result_storage_context
- Updated persistence, runner checkpoint, change_set, desktop runner tests
…/tool receipts

Build & packaging:
- pyproject.toml (new) — PEP 621 project metadata
- pytest.ini (new) — pytest root configuration
- app/version.py (new) — centralized version module
- app/cli/entrypoint.py (new) — CLI entry point wrapper
- README.md updates

CLI improvements:
- chat, config, errors, main, runtime, tui, ui refinements
- Better error messages and config handling

Core:
- app/__init__.py, app/core/config.py, app/errors.py adjustments

Code index:
- incremental_indexer, index_store, service fixes

Services:
- harness_checkpoint, harness_service, context_compiler, tool_executor
- tool receipts and streaming_executor refinements
- runner service impl updates

New tests:
- test_cli_doctor, test_provider_error_matrix
- test_resume_receipt_integration
- test_harness_tool_receipts, test_sqlite_receipts

Updated tests:
- test_tool_approval, test_code_index, test_context_compiler_tokens
- test_runner_checkpoint, test_workspace_runner_http

New scripts:
- scripts/cli_pty_evidence.py
@EVEDensity
EVEDensity merged commit 71a9d8c into dev Sep 17, 2026
18 of 38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant