25 Commits
Author SHA1 Message Date
showtan001 60ee157e97 feat: preserve planner context across task steps
Tests / Test apps.device-host-agent.tests.test_mcp_token.test_load_or_create_concurrent_calls_do_not_corrupt failed
2026-08-30 22:59:19 +08:00
q792602257 b0932dd398 feat(cloud): skip MCP-busy devices in scheduler 2026-07-21 14:37:21 +08:00
q792602257 9e3007e7f6 feat(cloud): accept mcp_busy_device_ids in heartbeat payload 2026-07-21 14:32:44 +08:00
q792602257 bd6b7e64e2 Merge branch 'worktree-task-cancellation': task cancellation feature
Tests / Test passed: 926
# Conflicts:
#	packages/cloud-platform/cloud/schema.py
2026-07-15 19:39:28 +08:00
q792602257 18f053e64b Add Host Agent local console task cancellation (task-cancellation 7.1-7.3)
- New internal API route POST /internal/v1/hosts/{host_id}/tasks/{task_id}/cancel,
  authenticated via the host's own bearer credential (authorize_host) with an
  ownership check, since host tokens carry no scopes and cannot reach the
  public SDK's tasks:submit-scoped cancel endpoint.
- HostAgentClient.cancel_task() calls the new internal route directly.
- create_console_app() gains a cancel_task callable with automatic default
  wiring from host_client, so production app.py needs no changes.
- Local console: POST /tasks/{task_id}/cancel route resolves the local
  execution id to its Cloud source_task_id before cancelling, and the task
  detail page/template show a Cancel button plus notice/error banners.
- Tests across all three layers: internal API route, Jinja2 template
  rendering, and FastAPI console route behavior.
2026-07-15 19:13:40 +08:00
q792602257 d69be48f96 feat(planner): persist reusable action semantics
Tests / Test passed: 879
2026-07-15 18:14:28 +08:00
q792602257 8a0d48eada feat(cloud): surface cancellation over the internal Host<->Cloud protocol
- LeaseRenewalResponse gains cancel_requested (populated from the
  repository's renew_lease result)
- TerminalResultRequest.status widened to accept "cancelled"
- Add internal API tests for a renewal surfacing cancel_requested=True
  and a cancelled terminal report being accepted/idempotent
2026-07-15 18:13:43 +08:00
q792602257 a5aeb8889c feat(runtime): add planner reflection history with rationale and thinking
Tests / Test failed: 2, passed: 849
- ToolCallDecision captures thinking blocks and pre-tool text output
- AnthropicToolCallingClient supports optional extended thinking (budget_tokens + beta header)
- PlannedStep carries rationale and thinking from each LLM decision
- WorldEvent replaces scene_summary with rationale/thinking/page fields (backward-compatible)
- AI planner system prompt instructs reflection before each tool call
- _history_summary() emits compact {page, rationale, action, success} dicts
- Cloud DB migration 0011 adds nullable rationale/thinking columns to planner_decision_log
- OpenAI client extracts reasoning_content into thinking field
2026-07-15 12:43:22 +08:00
q792602257 778af2da53 fix(cloud): align planner configuration and records
Tests / Test passed: 856
2026-07-15 09:43:14 +08:00
q792602257 25ebc10a8a feat: downgrade Python baseline to 3.13 for PaddleOCR compatibility
Tests / Test passed: 789
paddlepaddle has no Python 3.14 (cp314) wheel on PyPI, so host-agent
deployments on 3.14 can never install it, causing OCR to fail at
runtime with RuntimeError. Pin the workspace to Python 3.13 across
all pyproject.toml files, the Docker base image, and the Jenkins CI
image; regenerate uv.lock against 3.13.

Also fixes a pre-existing Python-2-style `except X, Y:` syntax error
(invalid in all Python 3.x) in runtime/task.py and
packages/cloud-platform/cloud/{sql_repository,internal_api/api}.py,
introduced in 22d37ca9 and unrelated to this change's scope, which
blocked the full test suite from collecting on any interpreter
version.

openspec change: downgrade-python-3-13-paddleocr
2026-07-14 18:05:49 +08:00
q792602257andClaude Opus 4.6 ec261d57c2 feat: surface task execution progress across Host Agent and Cloud
Host Agent now persists step-level execution detail locally (via a real
TaskMetadataStore/Timeline wired into TaskRunner) and reports a bounded
in-progress snapshot piggybacked on lease renewal. Cloud persists that
snapshot per active assignment and exposes it through the existing task
list/detail query path; Cloud Console renders it as a live badge. Host
Agent's local console gains authenticated, read-only task list and
detail/timeline pages (same-origin, server-rendered) with inlined
screenshots.

Also fixes a pre-existing gap in the shared Timeline: the actual
per-step LLM prompt is now recorded instead of the task goal, benefiting
both Runtime and Host Agent consoles. When a host uses the cloud planner
transport, each decide call's prompt and resulting tool decision are
durably logged in a new planner_decision_log table (with bounded
retention) and browsable from Cloud Console; direct-transport hosts
explicitly surface a "not reported" state.

Includes Alembic migrations 0008 (progress columns on scheduled_tasks)
and 0009 (planner_decision_log), bounded Host-Agent-local retention,
dual-backend repository parity, and Vitest + pytest coverage. Task 6.5
(manual end-to-end device verification) remains.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-14 12:47:49 +08:00
q792602257 a166ffd8a4 feat(cloud): manage LLM providers in database
Tests / Test passed: 664
2026-07-14 00:31:47 +08:00
q792602257 5b64efab53 feat(cloud): report planner transport status 2026-07-13 23:15:46 +08:00
q792602257 40efa53411 feat(cloud): attribute planner token usage 2026-07-13 23:10:21 +08:00
q792602257 b4803f90e6 feat(cloud): enforce host governance budgets
Tests / Test passed: 662
2026-07-13 22:56:31 +08:00
q792602257 2cd314b183 feat(cloud): add targeted task governance foundation
Tests / Test passed: 659
2026-07-13 22:21:12 +08:00
q792602257 a68f609453 Implement cloud-planner-proxy: AI planner routes through Cloud API
Implements all 19 tasks of the cloud-planner-proxy OpenSpec change:

- Cloud API: cloud.planner_config (CloudPlannerConfig, load/build helpers)
  reusing runtime.tool_calling_client provider clients (no new dependency
  needed -- device-cloud-platform already depends on device-agent-runtime).
- Cloud API: new host-scoped POST /internal/v1/hosts/{host_id}/planner/decide
  internal endpoint, reusing existing bearer auth; logs only metadata
  (host id, tool name, latency, error class), never prompt/screenshot
  content.
- Host Agent: new AI_PLANNER_TRANSPORT config (direct default | cloud) and
  host_agent/cloud_planner_client.py::CloudProxyToolCallingClient, a
  synchronous ToolCallingClient implementation (structural, not importing
  runtime) that calls the new endpoint via its own httpx.Client -- avoids
  bridging the async HostAgentClient across the worker-thread boundary
  that AIPlanner.plan() runs in (asyncio.to_thread in lease.py).
- Host Agent wiring: create_execution_factories()/_host_agent_planner()
  select the cloud-proxy client only when AI_PLANNER_TRANSPORT=cloud;
  direct/unset transport is unchanged (still the default).
- Tests: 22 new tests across Cloud API config, the new endpoint, the new
  client, and transport-selection wiring; full non-integration suite
  (492 tests) passes with no regressions.
- Docs: docs/CLOUD_DEPLOYMENT.md documents the cloud transport, its
  trade-offs, and the credential split between Host Agent and Cloud API.

proposal.md/design.md were corrected during implementation to reflect two
findings: no new anthropic/openai dependency is actually needed, and
CloudProxyToolCallingClient uses its own sync httpx.Client rather than a
new HostAgentClient method, per the thread-boundary reasoning above.
2026-07-13 21:27:48 +08:00
q792602257 c162c2501b feat(cloud): remove static credentials and add host console
Tests / Test No test results found
2026-07-13 19:45:53 +08:00
q792602257 efeb3eb926 Implement edge-host-self-enrollment
Tests / Test passed: 581
Host Agent:
- One-time local operator account bootstrap (PBKDF2-HMAC-SHA256, atomic
  0600-permission write) gating the daemon's first unattended start via a
  new `setup` CLI subcommand.
- Default control-plane URL now https://amcp.home.jerryyan.top (env var
  override unchanged).
- Enrollment no longer requires a pre-issued token; falls back to
  zero-token self-service enrollment when none is configured.

Cloud control plane:
- CLOUD_SELF_SERVICE_ENROLLMENT_ENABLED (default false) opt-in flag.
- SelfServiceEnrollmentAuthProvider + ChainedEnrollmentAuthProvider:
  configured tokens still take priority; self-service only applies when
  no token matches, preserving edge-host-enrollment's token-bound path.
- Fixed a latent bug in sql_repository.py::enroll_host: the token-conflict
  lookup used `== enrollment_token_digest`, which SQLAlchemy compiles to
  `IS NULL` when the value is None, so every self-service enrollment after
  the first would have falsely collided with an existing NULL-digest host.
  Skipped that lookup entirely when the digest is None.

Docs/deploy: .env.example, compose.yaml, compose.deploy.yaml,
CLOUD_DEPLOYMENT.md, MACOS_IPHONE_SETUP.md updated for the new flag,
URL default, and required `device-host-agent setup` step.

Verification: 494 non-integration tests pass; openspec validate --strict
passes. PostgreSQL-backed contract tests and full manual end-to-end
verification were not run (no Postgres/Docker or reachable cloud-api in
this environment); noted as unchecked in tasks.md 7.2/7.4.
2026-07-13 18:30:49 +08:00
q792602257 e61dcca801 feat(cloud): add edge host enrollment 2026-07-13 13:54:16 +08:00
q792602257 7b717a05a9 test(openspec): map cloud integration scenarios 2026-07-13 08:21:44 +08:00
q792602257 a24efc1043 feat(host-protocol): renew and complete leases 2026-07-12 18:15:04 +08:00
q792602257 c7f8d0c128 feat(host-protocol): long poll assignments 2026-07-12 18:12:33 +08:00
q792602257 efb754fbe7 feat(host-protocol): accept heartbeat snapshots 2026-07-12 18:10:42 +08:00
q792602257 b4cbd83dcd feat(host-protocol): define internal API models 2026-07-12 18:07:42 +08:00