From 210d7db3383834f172a747ecb71286b5f1f397ab Mon Sep 17 00:00:00 2001 From: ogt Date: Fri, 10 Jul 2026 10:51:18 +0800 Subject: [PATCH] feat(agents): schedule mainstream agent version governance --- .../agent-version-lifecycle-watch.yaml | 114 +++ .../agent_market_discovery_classifier.py | 8 +- apps/api/src/services/agent_market_watch.py | 41 + .../awoooi_priority_work_order_readback.py | 24 + apps/api/tests/test_agent_market_watch.py | 30 +- ...agent_version_lifecycle_update_proposal.py | 18 +- ...t_version_lifecycle_update_proposal_api.py | 20 +- apps/web/messages/en.json | 9 +- apps/web/messages/zh-TW.json | 5 +- .../tabs/automation-inventory-tab.tsx | 25 +- apps/web/src/lib/api-client.ts | 8 + docs/ai/agent-market-watch-sources.v1.json | 204 ++-- docs/ai/agent-replacement-candidates.v1.json | 40 + docs/ai/ai-technology-watch-sources.v1.json | 237 +++-- ..._lifecycle_update_proposal_2026-07-10.json | 922 ++++++++++++++++++ .../agents/agent-market-discovery-classify.py | 53 +- 16 files changed, 1519 insertions(+), 239 deletions(-) create mode 100644 .gitea/workflows/agent-version-lifecycle-watch.yaml create mode 100644 docs/evaluations/ai_agent_version_lifecycle_update_proposal_2026-07-10.json diff --git a/.gitea/workflows/agent-version-lifecycle-watch.yaml b/.gitea/workflows/agent-version-lifecycle-watch.yaml new file mode 100644 index 000000000..90b51a952 --- /dev/null +++ b/.gitea/workflows/agent-version-lifecycle-watch.yaml @@ -0,0 +1,114 @@ +# ============================================================================= +# AWOOOI Agent Version Lifecycle Watch (Gitea Actions) +# ============================================================================= +# Weekly no-write AI Agent / SDK / framework version lifecycle readback. This +# workflow validates the committed proposal queue only; it does not install SDKs, +# call model APIs, write lockfiles, change workflows, send Telegram, switch +# providers, replace OpenClaw, or mutate production. + +name: Agent Version Lifecycle Watch + +on: + workflow_dispatch: + schedule: + - cron: '0 1 * * 2' # 每週二 09:00 台北 (UTC+8) + +jobs: + version-lifecycle-watch: + runs-on: awoooi-non110-ubuntu + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + + - name: Validate no-write version lifecycle proposal + id: lifecycle + run: | + set -euo pipefail + python3 - <<'PY' + import json + import os + from pathlib import Path + + candidates = sorted( + Path("docs/evaluations").glob("ai_agent_version_lifecycle_update_proposal_*.json") + ) + if not candidates: + raise SystemExit("no version lifecycle proposal snapshots found") + latest = candidates[-1] + with latest.open(encoding="utf-8") as handle: + data = json.load(handle) + if data.get("schema_version") != "ai_agent_version_lifecycle_update_proposal_v1": + raise SystemExit("unexpected version lifecycle schema_version") + if (data.get("program_status") or {}).get("read_only_mode") is not True: + raise SystemExit("version lifecycle proposal must stay read_only") + + boundaries = data.get("runtime_boundaries") or {} + forbidden = [ + "schedule_activation_allowed", + "external_market_lookup_allowed", + "external_registry_lookup_allowed", + "external_cve_lookup_allowed", + "package_upgrade_allowed", + "lockfile_write_allowed", + "workflow_write_allowed", + "provider_route_switch_allowed", + "openclaw_replacement_allowed", + "paid_api_call_allowed", + "telegram_direct_send_allowed", + "telegram_gateway_queue_write_allowed", + "production_write_allowed", + ] + unsafe = [key for key in forbidden if boundaries.get(key) is not False] + if unsafe: + raise SystemExit(f"version lifecycle runtime boundary must stay false: {unsafe}") + + rollups = data.get("rollups") or {} + if rollups.get("proposal_count") != len(data.get("update_proposals") or []): + raise SystemExit("proposal_count rollup mismatch") + if rollups.get("cadence_count") != len(data.get("cadence_matrix") or []): + raise SystemExit("cadence_count rollup mismatch") + required_proposals = { + "ai_agent_market_primary_source_radar", + "openclaw_challenger_replay_bench", + "mainstream_agent_framework_version_lifecycle_review", + } + proposal_ids = set(rollups.get("proposal_ids") or []) + missing = sorted(required_proposals - proposal_ids) + if missing: + raise SystemExit(f"missing required AI Agent version lifecycle proposals: {missing}") + + summary = { + "snapshot": str(latest), + "current_task_id": (data.get("program_status") or {}).get("current_task_id"), + "domain_count": rollups.get("domain_count"), + "proposal_count": rollups.get("proposal_count"), + "cadence_count": rollups.get("cadence_count"), + "approval_required_count": rollups.get("approval_required_count"), + "auto_execution_allowed_count": rollups.get("auto_execution_allowed_count"), + "production_write_count": rollups.get("production_write_count"), + "openclaw_replacement_allowed": boundaries.get("openclaw_replacement_allowed"), + } + + output_path = os.environ.get("GITHUB_OUTPUT") + if output_path: + with open(output_path, "a", encoding="utf-8") as handle: + for key, value in summary.items(): + handle.write(f"{key}={value}\n") + + step_summary_path = os.environ.get("GITHUB_STEP_SUMMARY") + if step_summary_path: + with open(step_summary_path, "a", encoding="utf-8") as handle: + handle.write("## Agent Version Lifecycle Watch\n\n") + handle.write(f"- Snapshot: {summary['snapshot']}\n") + handle.write(f"- Current task: {summary['current_task_id']}\n") + handle.write(f"- Domains: {summary['domain_count']}\n") + handle.write(f"- Proposals: {summary['proposal_count']}\n") + handle.write(f"- Cadences: {summary['cadence_count']}\n") + handle.write(f"- Approval-required proposals: {summary['approval_required_count']}\n") + handle.write(f"- Auto executions approved: {summary['auto_execution_allowed_count']}\n") + handle.write(f"- Production writes approved: {summary['production_write_count']}\n") + handle.write(f"- OpenClaw replacement allowed: {summary['openclaw_replacement_allowed']}\n") + handle.write("\nPolicy: no-write proposal validation only.\n") + + print(json.dumps(summary, ensure_ascii=False, sort_keys=True)) + PY diff --git a/apps/api/src/services/agent_market_discovery_classifier.py b/apps/api/src/services/agent_market_discovery_classifier.py index ec5763792..bb5443ff2 100644 --- a/apps/api/src/services/agent_market_discovery_classifier.py +++ b/apps/api/src/services/agent_market_discovery_classifier.py @@ -2,10 +2,10 @@ Agent market discovery classifier ================================= -Classifies AI controlled discovery repositories from primary GitHub +Classifies AI controlled discovery candidates from supplied primary-source metadata. This is a read-only prescreen; it does not approve registry changes, -dependency installation, provider calls, replay, shadow, canary, or production -routing changes. +dependency installation, provider calls, replay, shadow, canary, production +routing changes, or GitHub live metadata reads. """ from __future__ import annotations @@ -37,7 +37,7 @@ def run_agent_market_discovery_classification( "generated_at": generated_at or datetime.now(timezone.utc).isoformat(), # noqa: UP017 "inputs": { "discovery_review_generated_at": discovery_review.get("generated_at"), - "metadata_source": "github_repository_api_summary", + "metadata_source": "supplied_primary_source_metadata_or_policy_blocked_stub", }, "policy": { "auto_watch_registry_addition_approved": False, diff --git a/apps/api/src/services/agent_market_watch.py b/apps/api/src/services/agent_market_watch.py index 7705ffd53..2a5d07532 100644 --- a/apps/api/src/services/agent_market_watch.py +++ b/apps/api/src/services/agent_market_watch.py @@ -85,6 +85,12 @@ def run_agent_market_watch( changed_candidates = sum(1 for candidate in candidates if candidate["changed"]) watch_only_candidates = sum(1 for candidate in candidates if not candidate["changed"]) + policy_skipped_sources = sum( + 1 + for candidate in candidates + for source in candidate.get("sources") or [] + if source.get("status") == "skipped_by_policy" + ) return { "schema_version": "agent_market_watch_report_v1", @@ -104,6 +110,7 @@ def run_agent_market_watch( "watch_only_candidates": watch_only_candidates, "integration_queue_count": len(integration_queue), "failure_count": len(failures), + "policy_skipped_source_count": policy_skipped_sources, }, "candidates": candidates, "integration_queue": integration_queue, @@ -232,6 +239,21 @@ def _evaluate_source( "reference_version": reference_version, "error": None, } + if _is_policy_blocked_source(source_type, url): + return { + "source_id": source_id, + "type": source_type, + "url": url, + "status": "skipped_by_policy", + "http_status": None, + "version": reference_version, + "published_at": None, + "content_hash": None, + "changed_since_reference": False, + "reference_version": reference_version, + "error": None, + "policy_blocker": "github_global_freeze_no_github_api_or_github_web_reads", + } fetched = fetcher(url, timeout_seconds) previous = previous_sources.get((candidate_id, source_id), {}) @@ -285,6 +307,13 @@ def _is_github_rate_limited(url: str, fetched: FetchedSource) -> bool: return "rate limit" in body or "api rate limit exceeded" in body +def _is_policy_blocked_source(source_type: str, url: str) -> bool: + if source_type in {"github_release", "github_tags", "github_search"}: + return True + host = urlparse(url).netloc.lower() + return host in {"github.com", "www.github.com", "api.github.com"} + + def _parse_source(source_type: str, body: bytes) -> dict[str, str | None]: if source_type == "pypi": payload = _loads_json(body) @@ -335,7 +364,19 @@ def _fetch_discovery_source( timeout_seconds: int, ) -> dict[str, Any]: source_id = str(source.get("source_id", "")).strip() + source_type = str(source.get("type", "")).strip() url = str(source.get("url", "")).strip() + if _is_policy_blocked_source(source_type, url): + return { + "source_id": source_id, + "type": source.get("type"), + "url": url, + "status": "skipped_by_policy", + "http_status": None, + "items": [], + "error": None, + "policy_blocker": "github_global_freeze_no_github_api_or_github_web_reads", + } fetched = fetcher(url, timeout_seconds) result: dict[str, Any] = { "source_id": source_id, diff --git a/apps/api/src/services/awoooi_priority_work_order_readback.py b/apps/api/src/services/awoooi_priority_work_order_readback.py index bbdf3deb7..6861e44dc 100644 --- a/apps/api/src/services/awoooi_priority_work_order_readback.py +++ b/apps/api/src/services/awoooi_priority_work_order_readback.py @@ -741,6 +741,30 @@ _COMMANDER_INSERTED_REQUIREMENT_WORK_ITEMS: list[dict[str, Any]] = [ "next_action": "將每個 P0 runbook 補齊 AI controlled repair loop 欄位。", "mapped_workplan_id": "P1-006", }, + { + "id": "CIR-P1-AI-002", + "priority": "P1", + "order": 40, + "status": "in_progress", + "lane": "agent_market_research_version_lifecycle", + "request": "把目前市場上所有主流且專業的 AI Agent 完整研究、評估導入整合,且目前 AI Agent 版本是否需要更新/更版要納入定期排程。", + "normalized_work_item": ( + "AI Agent market governance:OpenAI Agents SDK、LangGraph、Google ADK、Microsoft Agent Framework、" + "CrewAI、LlamaIndex AgentWorkflow、Pydantic AI、AWS Strands、AutoGen legacy migration、" + "NVIDIA NeMo/Nemotron、Claude Agent SDK 等主流候選都要進入 primary-source watch、" + "integration review、offline replay gate、version lifecycle proposal 與 no-write update cadence。" + ), + "current_state": ( + "已開始補 agent-market / ai-technology registries、GitHub-freeze policy skip、replacement candidates " + "與版本生命週期 proposal;正式整合仍需 replay / shadow / canary / cost / security evidence。" + ), + "acceptance": ( + "Work Items、Agent Market Watch、AI Technology Watch 與 Agent Version Lifecycle API 可讀回候選清單、" + "版本 baseline、排程 cadence、GitHub-freeze 邊界與下一個 offline replay / update proposal gate。" + ), + "next_action": "跑 registry/schema/test 驗證後推 Gitea,讀回 production API/UI,確認此項出現在 Work Items。", + "mapped_workplan_id": "P1-006", + }, { "id": "CIR-P1-KM-001", "priority": "P1", diff --git a/apps/api/tests/test_agent_market_watch.py b/apps/api/tests/test_agent_market_watch.py index 6b7a1858c..95293154e 100644 --- a/apps/api/tests/test_agent_market_watch.py +++ b/apps/api/tests/test_agent_market_watch.py @@ -225,7 +225,7 @@ def test_docs_hash_ignores_dynamic_script_noise(): assert second_report["candidates"][0]["sources"][0]["changed_since_reference"] is False -def test_market_watch_parses_github_tags_for_projects_without_releases(): +def test_market_watch_policy_skips_github_tags_without_fetching(): registry = { "schema_version": "agent_market_watch_sources_v1", "policy": {"replacement_decision_allowed": False}, @@ -247,11 +247,7 @@ def test_market_watch_parses_github_tags_for_projects_without_releases(): } def fetcher(_url: str, _timeout: int) -> FetchedSource: - return FetchedSource( - status="ok", - http_status=200, - body=json.dumps([{"name": "v0.8.0"}]).encode(), - ) + raise AssertionError("GitHub sources must be skipped by policy") report = run_agent_market_watch( registry, @@ -262,9 +258,11 @@ def test_market_watch_parses_github_tags_for_projects_without_releases(): ) source = report["candidates"][0]["sources"][0] - assert source["status"] == "ok" - assert source["version"] == "v0.8.0" + assert source["status"] == "skipped_by_policy" + assert source["version"] is None + assert source["policy_blocker"] == "github_global_freeze_no_github_api_or_github_web_reads" assert report["summary"]["failure_count"] == 0 + assert report["summary"]["policy_skipped_source_count"] == 1 def test_versioned_source_ignores_metadata_hash_noise_when_version_is_unchanged(): @@ -335,7 +333,7 @@ def test_versioned_source_ignores_metadata_hash_noise_when_version_is_unchanged( assert report["candidates"][0]["sources"][0]["changed_since_reference"] is False -def test_github_rate_limit_carries_forward_previous_source_without_failure(): +def test_github_release_source_is_policy_skipped_before_rate_limit_handling(): registry = { "schema_version": "agent_market_watch_sources_v1", "policy": {"replacement_decision_allowed": False}, @@ -370,12 +368,7 @@ def test_github_rate_limit_carries_forward_previous_source_without_failure(): } def fetcher(_url: str, _timeout: int) -> FetchedSource: - return FetchedSource( - status="error", - http_status=403, - body=b'{"message":"API rate limit exceeded"}', - error="http_403", - ) + raise AssertionError("GitHub sources must be skipped by policy") report = run_agent_market_watch( registry, @@ -389,8 +382,9 @@ def test_github_rate_limit_carries_forward_previous_source_without_failure(): source = report["candidates"][0]["sources"][0] assert report["summary"]["failure_count"] == 0 assert report["summary"]["changed_candidates"] == 0 - assert source["status"] == "carried_forward_rate_limited" - assert source["version"] == "v1.2.3" + assert report["summary"]["policy_skipped_source_count"] == 1 + assert source["status"] == "skipped_by_policy" + assert source["version"] is None assert source["changed_since_reference"] is False assert source["error"] is None - assert source["carried_forward_from_previous"] is True + assert source["policy_blocker"] == "github_global_freeze_no_github_api_or_github_web_reads" diff --git a/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal.py b/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal.py index 6f2d96266..cdc6bd0c2 100644 --- a/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal.py +++ b/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal.py @@ -13,9 +13,11 @@ def test_load_latest_ai_agent_version_lifecycle_update_proposal_reads_committed_ data = load_latest_ai_agent_version_lifecycle_update_proposal() assert data["schema_version"] == "ai_agent_version_lifecycle_update_proposal_v1" - assert data["program_status"]["overall_completion_percent"] == 78 - assert data["program_status"]["current_task_id"] == "P2-413" - assert data["program_status"]["next_task_id"] == "P2-414" + assert data["program_status"]["overall_completion_percent"] == 80 + assert data["program_status"]["current_task_id"] == "CIR-P1-AI-002" + assert data["program_status"]["next_task_id"] == ( + "P1-006-agent-market-version-lifecycle-readback" + ) assert data["program_status"]["read_only_mode"] is True assert data["program_status"]["runtime_authority"] == ( "version_lifecycle_update_proposal_only_no_write_or_upgrade" @@ -31,8 +33,8 @@ def test_load_latest_ai_agent_version_lifecycle_update_proposal_reads_committed_ assert data["telegram_digest_contract"]["gateway_queue_write_allowed"] is False assert data["telegram_digest_contract"]["bot_api_call_allowed"] is False assert data["rollups"]["domain_count"] == len(data["lifecycle_domains"]) == 12 - assert data["rollups"]["proposal_count"] == len(data["update_proposals"]) == 12 - assert data["rollups"]["cadence_count"] == len(data["cadence_matrix"]) == 6 + assert data["rollups"]["proposal_count"] == len(data["update_proposals"]) == 13 + assert data["rollups"]["cadence_count"] == len(data["cadence_matrix"]) == 7 assert data["rollups"]["approval_gate_count"] == len(data["approval_gate_matrix"]) == 9 assert data["rollups"]["false_runtime_boundary_count"] == 29 assert data["rollups"]["auto_execution_allowed_count"] == 0 @@ -43,6 +45,10 @@ def test_load_latest_ai_agent_version_lifecycle_update_proposal_reads_committed_ proposal["proposal_id"] == "openclaw_challenger_replay_bench" for proposal in data["update_proposals"] ) + assert any( + proposal["proposal_id"] == "mainstream_agent_framework_version_lifecycle_review" + for proposal in data["update_proposals"] + ) assert all(proposal["requires_owner_approval"] is True for proposal in data["update_proposals"]) assert all(proposal["direct_update_allowed"] is False for proposal in data["update_proposals"]) @@ -68,7 +74,7 @@ def test_ai_agent_version_lifecycle_update_proposal_rejects_rollup_mismatch(tmp_ def test_ai_agent_version_lifecycle_update_proposal_rejects_direct_update(tmp_path): snapshot = _snapshot() snapshot["update_proposals"][0]["direct_update_allowed"] = True - snapshot["rollups"]["read_only_proposal_count"] = 11 + snapshot["rollups"]["read_only_proposal_count"] = 12 _write_snapshot(tmp_path, snapshot) with pytest.raises(ValueError, match="direct update"): diff --git a/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal_api.py b/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal_api.py index a784227f7..1bb4b0ea8 100644 --- a/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal_api.py +++ b/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal_api.py @@ -16,9 +16,11 @@ def test_ai_agent_version_lifecycle_update_proposal_endpoint_returns_committed_s assert response.status_code == 200 data = response.json() assert data["schema_version"] == "ai_agent_version_lifecycle_update_proposal_v1" - assert data["program_status"]["overall_completion_percent"] == 78 - assert data["program_status"]["current_task_id"] == "P2-413" - assert data["program_status"]["next_task_id"] == "P2-414" + assert data["program_status"]["overall_completion_percent"] == 80 + assert data["program_status"]["current_task_id"] == "CIR-P1-AI-002" + assert data["program_status"]["next_task_id"] == ( + "P1-006-agent-market-version-lifecycle-readback" + ) assert data["program_status"]["read_only_mode"] is True assert data["runtime_boundaries"]["package_upgrade_allowed"] is False assert data["runtime_boundaries"]["k3s_upgrade_allowed"] is False @@ -26,8 +28,8 @@ def test_ai_agent_version_lifecycle_update_proposal_endpoint_returns_committed_s assert data["runtime_boundaries"]["production_write_allowed"] is False assert data["runtime_boundaries"]["openclaw_replacement_allowed"] is False assert data["rollups"]["domain_count"] == 12 - assert data["rollups"]["proposal_count"] == 12 - assert data["rollups"]["approval_required_count"] == 12 + assert data["rollups"]["proposal_count"] == 13 + assert data["rollups"]["approval_required_count"] == 13 assert data["rollups"]["auto_execution_allowed_count"] == 0 assert data["rollups"]["telegram_direct_send_count"] == 0 assert data["rollups"]["production_write_count"] == 0 @@ -35,3 +37,11 @@ def test_ai_agent_version_lifecycle_update_proposal_endpoint_returns_committed_s proposal["proposal_id"] == "telegram_bot_gateway_policy_refresh" for proposal in data["update_proposals"] ) + assert any( + proposal["proposal_id"] == "mainstream_agent_framework_version_lifecycle_review" + for proposal in data["update_proposals"] + ) + assert any( + binding["binding_id"] == "agent_version_lifecycle_weekly_tuesday_0900_taipei" + for binding in data["periodic_schedule_bindings"] + ) diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 17b2721bc..8cb424809 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -4792,8 +4792,8 @@ } }, "versionLifecycleProposal": { - "title": "P2-413 版本生命週期更新提案", - "subtitle": "{current} → {next};更新提案 {proposals};批准 Gate {gates}。AI Agent 只做評估、排序、批准包與回滾計畫,不自動升級。", + "title": "AI Agent Version Lifecycle And Market Update Schedule", + "subtitle": "{current} → {next}; proposals {proposals}; approval gates {gates}. The AI Agent only evaluates, ranks, drafts approval packets, and plans rollback; it does not auto-upgrade.", "source": "{generated} · {current} → {next}", "badges": { "readOnly": "只讀提案佇列", @@ -4801,7 +4801,7 @@ "production": "正式環境寫入 {value}" }, "metrics": { - "overall": "P2-413 進度", + "overall": "Governance Progress", "domains": "治理領域", "proposals": "更新提案", "critical": "關鍵風險", @@ -4811,7 +4811,8 @@ "sections": { "proposals": "優先更新提案", "boundaries": "仍被關閉的 runtime 動作", - "cadence": "日週月與觸發節奏", + "cadence": "Daily / Weekly / Monthly Cadence", + "schedules": "Active Schedule Bindings", "telegram": "Telegram 報告草稿政策" }, "labels": { diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 87cf8869e..0b9c7362c 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -4792,7 +4792,7 @@ } }, "versionLifecycleProposal": { - "title": "P2-413 版本生命週期更新提案", + "title": "AI Agent 版本生命週期與市場更新排程", "subtitle": "{current} → {next};更新提案 {proposals};批准 Gate {gates}。AI Agent 只做評估、排序、批准包與回滾計畫,不自動升級。", "source": "{generated} · {current} → {next}", "badges": { @@ -4801,7 +4801,7 @@ "production": "正式環境寫入 {value}" }, "metrics": { - "overall": "P2-413 進度", + "overall": "治理進度", "domains": "治理領域", "proposals": "更新提案", "critical": "關鍵風險", @@ -4812,6 +4812,7 @@ "proposals": "優先更新提案", "boundaries": "仍被關閉的 runtime 動作", "cadence": "日週月與觸發節奏", + "schedules": "實際排程綁定", "telegram": "Telegram 報告草稿政策" }, "labels": { diff --git a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx index 36fbebbf4..afec25804 100644 --- a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx +++ b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx @@ -8112,7 +8112,7 @@ export function AutomationInventoryTab() {
{t('versionLifecycleProposal.sections.cadence')}
- {versionLifecycleProposal.cadence_matrix.slice(0, 4).map(cadence => ( + {versionLifecycleProposal.cadence_matrix.slice(0, 5).map(cadence => (
@@ -8127,6 +8127,29 @@ export function AutomationInventoryTab() { ))}
+ {!!versionLifecycleProposal.periodic_schedule_bindings?.length && ( +
+ {t('versionLifecycleProposal.sections.schedules')} +
+ {versionLifecycleProposal.periodic_schedule_bindings.map(binding => ( +
+
+ + {binding.binding_id} + + +
+ + {binding.local_time} · {binding.cron} + + + {binding.workflow} + +
+ ))} +
+
+ )}
{t('versionLifecycleProposal.sections.telegram')}
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index e91c5e8db..2b90f84f5 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -3996,6 +3996,14 @@ export interface AiAgentVersionLifecycleUpdateProposalSnapshot { summary: string gate: string }> + periodic_schedule_bindings?: Array<{ + binding_id: string + workflow: string + cron: string + local_time: string + purpose: string + runtime_write_allowed: false + }> } export interface HostRunawayAiopsLoopReadinessSnapshot { diff --git a/docs/ai/agent-market-watch-sources.v1.json b/docs/ai/agent-market-watch-sources.v1.json index 8180dfd3f..ccff74684 100644 --- a/docs/ai/agent-market-watch-sources.v1.json +++ b/docs/ai/agent-market-watch-sources.v1.json @@ -1,11 +1,12 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "schema_version": "agent_market_watch_sources_v1", - "updated_at": "2026-06-26", + "updated_at": "2026-07-10", "purpose": "AI Agent 市場定期評估的 primary-source watchlist;來源變更只會觸發重新評估,不代表取代或整合已批准。", "cadence": { "weekly_market_watch": "每週一 09:00 Asia/Taipei 產生只讀市場觀測報告與整合 / 探索 review 摘要。", "monthly_integration_review": "經 operator review 後,提交 market watch、integration review 與 discovery intake 的 reviewed baseline。", + "weekly_version_lifecycle_update_review": "每週二 09:00 Asia/Taipei 比對目前 AI Agent / SDK 版本 baseline,產生 no-write 更新提案。", "trigger_on_major_version": true }, "policy": { @@ -14,7 +15,9 @@ "paid_provider_requires_approval": true, "new_dependency_requires_approval": true, "raw_external_pages_committed": false, - "official_or_primary_sources_only": true + "official_or_primary_sources_only": true, + "github_sources_allowed": false, + "github_freeze_policy": "GitHub / gh / GitHub API / GitHub search disabled; use vendor docs, PyPI, npm, and committed offline metadata only." }, "candidates": [ { @@ -47,13 +50,13 @@ "source_id": "openai_agents_python_pypi", "type": "pypi", "url": "https://pypi.org/pypi/openai-agents/json", - "reference_version": null + "reference_version": "0.18.1" }, { "source_id": "openai_agents_typescript_npm", "type": "npm", "url": "https://registry.npmjs.org/@openai%2Fagents", - "reference_version": null + "reference_version": "0.13.1" } ] }, @@ -75,13 +78,7 @@ "source_id": "langgraph_pypi", "type": "pypi", "url": "https://pypi.org/pypi/langgraph/json", - "reference_version": null - }, - { - "source_id": "langgraph_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/langchain-ai/langgraph/releases/latest", - "reference_version": null + "reference_version": "1.2.9" } ] }, @@ -103,7 +100,7 @@ "source_id": "nvidia_nemo_agent_toolkit_pypi", "type": "pypi", "url": "https://pypi.org/pypi/nvidia-nat/json", - "reference_version": null + "reference_version": "1.8.0" }, { "source_id": "nvidia_nemotron_developer_page", @@ -171,13 +168,7 @@ "source_id": "google_adk_pypi", "type": "pypi", "url": "https://pypi.org/pypi/google-adk/json", - "reference_version": null - }, - { - "source_id": "google_adk_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/google/adk-python/releases/latest", - "reference_version": null + "reference_version": "2.4.0" } ] }, @@ -196,9 +187,9 @@ "reference_version": null }, { - "source_id": "microsoft_agent_framework_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/microsoft/agent-framework/releases/latest", + "source_id": "microsoft_autogen_migration_docs", + "type": "docs", + "url": "https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-autogen/", "reference_version": null } ] @@ -221,13 +212,125 @@ "source_id": "crewai_pypi", "type": "pypi", "url": "https://pypi.org/pypi/crewai/json", + "reference_version": "1.15.2" + } + ] + }, + { + "candidate_id": "llamaindex_agent_workflows", + "display_name": "LlamaIndex AgentWorkflow / Workflows", + "evaluation_priority": "can_test", + "recommended_role": "RAG / Document Agent Workflow Orchestrator", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "sources": [ + { + "source_id": "llamaindex_agent_workflows_docs", + "type": "docs", + "url": "https://developers.llamaindex.ai/python/examples/agent/agent_workflow_basic/", "reference_version": null }, { - "source_id": "crewai_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/crewAIInc/crewAI/releases/latest", + "source_id": "llamaindex_workflows_docs", + "type": "docs", + "url": "https://developers.llamaindex.ai/python/llamaagents/workflows/", "reference_version": null + }, + { + "source_id": "llama_index_core_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/llama-index-core/json", + "reference_version": "0.14.23" + } + ] + }, + { + "candidate_id": "pydantic_ai_agents", + "display_name": "Pydantic AI Agents", + "evaluation_priority": "can_test", + "recommended_role": "Type-Safe Tool Agent / Structured Output Runtime", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "sources": [ + { + "source_id": "pydantic_ai_overview_docs", + "type": "docs", + "url": "https://pydantic.dev/docs/ai/overview/", + "reference_version": null + }, + { + "source_id": "pydantic_ai_agents_docs", + "type": "docs", + "url": "https://pydantic.dev/docs/ai/core-concepts/agent/", + "reference_version": null + }, + { + "source_id": "pydantic_ai_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/pydantic-ai/json", + "reference_version": "2.8.0" + } + ] + }, + { + "candidate_id": "aws_strands_agents", + "display_name": "AWS Strands Agents", + "evaluation_priority": "can_test", + "recommended_role": "AWS-Native Model-First Agent SDK", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "sources": [ + { + "source_id": "strands_agents_docs", + "type": "docs", + "url": "https://strandsagents.com/docs/user-guide/quickstart/overview/", + "reference_version": null + }, + { + "source_id": "aws_strands_prescriptive_guidance", + "type": "docs", + "url": "https://docs.aws.amazon.com/prescriptive-guidance/latest/agentic-ai-frameworks/strands-agents.html", + "reference_version": null + }, + { + "source_id": "strands_agents_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/strands-agents/json", + "reference_version": "1.46.0" + }, + { + "source_id": "strands_agents_typescript_npm", + "type": "npm", + "url": "https://registry.npmjs.org/@strands-agents%2Fsdk", + "reference_version": "1.8.0" + } + ] + }, + { + "candidate_id": "autogen_legacy_migration", + "display_name": "AutoGen Legacy Migration", + "evaluation_priority": "watch_only", + "recommended_role": "Legacy Multi-Agent Framework Migration Signal", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "sources": [ + { + "source_id": "autogen_docs", + "type": "docs", + "url": "https://microsoft.github.io/autogen/stable//index.html", + "reference_version": null + }, + { + "source_id": "autogen_to_agent_framework_migration_docs", + "type": "docs", + "url": "https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-autogen/", + "reference_version": null + }, + { + "source_id": "autogen_agentchat_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/autogen-agentchat/json", + "reference_version": "0.7.5" } ] }, @@ -244,12 +347,6 @@ "type": "docs", "url": "https://hermes-agent.nousresearch.com", "reference_version": null - }, - { - "source_id": "hermes_agent_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/NousResearch/hermes-agent/releases/latest", - "reference_version": null } ] }, @@ -266,12 +363,6 @@ "type": "docs", "url": "https://microsoft.github.io/agent-governance-toolkit/", "reference_version": null - }, - { - "source_id": "microsoft_agent_governance_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/microsoft/agent-governance-toolkit/releases/latest", - "reference_version": null } ] }, @@ -288,12 +379,6 @@ "type": "docs", "url": "https://thclaws.ai", "reference_version": null - }, - { - "source_id": "thclaws_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/thClaws/thClaws/releases/latest", - "reference_version": null } ] }, @@ -310,12 +395,6 @@ "type": "docs", "url": "https://vstorm-co.github.io/pydantic-deepagents/", "reference_version": null - }, - { - "source_id": "pydantic_deepagents_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/vstorm-co/pydantic-deepagents/releases/latest", - "reference_version": null } ] }, @@ -332,12 +411,6 @@ "type": "docs", "url": "https://agentos.sh", "reference_version": null - }, - { - "source_id": "agentos_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/framerslab/agentos/releases/latest", - "reference_version": null } ] }, @@ -354,28 +427,9 @@ "type": "docs", "url": "https://bernstein.run", "reference_version": null - }, - { - "source_id": "bernstein_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/sipyourdrink-ltd/bernstein/releases/latest", - "reference_version": null } ] } ], - "discovery_sources": [ - { - "source_id": "github_ai_agent_topic", - "type": "github_search", - "url": "https://api.github.com/search/repositories?q=topic:ai-agent+stars:%3E500&sort=updated&order=desc", - "purpose": "Find new high-signal open-source AI Agent frameworks. Any finding requires manual source classification before integration." - }, - { - "source_id": "github_agent_framework_topic", - "type": "github_search", - "url": "https://api.github.com/search/repositories?q=topic:agent-framework+stars:%3E300&sort=updated&order=desc", - "purpose": "Find new agent framework candidates. Any finding requires official-source verification before being added as a candidate." - } - ] + "discovery_sources": [] } diff --git a/docs/ai/agent-replacement-candidates.v1.json b/docs/ai/agent-replacement-candidates.v1.json index c8281ecab..287fe9eca 100644 --- a/docs/ai/agent-replacement-candidates.v1.json +++ b/docs/ai/agent-replacement-candidates.v1.json @@ -220,6 +220,46 @@ "evaluation_priority": "secondary", "required_stage": "offline_replay" }, + { + "candidate_id": "llamaindex_agent_workflows", + "display_name": "LlamaIndex AgentWorkflow / Workflows", + "official_url": "https://developers.llamaindex.ai/python/examples/agent/agent_workflow_basic/", + "role": "rag_document_agent_workflow_orchestrator", + "evaluation_priority": "can_test", + "required_stage": "offline_replay", + "current_decision": "new_mainstream_candidate_registered_requires_no_sdk_no_api_adapter_then_offline_replay", + "sdk_dependency": "llama_index_core_package_not_installed" + }, + { + "candidate_id": "pydantic_ai_agents", + "display_name": "Pydantic AI Agents", + "official_url": "https://pydantic.dev/docs/ai/overview/", + "role": "type_safe_tool_agent_structured_output_runtime", + "evaluation_priority": "can_test", + "required_stage": "offline_replay", + "current_decision": "new_mainstream_candidate_registered_requires_no_sdk_no_api_adapter_then_offline_replay", + "sdk_dependency": "pydantic_ai_package_not_installed" + }, + { + "candidate_id": "aws_strands_agents", + "display_name": "AWS Strands Agents", + "official_url": "https://strandsagents.com/docs/user-guide/quickstart/overview/", + "role": "aws_native_model_first_agent_sdk", + "evaluation_priority": "can_test", + "required_stage": "offline_replay", + "current_decision": "new_mainstream_candidate_registered_requires_cost_provider_and_no_sdk_no_api_adapter_review", + "sdk_dependency": "strands_agents_package_not_installed" + }, + { + "candidate_id": "autogen_legacy_migration", + "display_name": "AutoGen Legacy Migration", + "official_url": "https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-autogen/", + "role": "legacy_multi_agent_framework_migration_signal", + "evaluation_priority": "watch_only", + "required_stage": "watch_only_primary_source_monitoring", + "current_decision": "watch_only_migration_signal_microsoft_agent_framework_is_successor_path", + "latest_discovery_classification": "docs/evaluations/agent_market_discovery_classification_2026-06-04.json" + }, { "candidate_id": "hermes_agent_personal_platform", "display_name": "NousResearch Hermes Agent", diff --git a/docs/ai/ai-technology-watch-sources.v1.json b/docs/ai/ai-technology-watch-sources.v1.json index 87ad5f9eb..a146ece10 100644 --- a/docs/ai/ai-technology-watch-sources.v1.json +++ b/docs/ai/ai-technology-watch-sources.v1.json @@ -1,6 +1,6 @@ { "schema_version": "ai_technology_watch_sources_v1", - "updated_at": "2026-06-26", + "updated_at": "2026-07-10", "cadence": { "near_real_time_watch": "每 6 小時執行一次只讀 primary-source 檢查,偵測主流 AI 技術版本、文件與 release 變更。", "daily_triage": "每日彙整變更技術,依商業適用性、依賴風險、成本風險與資安風險分組。", @@ -16,7 +16,9 @@ "workflow_modification_approved": false, "telegram_send_approved": false, "model_provider_switch_approved": false, - "host_write_approved": false + "host_write_approved": false, + "github_sources_allowed": false, + "github_freeze_policy": "GitHub / gh / GitHub API / GitHub search disabled; use vendor docs, PyPI, npm, and committed offline metadata only." }, "coverage_contract": { "scope": [ @@ -54,13 +56,13 @@ "source_id": "openai_agents_python_pypi", "type": "pypi", "url": "https://pypi.org/pypi/openai-agents/json", - "reference_version": "0.17.7" + "reference_version": "0.18.1" }, { "source_id": "openai_agents_typescript_npm", "type": "npm", "url": "https://registry.npmjs.org/%40openai%2Fagents", - "reference_version": "0.12.0" + "reference_version": "0.13.1" }, { "source_id": "openai_agents_docs", @@ -93,7 +95,8 @@ { "source_id": "nvidia_nemo_agent_toolkit_pypi", "type": "pypi", - "url": "https://pypi.org/pypi/nvidia-nat/json" + "url": "https://pypi.org/pypi/nvidia-nat/json", + "reference_version": "1.8.0" }, { "source_id": "nvidia_nemotron_3_ultra_blog", @@ -117,13 +120,7 @@ "source_id": "langgraph_pypi", "type": "pypi", "url": "https://pypi.org/pypi/langgraph/json", - "reference_version": "1.2.6" - }, - { - "source_id": "langgraph_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/langchain-ai/langgraph/releases/latest", - "reference_version": "1.2.6" + "reference_version": "1.2.9" }, { "source_id": "langgraph_docs", @@ -147,13 +144,7 @@ "source_id": "google_adk_pypi", "type": "pypi", "url": "https://pypi.org/pypi/google-adk/json", - "reference_version": "2.3.0" - }, - { - "source_id": "google_adk_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/google/adk-python/releases/latest", - "reference_version": "v2.3.0" + "reference_version": "2.4.0" }, { "source_id": "google_adk_docs", @@ -173,16 +164,15 @@ "requires_dependency_approval": true, "requires_security_review": true, "sources": [ - { - "source_id": "microsoft_agent_framework_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/microsoft/agent-framework/releases/latest", - "reference_version": "dotnet-1.11.0" - }, { "source_id": "microsoft_agent_framework_docs", "type": "docs", "url": "https://learn.microsoft.com/en-us/agent-framework/overview/" + }, + { + "source_id": "microsoft_autogen_migration_docs", + "type": "docs", + "url": "https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-autogen/" } ] }, @@ -201,7 +191,7 @@ "source_id": "crewai_pypi", "type": "pypi", "url": "https://pypi.org/pypi/crewai/json", - "reference_version": "1.14.7" + "reference_version": "1.15.2" }, { "source_id": "crewai_docs", @@ -210,6 +200,128 @@ } ] }, + { + "candidate_id": "llamaindex_agent_workflows", + "display_name": "LlamaIndex AgentWorkflow / Workflows", + "technology_area": "agent_frameworks", + "integration_surface": "rag_document_agent_workflow", + "awoooi_role": "RAG / 文件型 Agent workflow 與 ingestion orchestration 候選", + "evaluation_priority": "p1", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "llamaindex_agent_workflows_docs", + "type": "docs", + "url": "https://developers.llamaindex.ai/python/examples/agent/agent_workflow_basic/" + }, + { + "source_id": "llamaindex_workflows_docs", + "type": "docs", + "url": "https://developers.llamaindex.ai/python/llamaagents/workflows/" + }, + { + "source_id": "llama_index_core_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/llama-index-core/json", + "reference_version": "0.14.23" + } + ] + }, + { + "candidate_id": "pydantic_ai_agents", + "display_name": "Pydantic AI", + "technology_area": "agent_frameworks", + "integration_surface": "type_safe_agent_structured_output", + "awoooi_role": "type-safe tool agent、structured output 與 validation-first workflow 候選", + "evaluation_priority": "p1", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "pydantic_ai_overview_docs", + "type": "docs", + "url": "https://pydantic.dev/docs/ai/overview/" + }, + { + "source_id": "pydantic_ai_agents_docs", + "type": "docs", + "url": "https://pydantic.dev/docs/ai/core-concepts/agent/" + }, + { + "source_id": "pydantic_ai_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/pydantic-ai/json", + "reference_version": "2.8.0" + } + ] + }, + { + "candidate_id": "aws_strands_agents", + "display_name": "AWS Strands Agents", + "technology_area": "agent_frameworks", + "integration_surface": "aws_native_model_first_agent_sdk", + "awoooi_role": "AWS-native agent SDK watch / sandbox candidate,需成本與 provider gate", + "evaluation_priority": "p1", + "requires_cost_approval": true, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "strands_agents_docs", + "type": "docs", + "url": "https://strandsagents.com/docs/user-guide/quickstart/overview/" + }, + { + "source_id": "aws_strands_prescriptive_guidance", + "type": "docs", + "url": "https://docs.aws.amazon.com/prescriptive-guidance/latest/agentic-ai-frameworks/strands-agents.html" + }, + { + "source_id": "strands_agents_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/strands-agents/json", + "reference_version": "1.46.0" + }, + { + "source_id": "strands_agents_typescript_npm", + "type": "npm", + "url": "https://registry.npmjs.org/%40strands-agents%2Fsdk", + "reference_version": "1.8.0" + } + ] + }, + { + "candidate_id": "autogen_legacy_migration", + "display_name": "AutoGen Legacy Migration", + "technology_area": "agent_frameworks", + "integration_surface": "legacy_multi_agent_migration_signal", + "awoooi_role": "AutoGen legacy usage stays migration signal; Microsoft Agent Framework is the enterprise successor path.", + "evaluation_priority": "p2", + "requires_cost_approval": false, + "requires_dependency_approval": true, + "requires_security_review": true, + "sources": [ + { + "source_id": "autogen_docs", + "type": "docs", + "url": "https://microsoft.github.io/autogen/stable//index.html" + }, + { + "source_id": "autogen_to_agent_framework_migration_docs", + "type": "docs", + "url": "https://learn.microsoft.com/en-us/agent-framework/migration-guide/from-autogen/" + }, + { + "source_id": "autogen_agentchat_pypi", + "type": "pypi", + "url": "https://pypi.org/pypi/autogen-agentchat/json", + "reference_version": "0.7.5" + } + ] + }, { "candidate_id": "modelcontextprotocol_sdk", "display_name": "Model Context Protocol SDK", @@ -224,17 +336,8 @@ { "source_id": "mcp_typescript_sdk_npm", "type": "npm", - "url": "https://registry.npmjs.org/%40modelcontextprotocol%2Fsdk" - }, - { - "source_id": "mcp_typescript_sdk_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/modelcontextprotocol/typescript-sdk/releases/latest" - }, - { - "source_id": "mcp_typescript_sdk_docs", - "type": "docs", - "url": "https://github.com/modelcontextprotocol/typescript-sdk" + "url": "https://registry.npmjs.org/%40modelcontextprotocol%2Fsdk", + "reference_version": "1.29.0" }, { "source_id": "mcp_specification_2025_06_18", @@ -259,16 +362,6 @@ "requires_dependency_approval": true, "requires_security_review": true, "sources": [ - { - "source_id": "a2a_protocol_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/a2aproject/A2A/releases/latest" - }, - { - "source_id": "a2a_python_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/a2aproject/a2a-python/releases/latest" - }, { "source_id": "a2a_protocol_docs", "type": "docs", @@ -431,14 +524,9 @@ "requires_security_review": true, "sources": [ { - "source_id": "pgvector_github_tags", - "type": "github_tags", - "url": "https://api.github.com/repos/pgvector/pgvector/tags" - }, - { - "source_id": "pgvector_docs", + "source_id": "pgvector_pgxn", "type": "docs", - "url": "https://github.com/pgvector/pgvector" + "url": "https://pgxn.org/dist/vector/" } ] }, @@ -459,9 +547,9 @@ "url": "https://pypi.org/pypi/qdrant-client/json" }, { - "source_id": "qdrant_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/qdrant/qdrant/releases/latest" + "source_id": "qdrant_docs", + "type": "docs", + "url": "https://qdrant.tech/documentation/" } ] }, @@ -482,9 +570,9 @@ "url": "https://pypi.org/pypi/chromadb/json" }, { - "source_id": "chromadb_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/chroma-core/chroma/releases/latest" + "source_id": "chromadb_docs", + "type": "docs", + "url": "https://docs.trychroma.com/" } ] }, @@ -545,11 +633,6 @@ "requires_dependency_approval": true, "requires_security_review": true, "sources": [ - { - "source_id": "tgi_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/huggingface/text-generation-inference/releases/latest" - }, { "source_id": "tgi_docs", "type": "docs", @@ -574,28 +657,12 @@ "url": "https://pypi.org/pypi/vllm/json" }, { - "source_id": "vllm_github_release", - "type": "github_release", - "url": "https://api.github.com/repos/vllm-project/vllm/releases/latest" + "source_id": "vllm_docs", + "type": "docs", + "url": "https://docs.vllm.ai/en/latest/" } ] } ], - "discovery_sources": [ - { - "source_id": "github_ai_agent_discovery", - "type": "github_search", - "url": "https://api.github.com/search/repositories?q=topic:ai-agent+stars:%3E5000&sort=updated&order=desc" - }, - { - "source_id": "github_mcp_discovery", - "type": "github_search", - "url": "https://api.github.com/search/repositories?q=topic:mcp+stars:%3E1000&sort=updated&order=desc" - }, - { - "source_id": "github_rag_discovery", - "type": "github_search", - "url": "https://api.github.com/search/repositories?q=topic:rag+stars:%3E3000&sort=updated&order=desc" - } - ] + "discovery_sources": [] } diff --git a/docs/evaluations/ai_agent_version_lifecycle_update_proposal_2026-07-10.json b/docs/evaluations/ai_agent_version_lifecycle_update_proposal_2026-07-10.json new file mode 100644 index 000000000..a1dadbc9c --- /dev/null +++ b/docs/evaluations/ai_agent_version_lifecycle_update_proposal_2026-07-10.json @@ -0,0 +1,922 @@ +{ + "schema_version": "ai_agent_version_lifecycle_update_proposal_v1", + "generated_at": "2026-07-10T00:00:00+08:00", + "program_status": { + "overall_completion_percent": 80, + "current_priority": "P1", + "current_task_id": "CIR-P1-AI-002", + "next_task_id": "P1-006-agent-market-version-lifecycle-readback", + "read_only_mode": true, + "runtime_authority": "version_lifecycle_update_proposal_only_no_write_or_upgrade", + "status_note": "CIR-P1-AI-002 將市場主流 AI Agent / SDK / framework 研究、導入評估與版本生命週期更新排程納入同一個 no-write governance queue。Agent 可主動比對 official docs / PyPI / npm baseline、提出 replay / adapter / update proposal;實際 SDK install、paid API、provider switch、OpenClaw replacement、workflow write、production write 仍全部關閉。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json", + "docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json", + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "docs/evaluations/ai_agent_market_radar_readback_2026-06-26.json", + "docs/evaluations/ai_technology_radar_readback_2026-06-26.json", + "docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md", + "docs/ai/agent-market-watch-sources.v1.json", + "docs/ai/ai-technology-watch-sources.v1.json", + "docs/ai/agent-replacement-candidates.v1.json", + ".gitea/workflows/agent-market-watch.yaml", + ".gitea/workflows/ai-technology-watch.yaml", + ".gitea/workflows/agent-version-lifecycle-watch.yaml" + ], + "agent_roles": [ + { + "agent_id": "openclaw", + "role": "仲裁者", + "responsibility": "高風險版本變更、主機/K3s/stateful 維護窗、OpenClaw challenger 評估、回滾與最終 gate 判斷。" + }, + { + "agent_id": "hermes", + "role": "營運與知識執行者", + "responsibility": "套件/CI/觀測/Telegram 版本差異整理、報告化、runbook 草稿、批准包欄位完整性。" + }, + { + "agent_id": "nemotron", + "role": "AI 技術評測執行者", + "responsibility": "AI Agent/模型/SDK/MCP/RAG 候選的市場資料摘要、離線 replay 評估設計、schema 與工具鏈兼容性檢查。" + } + ], + "lifecycle_domains": [ + { + "domain_id": "ai_agents_models", + "display_name": "AI Agent / 模型 / SDK", + "owner_agent": "nemotron", + "risk_tier": "high", + "cadence": "weekly_primary_source + triggered_on_major_release", + "decision_policy": "市場 scorecard、replay、shadow、canary、成本、安全與可觀測性證據不足時只保留提案。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "backend_python_packages", + "display_name": "FastAPI / Python 套件", + "owner_agent": "hermes", + "risk_tier": "high", + "cadence": "daily_repo_manifest + weekly_primary_source", + "decision_policy": "只產生 dependency approval packet;不得寫 lockfile、不得安裝或升級。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "frontend_javascript_packages", + "display_name": "Next.js / pnpm 套件", + "owner_agent": "hermes", + "risk_tier": "medium", + "cadence": "daily_repo_manifest + weekly_primary_source", + "decision_policy": "只整理 UI/runtime 相容性、lockfile 差異與煙測計畫;不得改 lockfile。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "container_images", + "display_name": "Docker base image / image digest", + "owner_agent": "openclaw", + "risk_tier": "high", + "cadence": "weekly_digest_review + triggered_on_security", + "decision_policy": "只產 SBOM/digest pin 提案;不得 pull/build/push image。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "host_os_packages", + "display_name": "主機 OS / kernel / Nginx / SSH", + "owner_agent": "openclaw", + "risk_tier": "critical", + "cadence": "monthly_maintenance_review + triggered_on_security", + "decision_policy": "必須先有維護窗、備份、rollback owner、smoke plan;不得執行主機指令。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "k3s_kubernetes_components", + "display_name": "K3s / Kubernetes / CNI / Ingress", + "owner_agent": "openclaw", + "risk_tier": "critical", + "cadence": "monthly_skew_policy_review + triggered_on_eol", + "decision_policy": "先做 version skew 與節點維護窗批准包;不得 kubectl、drain、restart。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "stateful_services", + "display_name": "PostgreSQL / Redis / MinIO / Harbor / Gitea", + "owner_agent": "openclaw", + "risk_tier": "critical", + "cadence": "monthly_stateful_review + triggered_on_security", + "decision_policy": "任何更新前必須證明備份新鮮、restore drill 與資料相容性;不得 restart/migration。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "observability_stack", + "display_name": "Prometheus / Alertmanager / Grafana / OTEL / Sentry", + "owner_agent": "hermes", + "risk_tier": "medium", + "cadence": "weekly_freshness + monthly_upgrade_review", + "decision_policy": "只提出 route/receiver/collector 相容性矩陣;不得寫告警路由。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "telegram_bot_gateway", + "display_name": "Telegram Bot / Gateway / digest policy", + "owner_agent": "hermes", + "risk_tier": "high", + "cadence": "weekly_policy_review + triggered_on_delivery_failure", + "decision_policy": "只產 no-send digest 與收斂策略;不得直送 Bot 或寫 Gateway queue。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "mcp_rag_tool_registry", + "display_name": "MCP / RAG / tool registry", + "owner_agent": "nemotron", + "risk_tier": "medium", + "cadence": "weekly_contract_review + triggered_on_tool_release", + "decision_policy": "只整理工具能力、資料保留、redaction 與審核需求;不得啟用新工具或外部服務。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "ci_cd_runner_tools", + "display_name": "Gitea Actions / runner / deploy tooling", + "owner_agent": "hermes", + "risk_tier": "high", + "cadence": "weekly_runner_health + triggered_on_ci_failure", + "decision_policy": "只產 workflow/runner owner packet;不得修改 workflow 或自動 merge。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "backup_dr_tooling", + "display_name": "Backup / DR / restore tooling", + "owner_agent": "openclaw", + "risk_tier": "critical", + "cadence": "weekly_backup_freshness + monthly_restore_readiness", + "decision_policy": "只整理 restore drill 與 escrow readiness;不得刪備份、restore 或 prune。", + "current_authority": "L2_approval_package_only" + } + ], + "update_proposals": [ + { + "proposal_id": "ai_agent_market_primary_source_radar", + "domain_id": "ai_agents_models", + "display_name": "AI Agent 市場主流版本雷達", + "owner_agent": "nemotron", + "priority": "P2", + "risk_tier": "high", + "status": "proposal_ready_owner_review_required", + "summary": "持續把 OpenClaw、Hermes、NemoTron 與市場主流 Agent/SDK/MCP/A2A 能力放入 scorecard;只產生候選與差距,不切換 provider。", + "evidence_refs": [ + "docs/evaluations/ai_agent_market_radar_readback_2026-06-26.json", + "docs/evaluations/ai_technology_radar_readback_2026-06-26.json" + ], + "approval_gate": "market_replay_shadow_canary_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "primary source freshness readback", + "candidate scorecard replay fixture", + "成本/延遲/安全/可觀測性欄位完整性檢查" + ], + "rollback_plan": [ + "維持 incumbent route", + "撤回候選標籤", + "保留舊 scorecard 作為比較基線" + ], + "blocked_runtime_actions": [ + "provider route switch", + "paid API call", + "OpenClaw replacement" + ], + "telegram_policy": "action_required_digest_draft_only" + }, + { + "proposal_id": "openclaw_challenger_replay_bench", + "domain_id": "ai_agents_models", + "display_name": "OpenClaw challenger replay 評測台", + "owner_agent": "openclaw", + "priority": "P1", + "risk_tier": "critical", + "status": "blocked_until_replay_shadow_canary_evidence", + "summary": "建立可讓 NemoTron 或其他 challenger 用相同任務集比較仲裁品質的 replay 評測台;未完成 shadow/canary 前不得替換 OpenClaw。", + "evidence_refs": [ + "docs/HARD_RULES.md", + "docs/evaluations/ai_provider_route_matrix_2026-06-04.json" + ], + "approval_gate": "market_replay_shadow_canary_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "sanitized historical task replay", + "shadow decision disagreement review", + "canary stop condition table" + ], + "rollback_plan": [ + "OpenClaw remains arbitration default", + "disable challenger route flag", + "archive failed challenger scorecard" + ], + "blocked_runtime_actions": [ + "OpenClaw replacement", + "production routing", + "runtime agent arbitration switch" + ], + "telegram_policy": "critical_owner_review_draft_only" + }, + { + "proposal_id": "python_dependency_authority_alignment", + "domain_id": "backend_python_packages", + "display_name": "Python dependency authority 對齊", + "owner_agent": "hermes", + "priority": "P2", + "risk_tier": "high", + "status": "action_required_dependency_packet", + "summary": "把 API Python manifest、套件風險、測試矩陣與 rollback plan 收成一份 owner packet;本階段不安裝、不升級。", + "evidence_refs": [ + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "apps/api/pyproject.toml" + ], + "approval_gate": "dependency_upgrade_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "pytest target matrix", + "API smoke plan", + "dependency conflict review" + ], + "rollback_plan": [ + "restore previous lock snapshot", + "revert dependency PR branch", + "rerun API smoke" + ], + "blocked_runtime_actions": [ + "package upgrade", + "lockfile write", + "workflow trigger" + ], + "telegram_policy": "failure_or_owner_action_draft_only" + }, + { + "proposal_id": "frontend_pnpm_freshness_plan", + "domain_id": "frontend_javascript_packages", + "display_name": "前端 pnpm / Next.js 新鮮度計畫", + "owner_agent": "hermes", + "priority": "P3", + "risk_tier": "medium", + "status": "proposal_ready_owner_review_required", + "summary": "整理前端套件、i18n、瀏覽器煙測與 build/typecheck gate,低中風險也先形成可審核草案。", + "evidence_refs": [ + "apps/web/package.json", + "apps/web/pnpm-lock.yaml", + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json" + ], + "approval_gate": "dependency_upgrade_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "pnpm typecheck", + "eslint target files", + "desktop/mobile governance smoke" + ], + "rollback_plan": [ + "revert package update branch", + "restore previous lockfile", + "re-run production smoke after deploy marker" + ], + "blocked_runtime_actions": [ + "package upgrade", + "lockfile write", + "auto merge" + ], + "telegram_policy": "weekly_digest_draft_only" + }, + { + "proposal_id": "container_digest_sbom_pin_packet", + "domain_id": "container_images", + "display_name": "Container digest / SBOM pin 批准包", + "owner_agent": "openclaw", + "priority": "P2", + "risk_tier": "high", + "status": "blocked_until_sbom_and_image_gate", + "summary": "為 base image 與 runtime image 建立 digest pin、SBOM、漏洞摘要與部署煙測計畫;不 pull/build/push image。", + "evidence_refs": [ + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "docs/evaluations/docker_build_surface_inventory_2026-06-04.json" + ], + "approval_gate": "container_sbom_digest_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "SBOM source plan", + "image digest diff review", + "deployment smoke plan" + ], + "rollback_plan": [ + "retain previous image digest", + "revert manifest proposal", + "block rollout until smoke passes" + ], + "blocked_runtime_actions": [ + "image pull", + "docker build", + "registry push" + ], + "telegram_policy": "critical_image_digest_draft_only" + }, + { + "proposal_id": "k3s_skew_maintenance_window_packet", + "domain_id": "k3s_kubernetes_components", + "display_name": "K3s version skew 維護窗批准包", + "owner_agent": "openclaw", + "priority": "P1", + "risk_tier": "critical", + "status": "blocked_until_maintenance_window", + "summary": "將 K3s/Kubernetes skew policy、節點順序、備援與煙測列成批准包;不得 kubectl、drain、restart。", + "evidence_refs": [ + "docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json", + "https://kubernetes.io/releases/version-skew-policy/" + ], + "approval_gate": "k3s_version_skew_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "version skew table", + "node-by-node maintenance sequence", + "public route smoke plan" + ], + "rollback_plan": [ + "pause node sequence", + "restore previous control-plane state", + "run post-check readback" + ], + "blocked_runtime_actions": [ + "kubectl command", + "node drain", + "k3s upgrade" + ], + "telegram_policy": "critical_owner_review_draft_only" + }, + { + "proposal_id": "host_os_security_maintenance_packet", + "domain_id": "host_os_packages", + "display_name": "Host OS 安全維護窗批准包", + "owner_agent": "openclaw", + "priority": "P1", + "risk_tier": "critical", + "status": "blocked_until_host_maintenance_window", + "summary": "把 OS/kernel/Nginx/OpenSSH 更新變成維護窗提案;本階段只用既有 inventory,不 SSH、不 apt、不 reboot。", + "evidence_refs": [ + "docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json", + "docs/runbooks/K3S-OPTIMIZATION-RUNBOOK.md" + ], + "approval_gate": "host_maintenance_window_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "host impact matrix", + "pre-change backup check", + "post-change service smoke plan" + ], + "rollback_plan": [ + "maintenance abort condition", + "service restore sequence", + "owner communication plan" + ], + "blocked_runtime_actions": [ + "host command", + "os package upgrade", + "reboot" + ], + "telegram_policy": "critical_owner_review_draft_only" + }, + { + "proposal_id": "stateful_backup_first_upgrade_packet", + "domain_id": "stateful_services", + "display_name": "Stateful backup-first 升級批准包", + "owner_agent": "openclaw", + "priority": "P1", + "risk_tier": "critical", + "status": "blocked_until_backup_and_restore_evidence", + "summary": "PostgreSQL/Redis/MinIO/Harbor/Gitea 更新前先要求備份新鮮度、restore drill 與資料相容性;不 restart、不 migration。", + "evidence_refs": [ + "docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json", + "docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json" + ], + "approval_gate": "stateful_backup_restore_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "backup freshness readback", + "restore drill owner packet", + "data compatibility smoke" + ], + "rollback_plan": [ + "restore snapshot selection", + "service-level rollback owner", + "read-only consistency check" + ], + "blocked_runtime_actions": [ + "stateful restart", + "database migration", + "restore execution" + ], + "telegram_policy": "critical_owner_review_draft_only" + }, + { + "proposal_id": "observability_agentops_stack_freshness", + "domain_id": "observability_stack", + "display_name": "AgentOps 觀測堆疊新鮮度", + "owner_agent": "hermes", + "priority": "P3", + "risk_tier": "medium", + "status": "proposal_ready_owner_review_required", + "summary": "追蹤 Prometheus、Alertmanager、Grafana、OTEL、Sentry 與 GenAI telemetry 更新,只產相容性矩陣與告警降噪提案。", + "evidence_refs": [ + "docs/evaluations/observability_contract_matrix_2026-06-04.json", + "docs/evaluations/service_health_gap_matrix_2026-06-04.json" + ], + "approval_gate": "workflow_runner_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "alert route compatibility matrix", + "dashboard readback smoke", + "noise reduction acceptance criteria" + ], + "rollback_plan": [ + "retain previous alert route", + "disable candidate dashboard flag", + "restore previous receiver policy" + ], + "blocked_runtime_actions": [ + "alert route write", + "workflow write", + "production write" + ], + "telegram_policy": "failure_only_digest_draft" + }, + { + "proposal_id": "telegram_bot_gateway_policy_refresh", + "domain_id": "telegram_bot_gateway", + "display_name": "Telegram Bot / Gateway policy refresh", + "owner_agent": "hermes", + "priority": "P2", + "risk_tier": "high", + "status": "blocked_until_no_send_receipt_gate", + "summary": "把日報、週報、月報與 action-required 告警收斂成 no-send digest、receipt readback 與 owner review;不得實發 Telegram。", + "evidence_refs": [ + "docs/evaluations/ai_agent_telegram_action_required_digest_policy_2026-06-04.json", + "docs/evaluations/ai_agent_report_live_delivery_approval_package_2026-06-04.json" + ], + "approval_gate": "telegram_gateway_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "sanitized message preview", + "dedup key review", + "receipt readback dry-run" + ], + "rollback_plan": [ + "mute candidate digest type", + "restore previous no-send policy", + "owner-visible failure receipt" + ], + "blocked_runtime_actions": [ + "Telegram direct send", + "Gateway queue write", + "Bot API call" + ], + "telegram_policy": "no_send_owner_review_required" + }, + { + "proposal_id": "mcp_rag_tool_registry_freshness", + "domain_id": "mcp_rag_tool_registry", + "display_name": "MCP / RAG / tool registry 新鮮度", + "owner_agent": "nemotron", + "priority": "P3", + "risk_tier": "medium", + "status": "proposal_ready_owner_review_required", + "summary": "整理 MCP server、RAG memory、tool permission 與 redaction policy 版本差異;不得啟用新外部工具或寫入記憶。", + "evidence_refs": [ + "docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json", + "docs/evaluations/ai_agent_interaction_learning_proof_2026-06-04.json" + ], + "approval_gate": "mcp_rag_tool_registry_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "tool permission matrix", + "RAG retention and redaction review", + "sandbox replay for tool-call changes" + ], + "rollback_plan": [ + "remove candidate tool from registry draft", + "retain previous memory contract", + "mark replay pack rejected" + ], + "blocked_runtime_actions": [ + "new external tool activation", + "memory write", + "paid external service" + ], + "telegram_policy": "weekly_digest_draft_only" + }, + { + "proposal_id": "gitea_runner_deploy_tooling_refresh", + "domain_id": "ci_cd_runner_tools", + "display_name": "Gitea runner / deploy tooling refresh", + "owner_agent": "hermes", + "priority": "P2", + "risk_tier": "high", + "status": "blocked_until_workflow_owner_packet", + "summary": "整理 Gitea runner、deploy marker、workflow attestation 與 smoke gate 更新提案;不得改 workflow、建立 PR 或 auto merge。", + "evidence_refs": [ + "docs/evaluations/gitea_workflow_runner_health_2026-06-04.json", + "docs/LOGBOOK.md" + ], + "approval_gate": "workflow_runner_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "runner attestation readback", + "deploy marker verification plan", + "public smoke route list" + ], + "rollback_plan": [ + "keep previous workflow", + "pause candidate deploy marker", + "manual owner review before merge" + ], + "blocked_runtime_actions": [ + "workflow write", + "PR creation", + "auto merge" + ], + "telegram_policy": "deployment_failure_digest_draft_only" + }, + { + "proposal_id": "mainstream_agent_framework_version_lifecycle_review", + "domain_id": "ai_agents_models", + "display_name": "主流 AI Agent / Framework 版本生命週期定期審核", + "owner_agent": "nemotron", + "priority": "P1", + "risk_tier": "high", + "status": "proposal_ready_owner_review_required", + "summary": "每週固定比對市場主流 AI Agent / SDK / framework official docs、PyPI、npm baseline,判斷是否需要 adapter design、offline replay、sandbox 或更新批准包;不安裝、不付費呼叫、不切 production。", + "evidence_refs": [ + "docs/ai/agent-market-watch-sources.v1.json", + "docs/ai/ai-technology-watch-sources.v1.json", + "docs/ai/agent-replacement-candidates.v1.json", + ".gitea/workflows/agent-market-watch.yaml", + ".gitea/workflows/ai-technology-watch.yaml", + ".gitea/workflows/agent-version-lifecycle-watch.yaml" + ], + "approval_gate": "market_replay_shadow_canary_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "official docs / PyPI / npm baseline readback", + "market integration review for changed candidates", + "offline replay adapter plan before any SDK install", + "cost, security, observability, rollback fields complete" + ], + "rollback_plan": [ + "keep OpenClaw incumbent route", + "revert candidate to watch-only registry status", + "retain previous version baseline and replay scorecard" + ], + "blocked_runtime_actions": [ + "SDK install", + "paid API call", + "provider route switch", + "OpenClaw replacement", + "workflow write", + "production write" + ], + "telegram_policy": "weekly_digest_draft_only" + } + ], + "cadence_matrix": [ + { + "cadence_id": "daily_repo_manifest_readback", + "frequency": "daily", + "scope": "repo-only manifests and committed snapshots", + "allowed_now": true, + "owner_agent": "hermes", + "output": "只讀差異摘要與過期來源標記" + }, + { + "cadence_id": "weekly_primary_source_market_review", + "frequency": "weekly", + "scope": "AI Agent / SDK / MCP / RAG primary source review", + "allowed_now": false, + "owner_agent": "nemotron", + "output": "外部來源批准包,未批准不得 live lookup" + }, + { + "cadence_id": "weekly_dependency_supply_chain_review", + "frequency": "weekly", + "scope": "dependency, image, SBOM, license, CVE proposal", + "allowed_now": false, + "owner_agent": "hermes", + "output": "dependency owner packet" + }, + { + "cadence_id": "monthly_host_k3s_maintenance_review", + "frequency": "monthly", + "scope": "host OS, kernel, K3s, Kubernetes skew", + "allowed_now": false, + "owner_agent": "openclaw", + "output": "maintenance window proposal" + }, + { + "cadence_id": "monthly_stateful_backup_restore_review", + "frequency": "monthly", + "scope": "PostgreSQL, Redis, MinIO, Harbor, Gitea, backup/restore", + "allowed_now": false, + "owner_agent": "openclaw", + "output": "backup-first approval package" + }, + { + "cadence_id": "triggered_critical_security_or_eol_review", + "frequency": "triggered", + "scope": "critical vulnerability, EOL, runner failure, delivery failure", + "allowed_now": false, + "owner_agent": "openclaw", + "output": "urgent owner review packet" + }, + { + "cadence_id": "weekly_ai_agent_framework_version_review", + "frequency": "weekly_tuesday_0900_taipei", + "scope": "OpenAI Agents SDK, LangGraph, Google ADK, Microsoft Agent Framework, CrewAI, LlamaIndex, Pydantic AI, AWS Strands, AutoGen migration, NVIDIA NeMo/Nemotron, Claude Agent SDK baseline versions", + "allowed_now": true, + "owner_agent": "nemotron", + "output": "no-write version drift queue, integration review, replay/update proposal, and blocked runtime action list" + } + ], + "approval_gate_matrix": [ + { + "gate_id": "market_replay_shadow_canary_review", + "risk_tier": "critical", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "market scorecard", + "sanitized replay fixture", + "shadow/canary stop conditions", + "cost and latency comparison" + ] + }, + { + "gate_id": "dependency_upgrade_owner_review", + "risk_tier": "high", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "manifest diff", + "test matrix", + "rollback branch plan" + ] + }, + { + "gate_id": "container_sbom_digest_owner_review", + "risk_tier": "high", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "SBOM plan", + "digest diff", + "deployment smoke" + ] + }, + { + "gate_id": "host_maintenance_window_owner_review", + "risk_tier": "critical", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "maintenance window", + "affected hosts", + "rollback owner", + "service smoke" + ] + }, + { + "gate_id": "k3s_version_skew_owner_review", + "risk_tier": "critical", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "version skew policy", + "node sequence", + "cluster health readback" + ] + }, + { + "gate_id": "stateful_backup_restore_owner_review", + "risk_tier": "critical", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "backup freshness", + "restore drill", + "data compatibility" + ] + }, + { + "gate_id": "telegram_gateway_owner_review", + "risk_tier": "high", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "sanitized preview", + "dedup key", + "receipt dry-run" + ] + }, + { + "gate_id": "workflow_runner_owner_review", + "risk_tier": "high", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "runner attestation", + "workflow diff", + "deploy smoke plan" + ] + }, + { + "gate_id": "mcp_rag_tool_registry_review", + "risk_tier": "medium", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "tool permission matrix", + "retention policy", + "redaction review" + ] + } + ], + "telegram_digest_contract": { + "status": "draft_only_no_send", + "direct_send_allowed": false, + "gateway_queue_write_allowed": false, + "bot_api_call_allowed": false, + "success_noise_suppression": true, + "draft_outputs": [ + "日報版本候選摘要", + "週報市場與依賴漂移摘要", + "月報維護窗與高風險 gate 摘要", + "action-required owner review 草稿" + ], + "redaction_required": true + }, + "runtime_boundaries": { + "read_only_update_proposal_allowed": true, + "schedule_activation_allowed": false, + "external_market_lookup_allowed": false, + "external_registry_lookup_allowed": false, + "external_cve_lookup_allowed": false, + "package_upgrade_allowed": false, + "lockfile_write_allowed": false, + "host_upgrade_allowed": false, + "os_package_upgrade_allowed": false, + "kernel_upgrade_allowed": false, + "k3s_upgrade_allowed": false, + "kubectl_command_allowed": false, + "node_drain_allowed": false, + "reboot_allowed": false, + "stateful_restart_allowed": false, + "database_migration_allowed": false, + "image_pull_allowed": false, + "docker_build_allowed": false, + "registry_push_allowed": false, + "workflow_write_allowed": false, + "pr_creation_allowed": false, + "auto_merge_allowed": false, + "provider_route_switch_allowed": false, + "openclaw_replacement_allowed": false, + "paid_api_call_allowed": false, + "secret_read_allowed": false, + "telegram_direct_send_allowed": false, + "telegram_gateway_queue_write_allowed": false, + "production_write_allowed": false, + "conversation_transcript_display_allowed": false + }, + "rollups": { + "domain_count": 12, + "proposal_count": 13, + "cadence_count": 7, + "approval_gate_count": 9, + "read_only_proposal_count": 13, + "approval_required_count": 13, + "critical_candidate_count": 4, + "high_candidate_count": 6, + "false_runtime_boundary_count": 29, + "auto_execution_allowed_count": 0, + "telegram_direct_send_count": 0, + "telegram_gateway_queue_write_count": 0, + "production_write_count": 0, + "update_allowed_count": 0, + "domain_ids": [ + "ai_agents_models", + "backend_python_packages", + "backup_dr_tooling", + "ci_cd_runner_tools", + "container_images", + "frontend_javascript_packages", + "host_os_packages", + "k3s_kubernetes_components", + "mcp_rag_tool_registry", + "observability_stack", + "stateful_services", + "telegram_bot_gateway" + ], + "proposal_ids": [ + "ai_agent_market_primary_source_radar", + "container_digest_sbom_pin_packet", + "frontend_pnpm_freshness_plan", + "gitea_runner_deploy_tooling_refresh", + "host_os_security_maintenance_packet", + "k3s_skew_maintenance_window_packet", + "mainstream_agent_framework_version_lifecycle_review", + "mcp_rag_tool_registry_freshness", + "observability_agentops_stack_freshness", + "openclaw_challenger_replay_bench", + "python_dependency_authority_alignment", + "stateful_backup_first_upgrade_packet", + "telegram_bot_gateway_policy_refresh" + ] + }, + "next_actions": [ + { + "task_id": "P1-006-agent-market-version-lifecycle-readback", + "priority": "P1", + "owner_agent": "nemotron", + "summary": "讀回主流 AI Agent market watch、technology radar、replacement candidates 與 weekly version lifecycle proposal,確認每個候選都有下一個 replay/update gate。", + "gate": "market_replay_shadow_canary_review" + }, + { + "task_id": "P2-414", + "priority": "P2", + "owner_agent": "hermes", + "summary": "把日報、週報、月報的版本生命週期欄位與 P2-413 proposal queue 對齊。", + "gate": "report_schema_update_no_send" + }, + { + "task_id": "P2-415", + "priority": "P1", + "owner_agent": "openclaw", + "summary": "設計 OpenClaw challenger replay bench 的 sanitized task set 與評分欄位。", + "gate": "market_replay_shadow_canary_review" + }, + { + "task_id": "P2-416", + "priority": "P2", + "owner_agent": "nemotron", + "summary": "把 MCP/RAG/tool registry 的保留、遮罩與權限欄位轉成 owner packet。", + "gate": "mcp_rag_tool_registry_review" + }, + { + "task_id": "P2-417", + "priority": "P1", + "owner_agent": "openclaw", + "summary": "建立 host/K3s/stateful 維護窗批准包的 owner review readback。", + "gate": "maintenance_window_owner_review" + } + ], + "periodic_schedule_bindings": [ + { + "binding_id": "agent_market_watch_weekly_monday_0900_taipei", + "workflow": ".gitea/workflows/agent-market-watch.yaml", + "cron": "0 1 * * 1", + "local_time": "Monday 09:00 Asia/Taipei", + "purpose": "weekly primary-source Agent market integration review", + "runtime_write_allowed": false + }, + { + "binding_id": "ai_technology_watch_every_6_hours", + "workflow": ".gitea/workflows/ai-technology-watch.yaml", + "cron": "0 */6 * * *", + "local_time": "every 6 hours UTC / every 6 hours Taipei-shifted", + "purpose": "near-real-time AI technology and Agent version drift radar", + "runtime_write_allowed": false + }, + { + "binding_id": "agent_version_lifecycle_weekly_tuesday_0900_taipei", + "workflow": ".gitea/workflows/agent-version-lifecycle-watch.yaml", + "cron": "proposal_only_weekly_tuesday_0900_taipei", + "local_time": "Tuesday 09:00 Asia/Taipei", + "purpose": "no-write AI Agent version/update proposal review queue", + "runtime_write_allowed": false + } + ] +} diff --git a/scripts/agents/agent-market-discovery-classify.py b/scripts/agents/agent-market-discovery-classify.py index b37a69c49..8f29b2049 100644 --- a/scripts/agents/agent-market-discovery-classify.py +++ b/scripts/agents/agent-market-discovery-classify.py @@ -1,9 +1,12 @@ #!/usr/bin/env python3 """ -Classify market discovery repositories using primary GitHub metadata. +Classify market discovery repositories using supplied primary-source metadata. The command is read-only. It does not add watch-registry entries, install SDKs, call LLMs, approve paid provider use, enter replay, or change production. +Under the GitHub account-safety freeze it does not fetch GitHub metadata; callers +must provide sanitized metadata explicitly, or drafts are marked policy-blocked +for offline review. """ from __future__ import annotations @@ -14,7 +17,6 @@ import json import sys from pathlib import Path from typing import Any -from urllib.request import Request, urlopen ROOT = Path(__file__).resolve().parents[2] @@ -33,7 +35,7 @@ def main() -> int: metadata = ( _read_json(Path(args.metadata)) if args.metadata - else _fetch_repository_metadata(discovery_review, args.timeout_seconds) + else _policy_blocked_repository_metadata(discovery_review) ) service = _load_service() report = service.run_agent_market_discovery_classification( @@ -49,9 +51,8 @@ def main() -> int: return 0 -def _fetch_repository_metadata( +def _policy_blocked_repository_metadata( discovery_review: dict[str, Any], - timeout_seconds: int, ) -> dict[str, dict[str, Any]]: metadata: dict[str, dict[str, Any]] = {} for draft in discovery_review.get("candidate_drafts") or []: @@ -60,43 +61,17 @@ def _fetch_repository_metadata( repo = str(draft.get("repository_full_name", "")) if not repo: continue - try: - metadata[repo] = _fetch_one_repository(repo, timeout_seconds) - except Exception as exc: # noqa: BLE001 - metadata[repo] = { - "full_name": repo, - "html_url": draft.get("html_url"), - "description": None, - "topics": [], - "stargazers_count": draft.get("stargazers_count_max"), - "error": str(exc), - } + metadata[repo] = { + "full_name": repo, + "html_url": draft.get("html_url"), + "description": None, + "topics": [], + "stargazers_count": draft.get("stargazers_count_max"), + "policy_blocker": "github_global_freeze_metadata_must_be_supplied_offline", + } return metadata -def _fetch_one_repository(repo: str, timeout_seconds: int) -> dict[str, Any]: - request = Request( - f"https://api.github.com/repos/{repo}", - headers={ - "User-Agent": "awoooi-agent-market-discovery-classifier/1.0", - "Accept": "application/vnd.github+json", - }, - ) - with urlopen(request, timeout=timeout_seconds) as response: # noqa: S310 - payload = json.loads(response.read().decode("utf-8")) - return { - "full_name": str(payload.get("full_name") or repo), - "html_url": payload.get("html_url"), - "description": payload.get("description"), - "homepage": payload.get("homepage"), - "topics": list(payload.get("topics") or []), - "language": payload.get("language"), - "stargazers_count": payload.get("stargazers_count"), - "pushed_at": payload.get("pushed_at"), - "archived": bool(payload.get("archived", False)), - } - - def _read_json(path: Path) -> dict[str, Any]: with path.open(encoding="utf-8") as handle: payload = json.load(handle)