feat(integration): prepaid billing, engine UI, credits console, direct tasks, v2 final
- Prepaid credit system: org wallet, debit on usage, chat gate, admin credit console - Engine UI: /manager, /tasks, /tasks/[id], /workforce, /dashboard, /traces pages - Direct task creation (HTTP POST /tasks, MCP tasks_create) at pending_manager_review - Pause/resume/cancel/answer actions in engine API and UI - Employee cap (7 per org default), review pipeline guard (active reviewer required) - Scheduler: wide window prevents org starvation; archived employees excluded from cap - Tenant isolation: project-id cross-org 404, engine never uses another tenant's sandbox - Order route fix: manager built with pipeline states + employee profiles - Contract extension: ModelGrantPort.get_org_policy with DefaultOrgPolicy - Engine event types in @agy/contracts (task.step, task.gate, run.log, run.tool, team.changed) - api-engine.ts typed client; api-credit.ts client - Alembic migration c4d5e6f7a8b9: prepaid credit + engine UI tables - 859 tests passed, 1 skipped; ruff/mypy/tsc/eslint/vitest all green Refs: PLAN-v2.md §9 (T1–T43), todo-v2-final.md
This commit is contained in:
@@ -16,6 +16,7 @@ from app.application.container_engine import build_scheduler
|
||||
from app.infrastructure.db.models import AiModelRow, OrganizationRow
|
||||
from app.infrastructure.repositories.engine import PipelineStateRepository
|
||||
from app.infrastructure.repositories.workforce import TaskRepository
|
||||
from tests.billing_helpers import fund_org
|
||||
from tests.conftest import auth_header
|
||||
from tests.engine_fakes import ScriptedModel, ScriptedReply
|
||||
from tests.test_org_wizard import _complete, _manager
|
||||
@@ -58,6 +59,9 @@ async def test_an_order_in_a_thread_without_a_project_gets_the_orgs_project(
|
||||
org_id = body["organization"]["id"]
|
||||
project_id = body["project"]["id"]
|
||||
|
||||
# prepaid (D-B): the wizard's org starts at zero credit, and an order costs model calls.
|
||||
await fund_org(session_factory, org_id)
|
||||
|
||||
# The web UI's way: a thread with no project_id.
|
||||
created = await client.post(
|
||||
f"/api/organizations/{org_id}/threads", json={"title": "گفتگو"}, headers=headers
|
||||
@@ -66,8 +70,10 @@ async def test_an_order_in_a_thread_without_a_project_gets_the_orgs_project(
|
||||
assert created.json()["project_id"] is None
|
||||
thread_id = created.json()["id"]
|
||||
|
||||
# The order goes to the engine's manager. The HTTP message route builds its manager
|
||||
# without pipeline states (workforce_service._manager_turns), so it cannot show the state.
|
||||
# The order goes to the engine's manager. The HTTP message route builds its manager with
|
||||
# the pipeline states too (workforce_service._manager_turns, covered by
|
||||
# test_integration_order_route.py); this test calls the manager directly only to read the
|
||||
# reply and the states in the same session.
|
||||
async with session_factory() as session:
|
||||
owner_user_id = (await session.get(OrganizationRow, org_id)).owner_user_id
|
||||
_, assistant = await build_scheduler(session).manager.chat(
|
||||
|
||||
Reference in New Issue
Block a user