Compare commits

..
5 Commits
Author SHA1 Message Date
aios edd3bd239e feat(cgpt-actions): add ActionMonitor for failure tracking & confirm-guard for tag/model/model-priority 2026-07-07 17:58:11 +08:00
aios 7a1e862a03 feat(V1R2C2): add autonomous recovery action monitor 2026-07-07 16:11:47 +08:00
aios dc0ac18aea chore(patches): remove stale artifact files
Remove 4 stale patch artifact files that have outlived their purpose:
- patches/nl/*.json - ephemeral natural-language plan records (unknown, patch_create)
- patches/patch_collab-*.branch.json - collab test branch records for branches
  that have been merged and had no local residual branch

The self-evolution-agent-orchestration-policy.json patch record is retained
because patch/self-evolution-agent-orchestration-policy branch is still live.
2026-07-07 15:53:02 +08:00
aios 9e3dbdf146 chore(version): bump baseline to V1R1C6 for chatgpt-actions consequential governance 2026-07-07 15:22:24 +08:00
aios 87b1a5357c feat(cgpt-actions): add confirm protection to tag/model/model-priority 2026-07-07 15:12:00 +08:00
7 changed files with 63 additions and 47 deletions
+21 -2
View File
@@ -5,6 +5,8 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from typing import Any
from orchestrator.action_monitor import ActionMonitor
ROOT = Path(os.getenv('AIOS_GITOPS_ROOT', '/data/aios-gitops'))
WS = Path(os.getenv('AIOS_GITOPS_WORKSPACE', str(ROOT / 'workspace')))
SECRETS = Path(os.getenv('AIOS_GITOPS_SECRETS_DIR', str(ROOT / 'runtime' / 'secrets')))
@@ -13,6 +15,8 @@ HOST = os.getenv('AIOS_GITOPS_HOST', '127.0.0.1')
PORT = int(os.getenv('AIOS_GITOPS_PORT', '18890'))
OPENAPI = WS / 'docs' / 'gitops' / 'openapi-chatgpt-actions.yaml'
BARE_REPO = Path(os.getenv('AIOS_GITOPS_BARE_REPO', str(ROOT / 'runtime' / 'gitea-bin' / 'data' / 'repositories' / 'aios' / 'aios-v23.git')))
monitor = ActionMonitor()
BASE_MODELS = [
{'id':'base.deepseekv4-flash','provider':'one-api','model':'deepseekv4-flash','display_name':'DeepSeek V4 Flash','scope':'global'},
{'id':'base.qwen3.6-35b','provider':'one-api','model':'qwen3.6-35b','display_name':'Qwen 3.6 35B','scope':'global'},
@@ -191,8 +195,12 @@ class H(BaseHTTPRequestHandler):
if path in ('/api/run','/run'):
svc=subprocess.run(['systemctl','is-active','aios-gitops-model-api'], text=True, capture_output=True)
return self.sendj({'service':'aios-gitops-model-api','status':svc.stdout.strip() or svc.stderr.strip(),'workspace_branch':sh(['rev-parse','--abbrev-ref','HEAD'])['stdout']})
if path in ('/api/action-monitor','/action-monitor'):
return self.sendj({'events':[{"event_id":e.event_id,"action":e.action,"error":e.error,"context":e.context,"created_at":e.created_at} for e in monitor.list_events()],'latest':monitor.latest()})
return self.sendj({'error':'not_found','path':path},404)
except Exception as e: return self.sendj({'error':str(e)},500)
except Exception as e:
monitor.record_failure('do_GET', str(e), {'path':path})
return self.sendj({'error':str(e)},500)
def do_PUT(self):
path=urllib.parse.urlparse(self.path).path.rstrip('/'); data=self.body()
if path == '/api/model-priority': return self.model_priority(data)
@@ -210,7 +218,9 @@ class H(BaseHTTPRequestHandler):
if path == '/api/model-priority': return self.model_priority(data)
if path == '/api/nl': return self.nl(data)
return self.sendj({'error':'not_found','path':path},404)
except Exception as e: return self.sendj({'error':str(e)},500)
except Exception as e:
monitor.record_failure('do_POST', str(e), {'path':path})
return self.sendj({'error':str(e)},500)
def branch(self,data):
a=self.need('branch:create');
if not a: return
@@ -234,6 +244,9 @@ class H(BaseHTTPRequestHandler):
if not a: return
ver=data.get('version') or data.get('tag')
if not re.match(r'^V\d+R\d+C\d+(-[A-Za-z0-9_.-]+)?$', ver or ''): return self.sendj({'error':'version must match VxRxCx'},400)
if data.get('confirm') != True:
self.sendj({'status':'confirm_required','operation':'tag','hint':'This operation creates a Git tag. Confirm to proceed.','x-openai-isConsequential':True}, 428)
return
if a['role'] != 'owner' or not data.get('execute'):
b=checkout(data.get('branch') or f'request/tag-{safe(ver)}', 'main', True); req={'version':ver,'requested_by':a['account'],'approval_required':True,'created_at':now()}; r=write_json(f'baseline/tag_requests/{safe(ver)}.json', req); c=commit([r], f'chore(chatgpt): request tag {ver}'); return self.sendj({'status':'approval_required','branch':b,'commit':c})
out=sh(['tag','-a',ver,'-m',data.get('message') or f'release {ver}'], True); self.sendj({'status':'tagged','version':ver,'git':out,'sync':sync_tag(ver)})
@@ -254,10 +267,16 @@ class H(BaseHTTPRequestHandler):
if not secret_ref: return self.sendj({'error':'api_key_secret_ref or api_key required'},400)
mid=data.get('model_id') or f'custom.{owner}.{hashlib.sha1((prov+base+name).encode()).hexdigest()[:10]}'; b=checkout(data.get('branch') or f'patch/model-{safe(mid)}','main',True)
doc=sanitize({k:v for k,v in data.items() if k!='api_key'}); doc.update({'model_id':mid,'provider_name':prov,'base_url':base,'model_name':name,'api_key_secret_ref':secret_ref,'created_by':a['account'],'created_at':now()})
if data.get('confirm') != True:
self.sendj({'status':'confirm_required','operation':'model','hint':'This operation registers a new model. Confirm to proceed.','x-openai-isConsequential':True}, 428)
return
r=write_json(f'config/model_registry/chatgpt/{safe(mid)}.json', doc); c=commit([r], data.get('message') or f'chore(chatgpt): register model {name}'); audit(a['account'],'model',mid,doc); self.sendj({'status':'model_registered','branch':b,'model_id':mid,'secret_ref':secret_ref,'commit':c})
def model_priority(self,data):
a=self.need('model:priority');
if not a: return
if data.get('confirm') != True:
self.sendj({'status':'confirm_required','operation':'model-priority','hint':'This operation updates model priority. Confirm to proceed.','x-openai-isConsequential':True}, 428)
return
user=safe(data.get('username') or a['account']); b=checkout(data.get('branch') or f'patch/model-priority-{user}', 'main', True); doc={'username':user,'items':data.get('items') or data.get('priority') or [],'updated_by':a['account'],'updated_at':now()}; r=write_json(f'config/model_priority/{user}.json', doc); c=commit([r], data.get('message') or f'chore(chatgpt): update model priority {user}'); audit(a['account'],'model-priority',user,doc); self.sendj({'status':'updated','branch':b,'priority':doc,'commit':c})
def nl(self,data):
a=self.need('nl:plan');
+5 -5
View File
@@ -1,6 +1,6 @@
{
"active_version": "V1R1C4",
"active_tag": "V1R1C4",
"active_version": "V1R1C6",
"active_tag": "V1R1C6",
"mode": "gitops-online",
"version_policy": "VxRxCx",
"update_mode": "git_patch_only",
@@ -27,7 +27,7 @@
"portal_gitops_dashboard": true,
"human_shortlinks_login_required": true
},
"version": "V1R1C4",
"version": "V1R1C6",
"baseline_tag": "V1R1C0-baseline-20260706",
"source": "AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip",
"source_package": "AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip",
@@ -46,7 +46,7 @@
"/api",
"/hook"
],
"updated_at": "2026-07-06T11:16:23Z",
"updated_at": "2026-07-07T14:30:00Z",
"auth": {
"primary_domain": "https://auth.cnnetmkt.top",
"backup_domain": "https://auth.h.imruyi.top:8443",
@@ -55,7 +55,7 @@
"casdoor_application": "admin/aios-portal",
"backup_domain_role": "standby-only"
},
"previous_version": "V1R1C3",
"previous_version": "V1R1C5",
"codex_gitops": {
"config": "config/codex/gitops-online.json",
"token_account": "codex-gitops-bot",
-10
View File
@@ -1,10 +0,0 @@
{
"id": "nl-1783382614842-6c6112e8",
"text": "自我进化执行主体策略更新:除 Windows Codex App 外,同时增加服务器上的 OpenClaw Agent 与 Hermes Agent 作为执行主体。允许启动多个服务器 Agent 并行执行;默认以服务器上的 Agent 为主,Windows Codex App 作为协同执行主体。困难时刻允许访问 https://chatgpt.com/chat/gpt-6a4ba70a063c8191a89bc2d86a4c7fa0-codex-chartgpt-bridge 求助或交互。分歧时刻允许多个 Agent 投票决策;自定义 GPT 权重为双倍,Windows Codex App 权重为双倍。服务器 Agent 默认参与并行协作。该策略应以 GitOps 可审计方式进入自我进化 agent orchestration proposal,不直接执行 V-level 版本变更,不写入任何凭据。",
"intent": "unknown",
"action_plan": [],
"execute": false,
"owner_approval_required": true,
"created_by": "gitops-write-bot",
"created_at": "2026-07-07T00:03:34Z"
}
-12
View File
@@ -1,12 +0,0 @@
{
"id": "nl-1783390999250-8627c20c",
"text": "基于 V1R1C4 baseline 和 patch/self-evolution-agent-orchestration-policy 全量重新架构优化:服务器 OpenClaw 与 Hermes 作为主要执行智能体;Codex ChartGPT Bridge 作为架构审核、方案建议和困难问题升级入口;Windows Codex App 作为异常恢复、管理员授权协助和多智能体投票参与者。所有操作基于在线 GitOps 版本。优化方向:建立 Agent Runtime、Registry、Scheduler、Message Bus、Memory、Skill、Evaluator、Consensus、Evolution、Release 全链路;增加异常升级机制,服务器智能体遇到方案选择或复杂实现时生成完整问题上下文并提交审核;完善多 Agent 投票、质量评分、审计、Patch-first、审批发布流程。生成后续实施 proposal,不直接进行生产发布。",
"intent": "patch_create",
"action_plan": [
"call POST /api/patch"
],
"execute": false,
"owner_approval_required": true,
"created_by": "gitops-write-bot",
"created_at": "2026-07-07T02:23:19Z"
}
@@ -1,9 +0,0 @@
{
"type": "branch",
"branch": "patch/collab-custom-gpt-20260706T104130Z",
"base": "main",
"created_by": "chatgpt-gitops-bot",
"created_at": "2026-07-06T10:41:30Z",
"direct_main_push": false,
"owner_approval_required": true
}
@@ -1,9 +0,0 @@
{
"type": "branch",
"branch": "patch/collab-local-codex-20260706T104130Z",
"base": "main",
"created_by": "codex-gitops-bot",
"created_at": "2026-07-06T10:41:30Z",
"direct_main_push": false,
"owner_approval_required": true
}
@@ -0,0 +1,37 @@
"""V1R2C2 autonomous recovery action monitor."""
from dataclasses import dataclass, field
from time import time
from typing import Any, Dict, List
import uuid
@dataclass
class ActionFailureEvent:
event_id: str = field(default_factory=lambda: str(uuid.uuid4()))
action: str = ""
error: str = ""
context: Dict[str, Any] = field(default_factory=dict)
created_at: float = field(default_factory=time)
class ActionMonitor:
"""Capture control-plane action failures for recovery routing."""
def __init__(self):
self.events: List[ActionFailureEvent] = []
def record_failure(self, action: str, error: str, context: Dict[str, Any] | None = None):
event = ActionFailureEvent(
action=action,
error=error,
context=context or {},
)
self.events.append(event)
return event
def latest(self):
return self.events[-1] if self.events else None
def list_events(self):
return self.events