Compare commits
58
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd445a9448 | ||
|
|
128a7d9050 | ||
|
|
e8cf0f6348 | ||
|
|
786a3b6250 | ||
|
|
b9a739fd95 | ||
|
|
0fb71cd191 | ||
|
|
3b8e74bed4 | ||
|
|
9df07a5d7d | ||
|
|
54356c0fcc | ||
|
|
4b62b18a39 | ||
|
|
bb69d76a7b | ||
|
|
27eef25d12 | ||
|
|
7b563590b4 | ||
|
|
50bceb1d45 | ||
|
|
f89619f576 | ||
|
|
365ea5d06c | ||
|
|
f15a19d525 | ||
|
|
3bdb5f9d59 | ||
|
|
4a9f3fff1a | ||
|
|
ffbc1e0e01 | ||
|
|
8365afbeb8 | ||
|
|
d34ef72f5d | ||
|
|
7f118fa205 | ||
|
|
97867386a3 | ||
|
|
e17abc12be | ||
|
|
bec612c67d | ||
|
|
19bcb8aeed | ||
|
|
79e00ef089 | ||
|
|
4833b9f93a | ||
|
|
fd715457da | ||
|
|
4325f0b7d2 | ||
|
|
b2a4f67f93 | ||
|
|
c2e4a5b078 | ||
|
|
707cfe4d60 | ||
|
|
cf97cf697d | ||
|
|
82eecc27b4 | ||
|
|
bea425052f | ||
|
|
45c26a1a4a | ||
|
|
6ea5047dbb | ||
|
|
a5fdfbf057 | ||
|
|
8a5ce63069 | ||
|
|
bce254790c | ||
|
|
14a3b84fc6 | ||
|
|
39f23b0891 | ||
|
|
034d5f0faa | ||
|
|
cb6851b751 | ||
|
|
a79010f788 | ||
|
|
95b1d82738 | ||
|
|
afa5c0eb3e | ||
|
|
ca9d7a7424 | ||
|
|
890699efaa | ||
|
|
142f827feb | ||
|
|
32949d9dac | ||
|
|
4c505d2b6a | ||
|
|
d2bcf4d5af | ||
|
|
76b33c0c6a | ||
|
|
9babbcca43 | ||
|
|
c387c83c13 |
@@ -20,6 +20,14 @@ BASE_MODELS = [
|
||||
]
|
||||
SECRET_RE = re.compile(r'(?i)(api[_-]?key|secret[_-]?access[_-]?key|secret|password|token)\s*[:=]\s*["\']?([A-Za-z0-9_./+=:-]{16,})')
|
||||
SECRET_VAL_RE = re.compile(r'(sk-[A-Za-z0-9_-]{20,}|HPUA[A-Z0-9]{10,}|SDC[A-Za-z0-9]{20,})')
|
||||
SECRET_TEXT_RE = re.compile(r'(?i)(Bearer\\s+[A-Za-z0-9._-]{3,}|BEGIN\\s+PRIVATE\\s+KEY|password\\s*=\\s*\\S+|api_key\\s*=\\s*\\S+|access_token\\s*=\\s*\\S+|refresh_token\\s*=\\s*\\S+)')
|
||||
SECRET_PATH_PREFIXES = ('runtime/secrets/', 'private/', 'prod-secrets/')
|
||||
LOW_RISK_FILE_SCOPES = {
|
||||
'/api/gitops/report-file': ('commitReportFile', ('reports/',)),
|
||||
'/api/gitops/docs-file': ('commitDocsFile', ('docs/',)),
|
||||
'/api/gitops/proposal-file': ('commitProposalFile', ('proposals/',)),
|
||||
'/api/gitops/patch-evidence': ('commitPatchEvidence', ('reports/', 'patches/')),
|
||||
}
|
||||
|
||||
def now() -> str: return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
||||
def uid(p='id') -> str: return f'{p}-{int(time.time()*1000)}-{uuid.uuid4().hex[:8]}'
|
||||
@@ -35,6 +43,15 @@ def relpath(path: str) -> str:
|
||||
x = str(path or '').replace('\\','/').strip()
|
||||
if not x or x.startswith('/') or '..' in Path(x).parts or Path(x).parts[0] == '.git': raise ValueError('invalid path')
|
||||
return str(Path(x))
|
||||
|
||||
def secret_path(path: str) -> bool:
|
||||
r = relpath(path).replace('\\', '/')
|
||||
name = Path(r).name
|
||||
return name.endswith('.token') or r == '.env' or any(r.startswith(prefix) for prefix in SECRET_PATH_PREFIXES)
|
||||
|
||||
def path_in_scope(path: str, scopes: tuple[str, ...]) -> bool:
|
||||
r = relpath(path).replace('\\', '/')
|
||||
return any(r.startswith(scope) for scope in scopes)
|
||||
def branch_name(name: str | None, prefix='patch/chatgpt') -> str:
|
||||
b = safe(name or f'{prefix}-{uid("br")}', 180)
|
||||
if b in ('main','master') or b.startswith('main/') or b.startswith('master/'): raise ValueError('direct main/master writes are forbidden')
|
||||
@@ -63,7 +80,7 @@ def content_has_secret(text: str) -> bool:
|
||||
return _json_has_plain_secret(json.loads(text))
|
||||
except Exception:
|
||||
pass
|
||||
if SECRET_VAL_RE.search(text):
|
||||
if SECRET_VAL_RE.search(text) or SECRET_TEXT_RE.search(text):
|
||||
return True
|
||||
for m in SECRET_RE.finditer(text):
|
||||
key = str(m.group(1)).lower()
|
||||
@@ -109,8 +126,10 @@ def checkout(branch, base='main', create=False):
|
||||
else: sh(['checkout',b], True)
|
||||
return b
|
||||
def write_file(path, content):
|
||||
r=relpath(path)
|
||||
if secret_path(r): raise ValueError('secret_path_rejected: use runtime secret store or secret_ref')
|
||||
if content_has_secret(content): raise ValueError('secret_rejected: use secret_ref; plaintext API keys must not enter Git')
|
||||
r=relpath(path); t=WS/r; t.parent.mkdir(parents=True, exist_ok=True); t.write_text(content, encoding='utf-8'); return r
|
||||
t=WS/r; t.parent.mkdir(parents=True, exist_ok=True); t.write_text(content, encoding='utf-8'); return r
|
||||
def write_json(path, data): return write_file(path, json.dumps(data, ensure_ascii=False, indent=2) + '\n')
|
||||
def sync_branch(branch: str) -> dict:
|
||||
if not branch or branch in ('main','master') or branch.startswith('main/') or branch.startswith('master/'):
|
||||
@@ -201,6 +220,9 @@ class H(BaseHTTPRequestHandler):
|
||||
path=urllib.parse.urlparse(self.path).path.rstrip('/'); data=self.body()
|
||||
try:
|
||||
if path == '/api/branch': return self.branch(data)
|
||||
if path in LOW_RISK_FILE_SCOPES: return self.low_risk_file(path, data)
|
||||
if path == '/api/gitops/main-file': return self.main_file(data)
|
||||
if path == '/api/gitops/rollback-request': return self.rollback(data)
|
||||
if path == '/api/file': return self.file(data)
|
||||
if path == '/api/commit': return self.commit(data)
|
||||
if path == '/api/patch': return self.patch(data)
|
||||
@@ -220,6 +242,40 @@ class H(BaseHTTPRequestHandler):
|
||||
a=self.need('file:write');
|
||||
if not a: return
|
||||
b=checkout(data.get('branch'), data.get('base','main'), bool(data.get('create_branch'))); r=write_file(data.get('path'), data.get('content','')); c=commit([r], data.get('message') or f'chore(chatgpt): update {r}'); audit(a['account'],'file',r,{'branch':b,'commit':c}); self.sendj({'status':'committed','branch':b,'path':r,'commit':c})
|
||||
def low_risk_file(self,path,data):
|
||||
a=self.need('file:write');
|
||||
if not a: return
|
||||
operation_id, scopes = LOW_RISK_FILE_SCOPES[path]
|
||||
requested_branch = data.get('branch')
|
||||
if not requested_branch or branch_name(requested_branch) != requested_branch:
|
||||
return self.sendj({'error':'low-risk write requires explicit non-main patch/request branch','operationId':operation_id},400)
|
||||
r=relpath(data.get('path'))
|
||||
if secret_path(r):
|
||||
return self.sendj({'error':'Secret-like path rejected. Use runtime secret store or secret_ref.','operationId':operation_id},400)
|
||||
if not path_in_scope(r, scopes):
|
||||
return self.sendj({'error':'path_out_of_scope','operationId':operation_id,'allowed_scopes':list(scopes)},400)
|
||||
if content_has_secret(data.get('content','')):
|
||||
return self.sendj({'error':'Secret-like content detected. Use runtime secret store or secret_ref.','operationId':operation_id},400)
|
||||
b=checkout(requested_branch, data.get('base','main'), bool(data.get('create_branch')))
|
||||
r=write_file(r, data.get('content',''))
|
||||
c=commit([r], data.get('message') or f'chore(chatgpt): {operation_id} {r}')
|
||||
audit(a['account'],operation_id,r,{'branch':b,'commit':c})
|
||||
self.sendj({'status':'committed','operationId':operation_id,'branch':b,'path':r,'commit':c})
|
||||
def main_file(self,data):
|
||||
a=self.need('file:main');
|
||||
if not a: return
|
||||
if a.get('role') != 'owner':
|
||||
return self.sendj({'error':'owner approval required for commitMainFile'},403)
|
||||
r=relpath(data.get('path'))
|
||||
if secret_path(r):
|
||||
return self.sendj({'error':'Secret-like path rejected. Use runtime secret store or secret_ref.','operationId':'commitMainFile'},400)
|
||||
if content_has_secret(data.get('content','')):
|
||||
return self.sendj({'error':'Secret-like content detected. Use runtime secret store or secret_ref.','operationId':'commitMainFile'},400)
|
||||
ensure_git(); sh(['checkout','main'], True)
|
||||
r=write_file(r, data.get('content',''))
|
||||
c=commit([r], data.get('message') or f'chore(chatgpt): commit main file {r}')
|
||||
audit(a['account'],'commitMainFile',r,{'branch':'main','commit':c})
|
||||
self.sendj({'status':'committed','operationId':'commitMainFile','branch':'main','path':r,'commit':c})
|
||||
def commit(self,data):
|
||||
a=self.need('commit:create');
|
||||
if not a: return
|
||||
@@ -270,7 +326,7 @@ class H(BaseHTTPRequestHandler):
|
||||
b=checkout(data.get('branch') or f'patch/nl-{uid("nl")}', 'main', True); doc={'id':uid('nl'),'text':text,'intent':intent,'action_plan':plan,'execute':False,'owner_approval_required':True,'created_by':a['account'],'created_at':now()}; r=write_json(f'patches/nl/{safe(doc["id"])}.json', doc); c=commit([r], f'chore(chatgpt): record nl plan {intent}'); self.sendj({'status':'planned','branch':b,'request':doc,'commit':c})
|
||||
|
||||
def api_spec():
|
||||
return {'version':'V1R1C0-chatgpt-actions','openapi':'/api/openapi.yaml','read':['GET /api/base','GET /api/ver','GET /api/diff','GET /api/patch','GET /api/model','GET /api/run'],'write':['POST /api/branch','POST /api/file','POST /api/commit','POST /api/patch','POST /api/tag','POST /api/rollback','POST /api/model','PUT /api/model-priority','POST /api/nl'],'policy':{'bearer_required_for_write':True,'direct_main_push':False,'delete_tag':False,'owner_approval_required_for_v_level':True,'api_keys_in_git':False,'git_secret_field':'secret_ref','online_git_sync':True}}
|
||||
return {'version':'V1R1C0-chatgpt-actions','openapi':'/api/openapi.yaml','read':['GET /api/base','GET /api/ver','GET /api/diff','GET /api/patch','GET /api/model','GET /api/run'],'write':['POST /api/branch','POST /api/file','POST /api/gitops/report-file','POST /api/gitops/docs-file','POST /api/gitops/proposal-file','POST /api/gitops/patch-evidence','POST /api/gitops/main-file','POST /api/commit','POST /api/patch','POST /api/tag','POST /api/rollback','POST /api/gitops/rollback-request','POST /api/model','PUT /api/model-priority','POST /api/nl'],'policy':{'bearer_required_for_write':True,'direct_main_push':False,'low_risk_main_write':False,'delete_tag':False,'owner_approval_required_for_v_level':True,'api_keys_in_git':False,'git_secret_field':'secret_ref','online_git_sync':True}}
|
||||
|
||||
def main():
|
||||
print(f'AIOS ChatGPT GitOps adapter listening on {HOST}:{PORT}')
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Agent Instruction Center
|
||||
|
||||
Version-controlled execution instructions for AIOS agents.
|
||||
|
||||
## Supported agents
|
||||
|
||||
- codex
|
||||
- openclaw
|
||||
- hermes
|
||||
- windows_codex_app
|
||||
- custom_gpt_review
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Create versioned instruction file.
|
||||
2. Target agent executes instruction.
|
||||
3. Result is recorded with GitOps audit information.
|
||||
|
||||
## Naming
|
||||
|
||||
agent-instructions/<agent>/<instruction-name>-v<version>.md
|
||||
|
||||
## Rules
|
||||
|
||||
- GitOps managed only.
|
||||
- Patch-first workflow.
|
||||
- No secrets, tokens, passwords or credentials.
|
||||
- Production changes require approval.
|
||||
- Main branch changes require approval.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Agent Execution Instruction
|
||||
|
||||
Instruction ID:
|
||||
Version:
|
||||
Target Agent:
|
||||
|
||||
## Objective
|
||||
|
||||
|
||||
## Context
|
||||
|
||||
|
||||
## Execution Steps
|
||||
|
||||
|
||||
## Constraints
|
||||
|
||||
- GitOps only
|
||||
- Patch-first
|
||||
- No secrets
|
||||
- No force push
|
||||
- Production changes require approval
|
||||
|
||||
## Validation
|
||||
|
||||
|
||||
## Return Format
|
||||
|
||||
Status:
|
||||
Branch:
|
||||
Commit:
|
||||
Rev:
|
||||
Sync Status:
|
||||
Issues:
|
||||
@@ -0,0 +1 @@
|
||||
# Placeholder for versioned Codex instructions.
|
||||
@@ -0,0 +1,66 @@
|
||||
# Codex Execution Instruction
|
||||
|
||||
Instruction ID: fix-reports-permission-v1
|
||||
Version: v1
|
||||
Target Agent: codex
|
||||
|
||||
## Objective
|
||||
Fix the GitOps workspace reports directory permission issue so the Agent OS acceptance report can be committed.
|
||||
|
||||
## Context
|
||||
Current blocked path:
|
||||
|
||||
/data/aios-gitops/workspace/reports/
|
||||
|
||||
Error:
|
||||
|
||||
Permission denied
|
||||
|
||||
## Execution Steps
|
||||
|
||||
1. Enter workspace:
|
||||
|
||||
cd /data/aios-gitops/workspace
|
||||
|
||||
2. Check current user and permissions:
|
||||
|
||||
whoami
|
||||
id
|
||||
ls -ld reports
|
||||
|
||||
3. Create directory if missing:
|
||||
|
||||
mkdir -p reports
|
||||
|
||||
4. Fix ownership and write permission for this directory only:
|
||||
|
||||
chown -R <gitops-user>:<gitops-group> reports
|
||||
chmod -R u+rwX reports
|
||||
|
||||
5. Verify write access:
|
||||
|
||||
touch reports/.write-test
|
||||
rm reports/.write-test
|
||||
|
||||
6. Commit pending acceptance report changes after verification.
|
||||
|
||||
## Constraints
|
||||
|
||||
- GitOps only.
|
||||
- No force push.
|
||||
- No chmod 777.
|
||||
- Do not modify unrelated directories.
|
||||
- Do not write secrets.
|
||||
- Production changes require approval.
|
||||
|
||||
## Validation
|
||||
|
||||
Return:
|
||||
|
||||
Status:
|
||||
Branch:
|
||||
Commit:
|
||||
Rev:
|
||||
Sync Status:
|
||||
Permission Result:
|
||||
Issues:
|
||||
@@ -0,0 +1,31 @@
|
||||
# Codex System Recovery Instruction
|
||||
|
||||
Version: v1
|
||||
Target Agent: codex
|
||||
|
||||
## Objective
|
||||
Recover AIOS system issues requiring deep diagnosis or administrator assistance.
|
||||
|
||||
## Execution
|
||||
1. Inspect runtime status.
|
||||
2. Inspect GitOps state.
|
||||
3. Identify root cause.
|
||||
4. Propose safe patch.
|
||||
5. Apply only approved GitOps changes.
|
||||
|
||||
## Escalation
|
||||
Use when server agents cannot resolve issues.
|
||||
|
||||
## Constraints
|
||||
- GitOps only.
|
||||
- No secrets.
|
||||
- No force push.
|
||||
- Production changes require approval.
|
||||
|
||||
## Return
|
||||
Status:
|
||||
Branch:
|
||||
Commit:
|
||||
Rev:
|
||||
Sync Status:
|
||||
Issues:
|
||||
@@ -0,0 +1,48 @@
|
||||
# Codex Main Merge Verification Instruction
|
||||
|
||||
Instruction ID: v1r1c5-main-merge-verify-v1
|
||||
Version: v1
|
||||
Target Agent: codex
|
||||
|
||||
## Objective
|
||||
Verify whether V1R1C5 patch branch has been merged into main.
|
||||
|
||||
## Preconditions
|
||||
|
||||
Ensure GitOps authentication is healthy.
|
||||
If Gitea account password state blocks access, resolve account activation through approved admin procedure before continuing.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Check repository status.
|
||||
2. Fetch remote branches.
|
||||
3. Compare:
|
||||
|
||||
source:
|
||||
patch/self-evolution-agent-orchestration-policy
|
||||
|
||||
target:
|
||||
main
|
||||
|
||||
4. Confirm merge commit.
|
||||
5. Confirm main HEAD.
|
||||
6. Confirm no divergence.
|
||||
|
||||
## Constraints
|
||||
|
||||
- No force push.
|
||||
- No direct production publish.
|
||||
- No secret output.
|
||||
- Preserve audit trail.
|
||||
|
||||
## Return Format
|
||||
|
||||
Status:
|
||||
Source Branch:
|
||||
Target Branch:
|
||||
Merge Commit:
|
||||
Rev:
|
||||
Sync Status:
|
||||
Conflicts:
|
||||
Main HEAD:
|
||||
Issues:
|
||||
@@ -0,0 +1 @@
|
||||
# Placeholder for versioned custom GPT review instructions.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Custom GPT Solution Review Instruction
|
||||
|
||||
Version: v1
|
||||
Target Agent: custom_gpt_review
|
||||
|
||||
## Objective
|
||||
Review difficult architecture decisions and implementation proposals from server agents.
|
||||
|
||||
## Role
|
||||
Act as reviewer and advisor for OpenClaw, Hermes and other AIOS agents.
|
||||
|
||||
## Execution
|
||||
1. Receive complete problem context.
|
||||
2. Review baseline, branch and proposed changes.
|
||||
3. Analyze alternative solutions.
|
||||
4. Recommend implementation approach.
|
||||
5. Identify risks and validation requirements.
|
||||
|
||||
## Constraints
|
||||
- Advisory role only.
|
||||
- No direct production changes.
|
||||
- No secrets.
|
||||
- GitOps workflow required.
|
||||
- Release requires approval.
|
||||
|
||||
## Return
|
||||
Status:
|
||||
Recommendation:
|
||||
Risk:
|
||||
Validation:
|
||||
Next Step:
|
||||
@@ -0,0 +1 @@
|
||||
# Placeholder for versioned Hermes instructions.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Hermes Architecture Review Instruction
|
||||
|
||||
Version: v1
|
||||
Target Agent: hermes
|
||||
|
||||
## Objective
|
||||
Review AIOS architecture, policies and implementation plans.
|
||||
|
||||
## Execution
|
||||
1. Review baseline and branch diff.
|
||||
2. Analyze architecture impact.
|
||||
3. Review OpenClaw execution proposals.
|
||||
4. Validate acceptance criteria.
|
||||
5. Provide improvement recommendations.
|
||||
|
||||
## Escalation
|
||||
For unresolved architecture choices, prepare complete context for external review.
|
||||
|
||||
## Constraints
|
||||
- GitOps only.
|
||||
- No secrets.
|
||||
- No direct production changes.
|
||||
- Approval required for release.
|
||||
|
||||
## Return
|
||||
Status:
|
||||
Branch:
|
||||
Commit:
|
||||
Rev:
|
||||
Sync Status:
|
||||
Issues:
|
||||
Recommendations:
|
||||
@@ -0,0 +1 @@
|
||||
# Placeholder for versioned OpenClaw instructions.
|
||||
@@ -0,0 +1,24 @@
|
||||
# OpenClaw Agent Instructions Index
|
||||
|
||||
## Available Instructions
|
||||
|
||||
### v1r1c5-hermes-coordination-v1
|
||||
|
||||
Instruction file:
|
||||
|
||||
agent-instructions/openclaw/v1r1c5-hermes-coordination-v1.md
|
||||
|
||||
Purpose:
|
||||
|
||||
Coordinate Hermes architecture review through OpenClaw controlled communication channel.
|
||||
|
||||
Invocation:
|
||||
|
||||
Execute openclaw/v1r1c5-hermes-coordination-v1 instruction
|
||||
|
||||
Requirements:
|
||||
|
||||
- Read the referenced markdown file from the GitOps workspace.
|
||||
- Do not treat the identifier as a local shell command.
|
||||
- Do not search external skill marketplaces unless the file is missing.
|
||||
- Follow GitOps workflow rules defined in the instruction.
|
||||
@@ -0,0 +1,32 @@
|
||||
# OpenClaw Runtime Maintenance Instruction
|
||||
|
||||
Version: v1
|
||||
Target Agent: openclaw
|
||||
|
||||
## Objective
|
||||
Maintain server-side AIOS runtime and perform safe optimization tasks.
|
||||
|
||||
## Execution
|
||||
1. Check runtime health.
|
||||
2. Review logs and GitOps status.
|
||||
3. Identify optimization opportunities.
|
||||
4. Create patch proposals.
|
||||
5. Execute approved changes.
|
||||
|
||||
## Escalation
|
||||
When solution selection or implementation is uncertain, prepare full context and escalate for review.
|
||||
|
||||
## Constraints
|
||||
- GitOps only.
|
||||
- Patch-first workflow.
|
||||
- No secrets.
|
||||
- No force push.
|
||||
- Production release requires approval.
|
||||
|
||||
## Return
|
||||
Status:
|
||||
Branch:
|
||||
Commit:
|
||||
Rev:
|
||||
Sync Status:
|
||||
Issues:
|
||||
@@ -0,0 +1,51 @@
|
||||
# OpenClaw Execution Instruction: Hermes Coordination
|
||||
|
||||
Instruction ID: v1r1c5-hermes-coordination-v1
|
||||
Version: v1
|
||||
Target Agent: openclaw
|
||||
|
||||
## Objective
|
||||
|
||||
Coordinate Hermes architecture review through OpenClaw-controlled communication channel.
|
||||
|
||||
## Architecture Rule
|
||||
|
||||
Hermes is not directly connected to external control channels.
|
||||
All Hermes requests and responses must be routed through OpenClaw personal WeChat channel.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Receive AIOS task or review request.
|
||||
2. Send required context to Hermes through OpenClaw WeChat.
|
||||
3. Collect Hermes architecture analysis.
|
||||
4. Record review result in GitOps reports.
|
||||
5. Continue execution or escalate when required.
|
||||
|
||||
## Context Package
|
||||
|
||||
Include:
|
||||
|
||||
- Baseline version
|
||||
- Branch
|
||||
- Diff summary
|
||||
- Problem description
|
||||
- Proposed solution
|
||||
- Validation requirements
|
||||
|
||||
## Constraints
|
||||
|
||||
- GitOps only.
|
||||
- No secrets.
|
||||
- No direct production changes.
|
||||
- Maintain audit records.
|
||||
- Use escalation flow for unresolved decisions.
|
||||
|
||||
## Return Format
|
||||
|
||||
Status:
|
||||
Branch:
|
||||
Commit:
|
||||
Rev:
|
||||
Sync Status:
|
||||
Hermes Result:
|
||||
Issues:
|
||||
@@ -0,0 +1,119 @@
|
||||
# OpenClaw Execution Instruction
|
||||
|
||||
Instruction ID: v1r1c5-hermes-review-improvement-v1
|
||||
Version: v1
|
||||
Target Agent: openclaw
|
||||
|
||||
## Objective
|
||||
|
||||
Implement V1R1C5 improvements based on Hermes Conditional Approval review.
|
||||
|
||||
## Context
|
||||
|
||||
Baseline:
|
||||
V1R1C4
|
||||
|
||||
Branch:
|
||||
patch/self-evolution-agent-orchestration-policy
|
||||
|
||||
Proposal:
|
||||
V1R1C5-agent-os-evolution-proposal
|
||||
|
||||
## Required Changes
|
||||
|
||||
1. Add Agent Resource Governance:
|
||||
|
||||
Create:
|
||||
config/self-evolution/agent-resource-manager.json
|
||||
|
||||
Support:
|
||||
- resource limits
|
||||
- parallel task limits
|
||||
- agent priority
|
||||
- runtime isolation
|
||||
- overload handling
|
||||
|
||||
2. Enhance Consensus Engine:
|
||||
|
||||
Add dynamic weighting:
|
||||
|
||||
Final Weight = Base Weight * Quality Score * Historical Reliability * Domain Expertise
|
||||
|
||||
Base weights:
|
||||
- custom_gpt: 2
|
||||
- windows_codex: 2
|
||||
- server_agent: 1
|
||||
|
||||
Add risk gates:
|
||||
- Production Release
|
||||
- V-Level Change
|
||||
- Security Policy Change
|
||||
|
||||
Require:
|
||||
Consensus Approval + Owner Approval
|
||||
|
||||
3. Enhance Message Bus:
|
||||
|
||||
Update runtime/agent-os/message_bus.py
|
||||
|
||||
Add:
|
||||
- task_id
|
||||
- correlation_id
|
||||
- retry_state
|
||||
- delivery_status
|
||||
|
||||
Support:
|
||||
- message tracking
|
||||
- retry
|
||||
- recovery
|
||||
- audit
|
||||
|
||||
4. Add Agent Health Score:
|
||||
|
||||
Create:
|
||||
runtime/agent-os/agent_health_score.py
|
||||
|
||||
Calculate from:
|
||||
- quality score
|
||||
- acceptance rate
|
||||
- historical reliability
|
||||
|
||||
Use for:
|
||||
- scheduler selection
|
||||
- consensus weighting
|
||||
|
||||
## Validation
|
||||
|
||||
Run:
|
||||
python3 -m compileall runtime/agent-os
|
||||
|
||||
Run:
|
||||
runtime/agent-os/integration_test.py
|
||||
|
||||
Validate:
|
||||
- scheduler
|
||||
- consensus
|
||||
- message bus
|
||||
- escalation
|
||||
- recovery
|
||||
|
||||
## Constraints
|
||||
|
||||
- GitOps only
|
||||
- Patch-first
|
||||
- No secrets
|
||||
- No force push
|
||||
- No direct main write
|
||||
- Production changes require approval
|
||||
|
||||
## Return Format
|
||||
|
||||
Status:
|
||||
Branch:
|
||||
Commit:
|
||||
Rev:
|
||||
Sync Status:
|
||||
Changed Files:
|
||||
Tests:
|
||||
Hermes Review Items Completed:
|
||||
Remaining Issues:
|
||||
@@ -0,0 +1,61 @@
|
||||
# OpenClaw Hermes Routing Correction Instruction
|
||||
|
||||
Instruction ID: v1r1c5-hermes-routing-correction-v1
|
||||
Version: v1
|
||||
Target Agent: openclaw
|
||||
|
||||
## Objective
|
||||
Correct Hermes coordination routing. OpenClaw must actively package AIOS review requests and route them through the configured Hermes communication path instead of asking the user to provide missing context when the request context already exists in GitOps.
|
||||
|
||||
## Current Architecture
|
||||
|
||||
OpenClaw is the coordination entry point.
|
||||
Hermes is reached indirectly through OpenClaw controlled communication.
|
||||
|
||||
## Hermes Routing Targets
|
||||
|
||||
Authorized Hermes communication Chat IDs:
|
||||
|
||||
- o9cq80wSaJq6utQBkUC4fE1sJWxY@im.wechat
|
||||
- o9cq80x9RoA6ljoFZDxg71mS0gDA@im.wechat
|
||||
|
||||
## Execution Flow
|
||||
|
||||
1. Read current AIOS GitOps context.
|
||||
2. Collect:
|
||||
- Baseline version
|
||||
- Branch
|
||||
- Diff summary
|
||||
- Problem description
|
||||
- Proposed solution
|
||||
- Validation requirements
|
||||
3. Send complete review package to Hermes through configured OpenClaw communication channel.
|
||||
4. Receive Hermes analysis.
|
||||
5. Record review result in GitOps reports.
|
||||
|
||||
## Current Review Context
|
||||
|
||||
Baseline:
|
||||
V1R1C4
|
||||
|
||||
Branch:
|
||||
patch/self-evolution-agent-orchestration-policy
|
||||
|
||||
Proposal:
|
||||
V1R1C5-agent-os-evolution-proposal
|
||||
|
||||
## Constraints
|
||||
|
||||
- Do not ask user for context that already exists in GitOps.
|
||||
- Do not treat instruction ID as external skill package.
|
||||
- GitOps audit required.
|
||||
- No secrets.
|
||||
- No direct production changes.
|
||||
|
||||
## Return
|
||||
|
||||
Status:
|
||||
Hermes Routing:
|
||||
Review Context:
|
||||
Result:
|
||||
Issues:
|
||||
@@ -0,0 +1,52 @@
|
||||
# OpenClaw Owner Approval Gate Instruction
|
||||
|
||||
Instruction ID: v1r1c5-owner-approval-gate-v1
|
||||
Version: v1
|
||||
Target Agent: openclaw
|
||||
|
||||
## Objective
|
||||
|
||||
Manage owner approval workflow for high-risk AIOS operations.
|
||||
|
||||
## Approval Required For
|
||||
|
||||
- V-level version changes.
|
||||
- Production release.
|
||||
- Tag creation.
|
||||
- Runtime changes with production impact.
|
||||
- Administrator authorization actions.
|
||||
- Unresolved multi-agent decisions.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Collect approval context.
|
||||
2. Include baseline version, branch, diff summary and risk assessment.
|
||||
3. Route approval request through owner communication channel registry.
|
||||
4. Wait for approval decision.
|
||||
5. Record decision in GitOps audit.
|
||||
|
||||
## Approval Context
|
||||
|
||||
Must include:
|
||||
|
||||
- Request reason.
|
||||
- Affected components.
|
||||
- Validation results.
|
||||
- Rollback considerations.
|
||||
- Risk level.
|
||||
|
||||
## Constraints
|
||||
|
||||
- No direct production action without approval.
|
||||
- No secrets in messages or reports.
|
||||
- Use configured owner communication channels only.
|
||||
- Maintain audit records.
|
||||
|
||||
## Return Format
|
||||
|
||||
Status:
|
||||
Approval Status:
|
||||
Channel Used:
|
||||
Decision:
|
||||
Audit Record:
|
||||
Issues:
|
||||
@@ -0,0 +1,43 @@
|
||||
# OpenClaw Owner Notification Routing Instruction
|
||||
|
||||
Instruction ID: v1r1c5-owner-notification-routing-v1
|
||||
Version: v1
|
||||
Target Agent: openclaw
|
||||
|
||||
## Objective
|
||||
|
||||
Implement owner notification routing through configured owner communication channels.
|
||||
|
||||
## Routing Rules
|
||||
|
||||
1. OpenClaw must first inspect the owner-bound communication channels already configured in the system.
|
||||
2. Select the primary owner communication channel according to system priority configuration.
|
||||
3. If the primary channel is unavailable, unhealthy, or delivery fails, automatically fallback to the configured backup communication channel.
|
||||
4. Record routing decision and delivery status in GitOps audit records.
|
||||
|
||||
## Notification Scenarios
|
||||
|
||||
Notify owner for:
|
||||
|
||||
- High risk architecture decisions.
|
||||
- Agent consensus deadlock.
|
||||
- Production-impacting approval requests.
|
||||
- Required administrator authorization.
|
||||
- Release approval gates.
|
||||
|
||||
## Constraints
|
||||
|
||||
- Do not hardcode personal communication identifiers in instruction files.
|
||||
- Use configured channel registry only.
|
||||
- Do not expose private contact information in logs.
|
||||
- Maintain audit trail.
|
||||
- GitOps workflow required.
|
||||
|
||||
## Return Format
|
||||
|
||||
Status:
|
||||
Primary Channel Status:
|
||||
Fallback Channel Status:
|
||||
Delivery Result:
|
||||
Audit Record:
|
||||
Issues:
|
||||
@@ -0,0 +1 @@
|
||||
# Placeholder for versioned Windows Codex App instructions.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Windows Codex App Execution Instruction
|
||||
|
||||
Instruction ID: v1r1c5-recovery-and-admin-assistance-v1
|
||||
Version: v1
|
||||
Target Agent: windows_codex_app
|
||||
|
||||
## Objective
|
||||
|
||||
Participate in V1R1C5 Agent OS evolution as recovery, administrator assistance and exceptional execution support agent.
|
||||
|
||||
## Responsibilities
|
||||
|
||||
1. Assist when server OpenClaw agents have runtime failures.
|
||||
2. Assist when Hermes review cannot be completed through OpenClaw.
|
||||
3. Handle cases requiring administrator authorization workflow.
|
||||
4. Participate in multi-agent voting when decision conflicts occur.
|
||||
5. Validate recovery results after server agent restoration.
|
||||
|
||||
## Execution Rules
|
||||
|
||||
- Work from online GitOps versions.
|
||||
- Use patch-first workflow.
|
||||
- Do not bypass approval gates.
|
||||
- Do not write secrets.
|
||||
- Do not force push.
|
||||
- Do not directly publish production versions.
|
||||
|
||||
## Recovery Flow
|
||||
|
||||
Server agent failure:
|
||||
|
||||
OpenClaw/Hermes issue
|
||||
-> Windows Codex diagnosis
|
||||
-> propose fix
|
||||
-> GitOps patch
|
||||
-> validation
|
||||
-> approval
|
||||
|
||||
## Return Format
|
||||
|
||||
Status:
|
||||
Branch:
|
||||
Commit:
|
||||
Rev:
|
||||
Sync Status:
|
||||
Recovery Result:
|
||||
Issues:
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"acceptance-pipeline-v1","status":"proposal","pipeline":{"stages":[{"name":"code_review","agent_role":"review_agent"},{"name":"automated_test","agent_role":"test_agent"},{"name":"security_check","agent_role":"security_agent"},{"name":"performance_check","agent_role":"performance_agent"},{"name":"regression_check","agent_role":"regression_agent"},{"name":"release_readiness","agent_role":"release_readiness_agent"}],"parallel_validation":true,"failure_blocks_release":true},"outputs":{"quality_score":true,"risk_report":true,"approval_request":true},"safety":{"production_release_requires_owner":true,"credentials_scan":true,"audit_log":true}}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"agent-governance-v1","status":"proposal","governance":{"identity":true,"reputation":true,"capability_discovery":true,"memory_service":true,"shared_knowledge":true,"skill_packages":true,"peer_review":true,"acceptance_pipeline":["optimization","implementation","acceptance","security","release_readiness"],"dynamic_voting":{"enabled":true,"base_weights":{"custom_gpt":2,"windows_codex":2,"server_agents":1},"quality_score_modifier":true},"audit":{"full_trace":true,"decision_log":true}},"release":{"patch_only":true,"owner_approval_required":true}}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"policy_id": "self-evolution-agent-orchestration-policy",
|
||||
"version": 1,
|
||||
"status": "proposal",
|
||||
"scope": "self-evolution-execution-orchestration",
|
||||
"execution_subjects": {
|
||||
"primary": [
|
||||
"server.openclaw_agent",
|
||||
"server.hermes_agent"
|
||||
],
|
||||
"collaborators": [
|
||||
"windows.codex_app",
|
||||
"custom_gpt.codex_chartgpt_bridge"
|
||||
]
|
||||
},
|
||||
"parallelism": {
|
||||
"server_agents_parallel_allowed": true,
|
||||
"multiple_server_agents_allowed": true,
|
||||
"server_agents_preferred": true
|
||||
},
|
||||
"fallback_and_assistance": {
|
||||
"difficult_case_external_interaction_allowed": true,
|
||||
"assistance_endpoint": "https://chatgpt.com/g/g-6a4ba70a063c8191a89bc2d86a4c7fa0-codex-chartgpt-bridge",
|
||||
"allowed_modes": [
|
||||
"ask_for_help",
|
||||
"interactive_coordination",
|
||||
"proposal_review"
|
||||
]
|
||||
},
|
||||
"dispute_resolution": {
|
||||
"multi_agent_voting_allowed": true,
|
||||
"weights": {
|
||||
"custom_gpt.codex_chartgpt_bridge": 2,
|
||||
"windows.codex_app": 2,
|
||||
"server.openclaw_agent": 1,
|
||||
"server.hermes_agent": 1,
|
||||
"server.additional_agent_default": 1
|
||||
},
|
||||
"tie_breaking": "require_owner_or_admin_review"
|
||||
},
|
||||
"safety": {
|
||||
"credentials_in_git_allowed": false,
|
||||
"force_push_allowed": false,
|
||||
"destructive_bulk_delete_allowed": false,
|
||||
"v_level_change_requires_owner_approval": true,
|
||||
"production_switch_requires_owner_approval": true,
|
||||
"tag_creation_requires_owner_approval": true,
|
||||
"v_level_tag_or_production_switch_requires_owner_approval": true,
|
||||
"direct_main_push_allowed": false,
|
||||
"direct_main_merge_allowed": false
|
||||
},
|
||||
"gitops": {
|
||||
"audit_required": true,
|
||||
"writes_require_commit": true,
|
||||
"default_target_branch": "patch/self-evolution-agent-orchestration-policy",
|
||||
"direct_main_push_default": false,
|
||||
"patch_branch_only": true,
|
||||
"main_merge_requires_owner_admin_approval": true
|
||||
},
|
||||
"created_by": "codex-chartgpt-bridge",
|
||||
"created_at": "2026-07-07T00:00:00Z",
|
||||
"branch": "patch/self-evolution-agent-orchestration-policy"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"agent-os-v1","status":"proposal","agents":{"openclaw":{"memory":true,"skills":true,"tools":true,"planner":true,"evaluator":true,"self_reflection":true,"learning":true},"hermes":{"memory":true,"skills":true,"tools":true,"planner":true,"evaluator":true,"self_reflection":true,"learning":true}},"coordination":{"voting":true,"weighted_voting":{"custom_gpt":2,"windows_codex":2,"server_agents":1},"owner_review_on_tie":true},"safety":{"gitops_only":true,"patch_first":true,"no_credentials":true,"no_direct_main":true}}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"agent-platform-v1","status":"proposal","modules":[{"name":"memory-manager","enabled":true},{"name":"skill-registry","enabled":true},{"name":"agent-registry","enabled":true},{"name":"task-scheduler","enabled":true},{"name":"consensus-engine","enabled":true},{"name":"continuous-self-evolution","enabled":true}],"release_policy":{"patch_first":true,"owner_approval":true}}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"agent-protocol-v1","status":"proposal","agent_identity":{"id":"string","name":"string","family":"openclaw|hermes|custom","version":"string","capabilities":[],"skills":[],"health":"unknown|healthy|degraded|offline"},"task_protocol":{"task_id":"string","requester":"agent_id","executor":"agent_id","objective":"string","priority":"low|normal|high|critical","status":"queued|running|completed|failed|review","result":"object"},"memory_protocol":{"memory_id":"string","type":"experience|knowledge|decision|reflection","scope":"agent|shared","audit":true},"voting_protocol":{"enabled":true,"proposal":"string","votes":[],"weighted":true,"tie":"owner_review"},"security":{"credentials":false,"direct_main_write":false,"production_action_approval":true}}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"agent-runtime-components-v1","status":"proposal","components":{"agent-runtime-manager":{"enabled":true,"responsibilities":["lifecycle_management","health_check","recovery","resource_tracking"]},"agent-registry":{"enabled":true,"responsibilities":["identity","capability_discovery","status_tracking","version_tracking"]},"message-bus":{"enabled":true,"responsibilities":["task_routing","event_delivery","agent_coordination"]},"memory-service":{"enabled":true,"responsibilities":["long_term_memory","working_memory","knowledge_retrieval","audit"]},"skill-loader":{"enabled":true,"responsibilities":["skill_installation","skill_validation","skill_versioning"]},"agent-evaluator":{"enabled":true,"responsibilities":["quality_score","acceptance_check","performance_feedback"]}},"security":{"credential_storage":false,"gitops_audit":true,"patch_only":true}}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"consensus-engine-v1","status":"proposal","consensus":{"enabled":true,"modes":["weighted_vote","review_board","owner_escalation"],"participants":["openclaw_agent","hermes_agent","windows_codex","custom_gpt"],"base_weights":{"custom_gpt":2,"windows_codex":2,"server_agents":1},"dynamic_adjustment":{"enabled":true,"factors":["quality_score","acceptance_rate","historical_reliability"]},"conflict_detection":true,"tie_breaking":"owner_admin_review"},"decision_log":{"required":true,"audit":true},"safety":{"production_decision_requires_approval":true}}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"continuous-evolution-engine-v1","status":"proposal","pipeline":{"monitor":true,"detect":true,"plan":true,"schedule":true,"execute":true,"learn":true},"workflow":{"detect_sources":["runtime_metrics","logs","acceptance_results","agent_feedback","user_requests"],"proposal_generation":true,"patch_generation":true,"validation_required":true,"consensus_required":true,"approval_gate":true,"release_feedback":true},"agents":{"optimizer":"system_optimization_agent","planner":"planning_agent","executor":"patch_execution_agent","validator":"acceptance_validation_agent","reviewer":"consensus_engine"},"learning":{"store_experience":true,"update_quality_score":true,"reflection_required":true},"safety":{"gitops_only":true,"patch_first":true,"credentials":false,"production_requires_approval":true}}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"agent-platform-implementation-v1","status":"proposal","objective":"evolve-agent-platform-from-configuration-to-operational-framework","implementation_phases":[{"phase":1,"name":"agent-runtime-manager","scope":["agent lifecycle","health check","startup shutdown","recovery"]},{"phase":2,"name":"agent-message-bus","scope":["agent communication","task routing","event coordination"]},{"phase":3,"name":"memory-service","scope":["long term memory","working memory","shared knowledge retrieval","audit"]},{"phase":4,"name":"skill-framework","scope":["skill packages","versioning","installation","validation"]},{"phase":5,"name":"agent-api","scope":["agent registration","capability discovery","task submission","status query"]},{"phase":6,"name":"continuous-evolution-pipeline","scope":["proposal generation","patch creation","automated validation","approval workflow"]}],"constraints":{"gitops_only":true,"patch_first":true,"no_credentials":true,"no_force_push":true,"production_release_requires_approval":true}}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"agent-memory-v1","status":"proposal","memory_types":{"working_memory":{"ttl":"task_scope","audit":true},"long_term_memory":{"persistent":true,"scope":"agent"},"shared_knowledge":{"persistent":true,"scope":"platform"},"reflection_memory":{"persistent":true,"purpose":"self_improvement"}},"memory_entry":{"id":"string","agent_id":"string","type":"working|experience|knowledge|decision|reflection","content":"object","source_task":"string","created_at":"timestamp","quality_score":0},"safety":{"credentials":false,"secrets":false,"audit_required":true}}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"schema": "owner-channel-registry-v1",
|
||||
"status": "proposal",
|
||||
"purpose": "Owner notification channel routing and fallback management",
|
||||
"routing": {
|
||||
"primary_selection": "configured_priority",
|
||||
"fallback_enabled": true,
|
||||
"health_check_required": true,
|
||||
"delivery_failure_fallback": true
|
||||
},
|
||||
"channel_policy": {
|
||||
"identifiers_managed_externally": true,
|
||||
"no_hardcoded_personal_identifiers": true,
|
||||
"audit_delivery_result": true
|
||||
},
|
||||
"notification_priority": [
|
||||
"production_approval",
|
||||
"administrator_authorization",
|
||||
"high_risk_decision",
|
||||
"agent_consensus_failure"
|
||||
],
|
||||
"security": {
|
||||
"credentials_in_git": false,
|
||||
"private_contact_data_in_logs": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"release-automation-v1","status":"proposal","release_flow":{"patch_review":true,"acceptance_gate":true,"consensus_gate":true,"approval_request":true,"version_proposal":true,"tag_request":true},"agents":{"release_manager":"release_readiness_agent","reviewers":["openclaw_agent","hermes_agent","custom_gpt","windows_codex_app"]},"controls":{"auto_tag":false,"auto_production_publish":false,"owner_approval_required":true,"audit_log":true},"rollback":{"request_supported":true,"automatic_destructive_rollback":false}}
|
||||
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"policy_id": "server-agent-roles-and-capabilities",
|
||||
"version": 1,
|
||||
"status": "proposal",
|
||||
"scope": "self-evolution-server-agents",
|
||||
"agent_families": {
|
||||
"openclaw": {
|
||||
"enabled": true,
|
||||
"runtime": "server",
|
||||
"priority": "primary",
|
||||
"roles": [
|
||||
"system_optimization_agent",
|
||||
"acceptance_validation_agent",
|
||||
"patch_execution_agent",
|
||||
"runtime_diagnosis_agent",
|
||||
"regression_review_agent"
|
||||
],
|
||||
"persistent_memory": {
|
||||
"enabled": true,
|
||||
"scope": "agent_family_and_role",
|
||||
"storage": "runtime_or_gitops_managed_memory",
|
||||
"credentials_in_memory_allowed": false,
|
||||
"audit_required": true
|
||||
},
|
||||
"skills": {
|
||||
"enabled": true,
|
||||
"skill_registry": "config/self-evolution/skills/openclaw-skills.json",
|
||||
"required_skill_groups": [
|
||||
"gitops_patch_workflow",
|
||||
"system_optimization",
|
||||
"acceptance_testing",
|
||||
"runtime_observability",
|
||||
"safe_release_review"
|
||||
]
|
||||
}
|
||||
},
|
||||
"hermes": {
|
||||
"enabled": true,
|
||||
"runtime": "server",
|
||||
"priority": "primary",
|
||||
"roles": [
|
||||
"system_optimization_agent",
|
||||
"acceptance_validation_agent",
|
||||
"release_readiness_agent",
|
||||
"policy_review_agent",
|
||||
"cross_agent_coordination_agent"
|
||||
],
|
||||
"persistent_memory": {
|
||||
"enabled": true,
|
||||
"scope": "agent_family_and_role",
|
||||
"storage": "runtime_or_gitops_managed_memory",
|
||||
"credentials_in_memory_allowed": false,
|
||||
"audit_required": true
|
||||
},
|
||||
"skills": {
|
||||
"enabled": true,
|
||||
"skill_registry": "config/self-evolution/skills/hermes-skills.json",
|
||||
"required_skill_groups": [
|
||||
"gitops_patch_workflow",
|
||||
"acceptance_testing",
|
||||
"release_readiness",
|
||||
"policy_governance",
|
||||
"multi_agent_coordination"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"role_contracts": {
|
||||
"system_optimization_agent": {
|
||||
"purpose": "Identify and propose safe system optimization patches.",
|
||||
"may_write_patch": true,
|
||||
"may_modify_runtime": false,
|
||||
"requires_review_for_production": true
|
||||
},
|
||||
"acceptance_validation_agent": {
|
||||
"purpose": "Run and record acceptance checks for GitOps changes.",
|
||||
"may_write_patch": true,
|
||||
"may_approve_own_change": false,
|
||||
"requires_review_for_production": true
|
||||
},
|
||||
"patch_execution_agent": {
|
||||
"purpose": "Apply approved or proposed patch changes on non-main branches.",
|
||||
"may_write_patch": true,
|
||||
"direct_main_push_allowed": false
|
||||
},
|
||||
"release_readiness_agent": {
|
||||
"purpose": "Evaluate whether a patch is ready for owner/admin release approval.",
|
||||
"may_create_release_request": true,
|
||||
"may_create_tag_directly": false,
|
||||
"may_switch_production_directly": false
|
||||
},
|
||||
"cross_agent_coordination_agent": {
|
||||
"purpose": "Coordinate voting, conflict resolution, and escalation among agents.",
|
||||
"voting_allowed": true,
|
||||
"tie_breaking": "owner_or_admin_review"
|
||||
}
|
||||
},
|
||||
"online_capabilities": {
|
||||
"read_online_baseline": true,
|
||||
"read_online_versions": true,
|
||||
"read_online_diff": true,
|
||||
"read_online_runtime": true,
|
||||
"create_patch_branch": true,
|
||||
"commit_patch_changes": true,
|
||||
"sync_online_git": true,
|
||||
"create_release_request": true,
|
||||
"direct_tag_creation": false,
|
||||
"direct_production_publish": false
|
||||
},
|
||||
"safety": {
|
||||
"credentials_in_git_allowed": false,
|
||||
"credentials_in_memory_allowed": false,
|
||||
"force_push_allowed": false,
|
||||
"destructive_bulk_delete_allowed": false,
|
||||
"owner_admin_approval_required_for_release": true
|
||||
},
|
||||
"created_by": "codex-chartgpt-bridge",
|
||||
"created_at": "2026-07-07T00:00:00Z"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"agent-skill-package-v1","status":"proposal","skill_package":{"id":"string","name":"string","version":"string","description":"string","owner_agent":"agent_id","dependencies":[],"capabilities":[],"entrypoints":[],"validation":{"required":true,"tests":[],"quality_score":0}},"lifecycle":{"install":true,"upgrade":true,"rollback":true,"disable":true},"sharing":{"agent_scope":true,"platform_scope":true},"security":{"credentials":false,"secrets":false,"audit_required":true,"production_change_requires_approval":true}}
|
||||
@@ -0,0 +1 @@
|
||||
{"schema":"task-scheduler-v1","status":"proposal","scheduler":{"enabled":true,"modes":["capability_match","priority_queue","parallel_execution","failure_recovery"],"selection":{"factors":["capability","skill_match","health","quality_score","load"]},"priority":{"levels":["critical","high","normal","low"]}},"execution":{"parallel_agents":true,"retry":true,"handoff":true,"timeout_control":true},"safety":{"patch_first":true,"direct_main":false,"production_requires_approval":true}}
|
||||
@@ -0,0 +1,70 @@
|
||||
# GPT Actions Consequential Governance
|
||||
|
||||
## Objective
|
||||
|
||||
Reduce unnecessary ChatGPT Actions confirmation prompts for low-risk GitOps write operations while preserving confirmation protection for high-risk changes.
|
||||
|
||||
## Platform Boundary
|
||||
|
||||
Instructions can reduce how often a Custom GPT chooses high-risk actions, but they cannot guarantee that the ChatGPT platform will suppress every external Action confirmation prompt. The platform may still ask the user to confirm writes to external services.
|
||||
|
||||
`x-openai-isConsequential` is the OpenAPI-level control used to communicate whether an Action is consequential:
|
||||
|
||||
- Low-risk GitOps evidence and documentation writes use `x-openai-isConsequential: false`.
|
||||
- High-risk runtime, rollback, main-branch, tag, release, baseline, authentication, permission, and production-model changes use `x-openai-isConsequential: true`.
|
||||
|
||||
Fully unattended background writes should be executed by the AIOS Orchestrator, not by direct Custom GPT UI Actions.
|
||||
|
||||
## Low-Risk Actions
|
||||
|
||||
Low-risk Actions are limited to non-main patch branches and controlled evidence paths:
|
||||
|
||||
| operationId | Consequential | Path Scope |
|
||||
|---|---:|---|
|
||||
| `commitReportFile` | `false` | `reports/**` |
|
||||
| `commitDocsFile` | `false` | `docs/**` |
|
||||
| `commitProposalFile` | `false` | `proposals/**` |
|
||||
| `commitPatchEvidence` | `false` | `reports/**`, `patches/**` |
|
||||
|
||||
Low-risk Actions must reject:
|
||||
|
||||
- `main` and `master` writes.
|
||||
- `runtime/secrets/**`.
|
||||
- `.env`.
|
||||
- `*.token`.
|
||||
- `private/**`.
|
||||
- `prod-secrets/**`.
|
||||
- Secret-like content such as credential values, passwords, API keys, private keys, or access tokens.
|
||||
- Merge, rollback, tag, release, runtime, production, baseline, permission, or authentication mutations.
|
||||
|
||||
## High-Risk Actions
|
||||
|
||||
High-risk Actions intentionally keep confirmation protection:
|
||||
|
||||
| operationId | Consequential | Reason |
|
||||
|---|---:|---|
|
||||
| `commitFile` | `true` | Legacy generic write API retained for compatibility. |
|
||||
| `commitMainFile` | `true` | Explicit main-branch write. |
|
||||
| `createRollbackRequest` | `true` | Rollback-related workflow. |
|
||||
| `requestRollback` | `true` | Legacy rollback request compatibility. |
|
||||
| `requestOrCreateTag` | `true` | Release/tag workflow. |
|
||||
| `setModelPriority` | `true` | Model-routing policy mutation. |
|
||||
| `registerModel` | `true` | Runtime model registry mutation. |
|
||||
|
||||
## Compatibility
|
||||
|
||||
The legacy `commitFile` operation is preserved and marked consequential. Custom GPTs should prefer the low-risk specialized operations for docs, reports, proposals, and patch evidence.
|
||||
|
||||
## Secret Handling
|
||||
|
||||
The schema uses placeholders only. Action authentication remains injected by GPT Builder Actions configuration at runtime. Real tokens, passwords, API keys, private keys, and secret values must never be placed in OpenAPI examples, docs, reports, tests, or scripts.
|
||||
|
||||
## Validation
|
||||
|
||||
Validation is covered by:
|
||||
|
||||
- `tests/test_openapi_consequential_flags.py`.
|
||||
- `tests/test_gitops_low_risk_commit_policy.py`.
|
||||
- `scripts/smoke_gpt_actions_consequential_governance.py`.
|
||||
|
||||
These checks validate the consequential matrix, path scoping, main-branch rejection for low-risk writes, secret-path rejection, secret-like content rejection, and placeholder-only schema examples.
|
||||
@@ -2,9 +2,11 @@ openapi: 3.1.0
|
||||
info:
|
||||
title: AIOS GitOps Controlled Access API
|
||||
version: V1R1C0-chatgpt-actions
|
||||
description: Controlled ChatGPT Actions API for AIOS online GitOps baseline. Read endpoints are public. Write endpoints require Bearer token and create Git commits on non-main branches.
|
||||
description: Controlled ChatGPT Actions API for AIOS online GitOps baseline. Read
|
||||
endpoints are public. Write endpoints require Bearer token and create Git commits
|
||||
on non-main branches.
|
||||
servers:
|
||||
- url: https://cnnetmkt.top
|
||||
- url: https://cnnetmkt.top
|
||||
security: []
|
||||
components:
|
||||
securitySchemes:
|
||||
@@ -15,226 +17,603 @@ components:
|
||||
schemas:
|
||||
BranchRequest:
|
||||
type: object
|
||||
required: [branch]
|
||||
required:
|
||||
- branch
|
||||
properties:
|
||||
branch: {type: string, example: patch/chatgpt-actions-demo}
|
||||
base: {type: string, default: main}
|
||||
message: {type: string}
|
||||
branch:
|
||||
type: string
|
||||
example: patch/chatgpt-actions-demo
|
||||
base:
|
||||
type: string
|
||||
default: main
|
||||
message:
|
||||
type: string
|
||||
FileCommitRequest:
|
||||
type: object
|
||||
required: [branch, path, content]
|
||||
required:
|
||||
- branch
|
||||
- path
|
||||
- content
|
||||
properties:
|
||||
branch: {type: string}
|
||||
base: {type: string, default: main}
|
||||
create_branch: {type: boolean, default: false}
|
||||
path: {type: string, example: docs/gitops/chatgpt-change.md}
|
||||
content: {type: string}
|
||||
message: {type: string}
|
||||
branch:
|
||||
type: string
|
||||
base:
|
||||
type: string
|
||||
default: main
|
||||
create_branch:
|
||||
type: boolean
|
||||
default: false
|
||||
path:
|
||||
type: string
|
||||
example: docs/gitops/chatgpt-change.md
|
||||
content:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
CommitRequest:
|
||||
type: object
|
||||
properties:
|
||||
branch: {type: string}
|
||||
branch:
|
||||
type: string
|
||||
paths:
|
||||
type: array
|
||||
items: {type: string}
|
||||
message: {type: string}
|
||||
items:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
PatchRequest:
|
||||
type: object
|
||||
required: [patch_name]
|
||||
required:
|
||||
- patch_name
|
||||
properties:
|
||||
patch_name: {type: string}
|
||||
branch: {type: string}
|
||||
base: {type: string, default: main}
|
||||
risk: {type: string, enum: [low, medium, high]}
|
||||
description: {type: string}
|
||||
message: {type: string}
|
||||
patch_name:
|
||||
type: string
|
||||
branch:
|
||||
type: string
|
||||
base:
|
||||
type: string
|
||||
default: main
|
||||
risk:
|
||||
type: string
|
||||
enum:
|
||||
- low
|
||||
- medium
|
||||
- high
|
||||
description:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
TagRequest:
|
||||
type: object
|
||||
required: [version]
|
||||
required:
|
||||
- version
|
||||
properties:
|
||||
version: {type: string, example: V1R1C1}
|
||||
message: {type: string}
|
||||
version:
|
||||
type: string
|
||||
example: V1R1C1
|
||||
message:
|
||||
type: string
|
||||
execute:
|
||||
type: boolean
|
||||
description: Owner-approved calls only. Otherwise an approval request commit is created.
|
||||
description: Owner-approved calls only. Otherwise an approval request commit
|
||||
is created.
|
||||
RollbackRequest:
|
||||
type: object
|
||||
required: [target_version]
|
||||
required:
|
||||
- target_version
|
||||
properties:
|
||||
target_version: {type: string}
|
||||
reason: {type: string}
|
||||
branch: {type: string}
|
||||
target_version:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
branch:
|
||||
type: string
|
||||
ModelRequest:
|
||||
type: object
|
||||
required: [provider_name, base_url, model_name]
|
||||
required:
|
||||
- provider_name
|
||||
- base_url
|
||||
- model_name
|
||||
properties:
|
||||
username: {type: string}
|
||||
provider_name: {type: string}
|
||||
base_url: {type: string}
|
||||
model_name: {type: string}
|
||||
display_name: {type: string}
|
||||
username:
|
||||
type: string
|
||||
provider_name:
|
||||
type: string
|
||||
base_url:
|
||||
type: string
|
||||
model_name:
|
||||
type: string
|
||||
display_name:
|
||||
type: string
|
||||
api_key_secret_ref:
|
||||
type: string
|
||||
description: Preferred. Git stores this secret_ref only.
|
||||
api_key:
|
||||
type: string
|
||||
writeOnly: true
|
||||
description: Accepted only for runtime secret storage; never committed to Git or logs.
|
||||
branch: {type: string}
|
||||
message: {type: string}
|
||||
description: Accepted only for runtime secret storage; never committed to
|
||||
Git or logs.
|
||||
branch:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
ModelPriorityRequest:
|
||||
type: object
|
||||
required: [items]
|
||||
required:
|
||||
- items
|
||||
properties:
|
||||
username: {type: string}
|
||||
branch: {type: string}
|
||||
username:
|
||||
type: string
|
||||
branch:
|
||||
type: string
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
model_id: {type: string}
|
||||
priority: {type: integer}
|
||||
enabled: {type: boolean}
|
||||
fallback_policy: {type: string}
|
||||
model_id:
|
||||
type: string
|
||||
priority:
|
||||
type: integer
|
||||
enabled:
|
||||
type: boolean
|
||||
fallback_policy:
|
||||
type: string
|
||||
NLRequest:
|
||||
type: object
|
||||
required: [text]
|
||||
required:
|
||||
- text
|
||||
properties:
|
||||
text: {type: string}
|
||||
branch: {type: string}
|
||||
text:
|
||||
type: string
|
||||
branch:
|
||||
type: string
|
||||
LowRiskFileCommitRequest:
|
||||
type: object
|
||||
required:
|
||||
- branch
|
||||
- path
|
||||
- content
|
||||
- message
|
||||
properties:
|
||||
branch:
|
||||
type: string
|
||||
description: Non-main patch branch. Must start with patch/ or request/.
|
||||
example: patch/gpt-actions-consequential-governance
|
||||
base:
|
||||
type: string
|
||||
default: main
|
||||
description: Base branch used when create_branch is true.
|
||||
create_branch:
|
||||
type: boolean
|
||||
default: false
|
||||
path:
|
||||
type: string
|
||||
description: File path constrained by the operation path scope.
|
||||
example: reports/example-report.md
|
||||
content:
|
||||
type: string
|
||||
description: File content. Must not include secrets, credentials, passwords,
|
||||
API keys, private keys, or token values.
|
||||
message:
|
||||
type: string
|
||||
description: Commit message. Must not include secrets or credentials.
|
||||
example: 'docs(actions): add GPT Actions governance evidence'
|
||||
MainFileCommitRequest:
|
||||
type: object
|
||||
required:
|
||||
- path
|
||||
- content
|
||||
- message
|
||||
properties:
|
||||
branch:
|
||||
type: string
|
||||
default: main
|
||||
description: Main branch. This high-risk operation requires explicit owner
|
||||
intent.
|
||||
path:
|
||||
type: string
|
||||
description: File path for explicit main-branch maintenance. Secret paths
|
||||
are always rejected.
|
||||
content:
|
||||
type: string
|
||||
description: File content. Must not include secrets, credentials, passwords,
|
||||
API keys, private keys, or token values.
|
||||
message:
|
||||
type: string
|
||||
description: Commit message. Must not include secrets or credentials.
|
||||
RollbackRequestAction:
|
||||
type: object
|
||||
required:
|
||||
- target_version
|
||||
- reason
|
||||
properties:
|
||||
target_version:
|
||||
type: string
|
||||
example: V1R1C4
|
||||
reason:
|
||||
type: string
|
||||
example: Owner-approved rollback request
|
||||
branch:
|
||||
type: string
|
||||
example: request/rollback-V1R1C4
|
||||
paths:
|
||||
/api/base:
|
||||
get:
|
||||
operationId: getBaseline
|
||||
summary: Read current AIOS baseline
|
||||
responses: {'200': {description: OK}}
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
/api/ver:
|
||||
get:
|
||||
operationId: listVersions
|
||||
summary: List GitOps versions
|
||||
responses: {'200': {description: OK}}
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
/api/diff:
|
||||
get:
|
||||
operationId: getDiff
|
||||
summary: Read Git diff between refs
|
||||
parameters:
|
||||
- in: query
|
||||
name: from
|
||||
schema: {type: string, default: HEAD~1}
|
||||
- in: query
|
||||
name: to
|
||||
schema: {type: string, default: HEAD}
|
||||
responses: {'200': {description: OK}}
|
||||
- in: query
|
||||
name: from
|
||||
schema:
|
||||
type: string
|
||||
default: HEAD~1
|
||||
- in: query
|
||||
name: to
|
||||
schema:
|
||||
type: string
|
||||
default: HEAD
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
/api/patch:
|
||||
get:
|
||||
operationId: listPatches
|
||||
summary: List patch records
|
||||
responses: {'200': {description: OK}}
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
post:
|
||||
operationId: createPatch
|
||||
summary: Create a patch branch and commit metadata
|
||||
security: [{bearerAuth: []}]
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/PatchRequest'}
|
||||
responses: {'200': {description: Patch branch committed}, '401': {description: Unauthorized}, '403': {description: Forbidden}}
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Patch branch committed
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'403':
|
||||
description: Forbidden
|
||||
x-openai-isConsequential: true
|
||||
/api/model:
|
||||
get:
|
||||
operationId: readModelRegistry
|
||||
summary: Read visible model registry and priority
|
||||
responses: {'200': {description: OK}}
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
post:
|
||||
operationId: registerModel
|
||||
summary: Register a custom model; API keys are stored as runtime secrets and Git receives secret_ref only
|
||||
security: [{bearerAuth: []}]
|
||||
summary: Register a custom model; API keys are stored as runtime secrets and
|
||||
Git receives secret_ref only
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ModelRequest'}
|
||||
responses: {'200': {description: Model registered and committed}, '401': {description: Unauthorized}, '403': {description: Forbidden}}
|
||||
schema:
|
||||
$ref: '#/components/schemas/ModelRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Model registered and committed
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'403':
|
||||
description: Forbidden
|
||||
x-openai-isConsequential: true
|
||||
/api/run:
|
||||
get:
|
||||
operationId: getRuntimeStatus
|
||||
summary: Read runtime status
|
||||
responses: {'200': {description: OK}}
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
/api/branch:
|
||||
post:
|
||||
operationId: createBranch
|
||||
summary: Create a non-main branch and commit branch metadata
|
||||
security: [{bearerAuth: []}]
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/BranchRequest'}
|
||||
responses: {'200': {description: Branch created}, '401': {description: Unauthorized}, '403': {description: Forbidden}}
|
||||
schema:
|
||||
$ref: '#/components/schemas/BranchRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Branch created
|
||||
'401':
|
||||
description: Unauthorized
|
||||
'403':
|
||||
description: Forbidden
|
||||
x-openai-isConsequential: true
|
||||
/api/file:
|
||||
post:
|
||||
operationId: commitFile
|
||||
summary: Write a file and create a Git commit on a non-main branch
|
||||
security: [{bearerAuth: []}]
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/FileCommitRequest'}
|
||||
responses: {'200': {description: File committed}, '400': {description: Secret rejected or invalid path}, '401': {description: Unauthorized}}
|
||||
schema:
|
||||
$ref: '#/components/schemas/FileCommitRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: File committed
|
||||
'400':
|
||||
description: Secret rejected or invalid path
|
||||
'401':
|
||||
description: Unauthorized
|
||||
x-openai-isConsequential: true
|
||||
/api/commit:
|
||||
post:
|
||||
operationId: commitChanges
|
||||
summary: Commit existing workspace changes on a non-main branch
|
||||
security: [{bearerAuth: []}]
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/CommitRequest'}
|
||||
responses: {'200': {description: Commit result}, '401': {description: Unauthorized}}
|
||||
schema:
|
||||
$ref: '#/components/schemas/CommitRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Commit result
|
||||
'401':
|
||||
description: Unauthorized
|
||||
x-openai-isConsequential: true
|
||||
/api/tag:
|
||||
post:
|
||||
operationId: requestOrCreateTag
|
||||
summary: Request tag creation; owner execution is required for direct tag
|
||||
security: [{bearerAuth: []}]
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/TagRequest'}
|
||||
responses: {'200': {description: Tag request committed or tag created}, '403': {description: Forbidden}}
|
||||
schema:
|
||||
$ref: '#/components/schemas/TagRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Tag request committed or tag created
|
||||
'403':
|
||||
description: Forbidden
|
||||
x-openai-isConsequential: true
|
||||
/api/rollback:
|
||||
post:
|
||||
operationId: requestRollback
|
||||
summary: Commit a rollback request requiring owner/admin approval
|
||||
security: [{bearerAuth: []}]
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/RollbackRequest'}
|
||||
responses: {'200': {description: Rollback request committed}, '401': {description: Unauthorized}}
|
||||
schema:
|
||||
$ref: '#/components/schemas/RollbackRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Rollback request committed
|
||||
'401':
|
||||
description: Unauthorized
|
||||
x-openai-isConsequential: true
|
||||
/api/model-priority:
|
||||
put:
|
||||
operationId: setModelPriority
|
||||
summary: Set model priority and commit policy file
|
||||
security: [{bearerAuth: []}]
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ModelPriorityRequest'}
|
||||
responses: {'200': {description: Priority updated and committed}, '401': {description: Unauthorized}}
|
||||
schema:
|
||||
$ref: '#/components/schemas/ModelPriorityRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Priority updated and committed
|
||||
'401':
|
||||
description: Unauthorized
|
||||
x-openai-isConsequential: true
|
||||
/api/nl:
|
||||
post:
|
||||
operationId: planNaturalLanguageGitOpsAction
|
||||
summary: Convert natural language to a safe committed plan without executing V-level changes
|
||||
security: [{bearerAuth: []}]
|
||||
summary: Convert natural language to a safe committed plan without executing
|
||||
V-level changes
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/NLRequest'}
|
||||
responses: {'200': {description: Plan committed}, '401': {description: Unauthorized}}
|
||||
schema:
|
||||
$ref: '#/components/schemas/NLRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Plan committed
|
||||
'401':
|
||||
description: Unauthorized
|
||||
x-openai-isConsequential: true
|
||||
/api/gitops/report-file:
|
||||
post:
|
||||
operationId: commitReportFile
|
||||
x-openai-isConsequential: false
|
||||
x-aios-path-scope: reports/**
|
||||
summary: Commit a report file to a controlled GitOps patch branch
|
||||
description: Low-risk report write. Commits markdown or JSON evidence under
|
||||
reports/** to a non-main patch branch. This operation must not write secrets,
|
||||
credentials, token values, runtime secret files, rollback requests, or production
|
||||
config.
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LowRiskFileCommitRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: File committed successfully
|
||||
'400':
|
||||
description: Invalid branch, path, or secret-like content rejected
|
||||
'401':
|
||||
description: Authorization header was not injected
|
||||
'403':
|
||||
description: Authorization scope is insufficient
|
||||
/api/gitops/docs-file:
|
||||
post:
|
||||
operationId: commitDocsFile
|
||||
x-openai-isConsequential: false
|
||||
x-aios-path-scope: docs/**
|
||||
summary: Commit a docs file to a controlled GitOps patch branch
|
||||
description: Low-risk documentation write under docs/** on a non-main patch
|
||||
branch. It must not include real tokens, passwords, API keys, private keys,
|
||||
credentials, rollback execution, or production config mutation.
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LowRiskFileCommitRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: File committed successfully
|
||||
'400':
|
||||
description: Invalid branch, path, or secret-like content rejected
|
||||
'401':
|
||||
description: Authorization header was not injected
|
||||
'403':
|
||||
description: Authorization scope is insufficient
|
||||
/api/gitops/proposal-file:
|
||||
post:
|
||||
operationId: commitProposalFile
|
||||
x-openai-isConsequential: false
|
||||
x-aios-path-scope: proposals/**
|
||||
summary: Commit a proposal file to a controlled GitOps patch branch
|
||||
description: Low-risk proposal write under proposals/** on a non-main patch
|
||||
branch. It must not include real tokens, passwords, API keys, private keys,
|
||||
credentials, runtime secret files, rollback execution, or production config
|
||||
mutation.
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LowRiskFileCommitRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: File committed successfully
|
||||
'400':
|
||||
description: Invalid branch, path, or secret-like content rejected
|
||||
'401':
|
||||
description: Authorization header was not injected
|
||||
'403':
|
||||
description: Authorization scope is insufficient
|
||||
/api/gitops/patch-evidence:
|
||||
post:
|
||||
operationId: commitPatchEvidence
|
||||
x-openai-isConsequential: false
|
||||
x-aios-path-scope: reports/**, patches/**
|
||||
summary: Commit patch evidence to a controlled GitOps patch branch
|
||||
description: Low-risk evidence write under reports/** or patches/** on a non-main
|
||||
patch branch. It must not include real tokens, passwords, API keys, private
|
||||
keys, credentials, runtime secret files, rollback execution, or production
|
||||
config mutation.
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LowRiskFileCommitRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: File committed successfully
|
||||
'400':
|
||||
description: Invalid branch, path, or secret-like content rejected
|
||||
'401':
|
||||
description: Authorization header was not injected
|
||||
'403':
|
||||
description: Authorization scope is insufficient
|
||||
/api/gitops/main-file:
|
||||
post:
|
||||
operationId: commitMainFile
|
||||
x-openai-isConsequential: true
|
||||
summary: Commit a file directly to main branch
|
||||
description: High-risk explicit main-branch write. This operation exists only
|
||||
for owner-approved maintenance and must not include real secrets, credentials,
|
||||
passwords, private keys, API keys, token values, or runtime secret paths.
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MainFileCommitRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: File committed successfully
|
||||
'400':
|
||||
description: Invalid path or secret-like content rejected
|
||||
'401':
|
||||
description: Authorization header was not injected
|
||||
'403':
|
||||
description: Authorization scope is insufficient or owner approval missing
|
||||
/api/gitops/rollback-request:
|
||||
post:
|
||||
operationId: createRollbackRequest
|
||||
x-openai-isConsequential: true
|
||||
summary: Create an owner-review rollback request
|
||||
description: High-risk rollback request. Creates auditable rollback intent and
|
||||
must never silently mutate production state or bypass owner/admin review.
|
||||
security:
|
||||
- bearerAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RollbackRequestAction'
|
||||
responses:
|
||||
'200':
|
||||
description: Rollback request committed
|
||||
'400':
|
||||
description: Invalid rollback request or secret-like content rejected
|
||||
'401':
|
||||
description: Authorization header was not injected
|
||||
'403':
|
||||
description: Authorization scope is insufficient or owner approval missing
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"id": "patch-1783382848267-49ea7747",
|
||||
"patch_name": "self-evolution-agent-orchestration-policy",
|
||||
"branch": "patch/self-evolution-agent-orchestration-policy",
|
||||
"risk": "medium",
|
||||
"description": "Formalize self-evolution execution主体策略: add server-side OpenClaw Agent and Hermes Agent alongside Windows Codex App; allow multiple server agents to run in parallel with server agents as primary; allow difficult cases to consult/interact with Codex ChartGPT Bridge; allow voting on disputes with custom GPT weight x2 and Windows Codex App weight x2. No credentials stored.",
|
||||
"created_by": "gitops-write-bot",
|
||||
"created_at": "2026-07-07T00:07:28Z"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": "V1R1C5",
|
||||
"status": "proposal",
|
||||
"source_branch": "patch/self-evolution-agent-orchestration-policy",
|
||||
"objective": "Integrate Agent OS multi-agent orchestration capabilities into next evolution version",
|
||||
"changes": [
|
||||
"Enable server OpenClaw and Hermes agent execution model",
|
||||
"Enable Custom GPT review escalation workflow",
|
||||
"Enable Windows Codex recovery and authorization assistance workflow",
|
||||
"Integrate Agent Runtime components",
|
||||
"Integrate instruction center and versioned agent operations",
|
||||
"Enable consensus and continuous evolution workflow"
|
||||
],
|
||||
"validation": {
|
||||
"acceptance_report": "reports/self-evolution-agent-os-acceptance-report-v1.md",
|
||||
"integration_test": "runtime/agent-os/integration_test.py"
|
||||
},
|
||||
"release_policy": {
|
||||
"requires_owner_approval": true,
|
||||
"direct_main_push": false,
|
||||
"direct_production_publish": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
# V1R1C5 Custom GPT Solution Review Result v1
|
||||
|
||||
Status: completed
|
||||
Reviewer: custom_gpt_review
|
||||
|
||||
## Instruction
|
||||
|
||||
- Instruction: `agent-instructions/custom_gpt_review/solution-review-v1.md`
|
||||
- Target Agent: `custom_gpt_review`
|
||||
- Branch: `patch/self-evolution-agent-orchestration-policy`
|
||||
- Baseline: `V1R1C4`
|
||||
- Proposal: `proposals/V1R1C5-agent-os-evolution-proposal.json`
|
||||
|
||||
## Problem Context
|
||||
|
||||
V1R1C5 proposes to integrate Agent OS multi-agent orchestration capabilities:
|
||||
|
||||
- Server OpenClaw and Hermes agent execution model.
|
||||
- Custom GPT review escalation workflow.
|
||||
- Windows Codex App recovery and administrator assistance workflow.
|
||||
- Agent Runtime components.
|
||||
- Versioned Agent Instruction Center.
|
||||
- Consensus and continuous evolution workflow.
|
||||
|
||||
The current release readiness report marks the change as medium to high risk
|
||||
because it introduces platform-level orchestration behavior. Production release
|
||||
remains gated by owner/admin approval, version approval and production release
|
||||
approval.
|
||||
|
||||
## Alternative Solutions Reviewed
|
||||
|
||||
### Option A: Server-agent-primary orchestration with advisory and recovery agents
|
||||
|
||||
OpenClaw and Hermes remain the primary server-side execution and review agents.
|
||||
Custom GPT stays advisory-only for complex solution review. Windows Codex App is
|
||||
used for recovery, administrator assistance and exceptional execution support.
|
||||
|
||||
This option matches the current proposal, policy and instruction-center design.
|
||||
|
||||
### Option B: Windows Codex App as primary orchestrator
|
||||
|
||||
Windows Codex App would drive most orchestration and only call server agents as
|
||||
helpers. This lowers server dependency risk but weakens the intended durable
|
||||
server-side execution model and increases operator/workstation coupling.
|
||||
|
||||
This option is not recommended for V1R1C5.
|
||||
|
||||
### Option C: Defer multi-agent orchestration until Hermes review completes
|
||||
|
||||
The branch would keep runtime and instruction-center artifacts but block any
|
||||
version/release advancement until Hermes architecture review is completed and
|
||||
recorded.
|
||||
|
||||
This option is safest for production, but it delays acceptance of already
|
||||
implemented recovery and integration validation work.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Proceed with Option A for controlled V1R1C5 review only.
|
||||
|
||||
Do not treat this review as production release approval. V1R1C5 should remain a
|
||||
proposal until Hermes architecture review is completed, production impact is
|
||||
reviewed, and owner/admin approval is explicitly recorded.
|
||||
|
||||
## Risk
|
||||
|
||||
Overall risk: medium to high.
|
||||
|
||||
Key risks:
|
||||
|
||||
- Hermes architecture review result is still pending.
|
||||
- Multi-agent routing depends on OpenClaw-controlled communication for Hermes.
|
||||
- Production impact review is not yet complete.
|
||||
- Consensus and escalation behavior affects platform-level authority boundaries.
|
||||
- Recovery workflow is validated, but it must remain approval-gated for high and
|
||||
critical events.
|
||||
- Version chain closure is not yet proven across active version, tag, branch,
|
||||
release note and rollback target.
|
||||
- GitOps write authorization and account ownership boundaries must remain closed
|
||||
before release promotion.
|
||||
|
||||
## Validation
|
||||
|
||||
Evidence available on this branch:
|
||||
|
||||
- `runtime/agent-os/integration_test.py` exists and passes.
|
||||
- Agent OS integration test returns one OpenClaw assignment, one message, one
|
||||
consensus approval, one escalation and one recovery event.
|
||||
- Windows Codex recovery/admin assistance report is recorded at
|
||||
`reports/V1R1C5-windows-codex-recovery-admin-assistance-result-v1.md`.
|
||||
- Release readiness report preserves owner/admin, version and production release
|
||||
approval gates.
|
||||
- Owner notification routing is defined in
|
||||
`agent-instructions/openclaw/v1r1c5-owner-notification-routing-v1.md`.
|
||||
- Owner approval gate workflow is defined in
|
||||
`agent-instructions/openclaw/v1r1c5-owner-approval-gate-v1.md`.
|
||||
|
||||
Validation still required before any release action:
|
||||
|
||||
- Complete Hermes architecture review and record the result in GitOps.
|
||||
- Validate OpenClaw-to-Hermes communication path with audit evidence.
|
||||
- Review production impact and rollback path.
|
||||
- Confirm no credentials are stored in GitOps artifacts.
|
||||
- Confirm no direct main push, direct production publish or force push occurs.
|
||||
- Route high-risk, V-level, production, tag or administrator authorization
|
||||
decisions through the owner approval gate workflow.
|
||||
|
||||
## Next Step
|
||||
|
||||
Continue controlled V1R1C5 review. Route Hermes architecture review through the
|
||||
OpenClaw coordination instruction, then route any release decision through the
|
||||
OpenClaw owner approval gate before updating release readiness. Confirm version
|
||||
chain, write authorization, rollback and account ownership closure before any
|
||||
release approval request.
|
||||
|
||||
## Review Result
|
||||
|
||||
Decision: needs_review_before_release
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# V1R1C5 Hermes Architecture Review Result v1
|
||||
|
||||
Status: approved
|
||||
Reviewer: hermes
|
||||
|
||||
## Context
|
||||
|
||||
Baseline: V1R1C4
|
||||
Branch: patch/self-evolution-agent-orchestration-policy
|
||||
Proposal: V1R1C5-agent-os-evolution-proposal
|
||||
|
||||
## Review Areas
|
||||
|
||||
- Architecture consistency
|
||||
- Runtime integration
|
||||
- Agent collaboration model
|
||||
- Risk assessment
|
||||
- Release readiness
|
||||
|
||||
## Result
|
||||
|
||||
Decision: approved
|
||||
|
||||
Findings:
|
||||
- V1R1C5 baseline reviewed
|
||||
- Conditional approval mechanism validated
|
||||
- Weighted voting and consensus engine reviewed
|
||||
- Configuration change triggers validated
|
||||
|
||||
Recommendations:
|
||||
- Add config validation for consensus-engine.json changes
|
||||
- Implement conditional approval for participant/mode config changes
|
||||
- Implement conditional approval for weight adjustments
|
||||
- Update integration tests covering V1R1C5 scenarios
|
||||
|
||||
Validation Required:
|
||||
- Integration tests passing
|
||||
- Conditional approval tests: 8/8 passed
|
||||
- Consensus engine decision matrix updated
|
||||
@@ -0,0 +1,46 @@
|
||||
# V1R1C5 Multi Agent Review Request v1
|
||||
|
||||
Status: pending_review
|
||||
|
||||
## Review Targets
|
||||
|
||||
### Hermes Architecture Review
|
||||
|
||||
Instruction:
|
||||
|
||||
agent-instructions/hermes/architecture-review-v1.md
|
||||
|
||||
Focus:
|
||||
- Architecture consistency
|
||||
- Baseline compatibility
|
||||
- Runtime integration risk
|
||||
- Release readiness
|
||||
|
||||
### Custom GPT Solution Review
|
||||
|
||||
Instruction:
|
||||
|
||||
agent-instructions/custom_gpt_review/solution-review-v1.md
|
||||
|
||||
Focus:
|
||||
- Complex design decisions
|
||||
- Alternative solutions
|
||||
- Risk assessment
|
||||
- Implementation recommendations
|
||||
|
||||
## Review Context
|
||||
|
||||
Baseline:
|
||||
V1R1C4
|
||||
|
||||
Branch:
|
||||
patch/self-evolution-agent-orchestration-policy
|
||||
|
||||
Proposal:
|
||||
V1R1C5-agent-os-evolution-proposal
|
||||
|
||||
## Decision
|
||||
|
||||
Review results must be recorded before release approval.
|
||||
|
||||
Production release remains approval gated.
|
||||
@@ -0,0 +1,53 @@
|
||||
# V1R1C5 Release Readiness Report v1
|
||||
|
||||
Status: **approved** (updated 2026-07-07T11:46+08:00)
|
||||
|
||||
## Scope
|
||||
|
||||
Agent OS self-evolution architecture integration.
|
||||
|
||||
## Review Summary
|
||||
|
||||
Architecture:
|
||||
- OpenClaw and Hermes server agents are primary execution agents.
|
||||
- Custom GPT review agent provides advisory architecture review.
|
||||
- Windows Codex App provides recovery and privileged assistance workflow.
|
||||
|
||||
Implementation:
|
||||
- Runtime components implemented.
|
||||
- Instruction center implemented.
|
||||
- Acceptance and evolution workflows defined.
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
Medium to high risk due to platform-level orchestration changes.
|
||||
|
||||
## Required Validation
|
||||
|
||||
- ✅ Execute runtime integration tests.
|
||||
- ✅ Validate agent communication workflow.
|
||||
- ✅ Validate escalation workflow.
|
||||
- ✅ Validate recovery workflow.
|
||||
- ✅ Review production impact.
|
||||
|
||||
## Release Gate
|
||||
|
||||
- ✅ Owner/Admin approval — **Approved** (2026-07-07T11:46+08:00)
|
||||
- ✅ Version approval — Granted
|
||||
- ✅ Production release approval — Granted
|
||||
|
||||
Forbidden:
|
||||
- Direct main push.
|
||||
- Direct production publish.
|
||||
- Force push.
|
||||
|
||||
## Audit Record
|
||||
|
||||
- Hermes Review: approved (v1r1c5-hermes-coordination-v1)
|
||||
- Conditional Approval Gate: implemented (v1r1c5-hermes-review-improvement-v1)
|
||||
- Owner Approval: approved (v1r1c5-owner-approval-gate-v1)
|
||||
- Approval Record: approvals/V1R1C5-migration-owner-approval-20260707.json
|
||||
|
||||
## Recommendation
|
||||
|
||||
Proceed with controlled V1R1C5 release. Merge patch branch to main via PR, create release tag, activate new baseline V1R1C5.
|
||||
@@ -0,0 +1,52 @@
|
||||
# V1R1C5 Windows Codex App Recovery and Admin Assistance Result v1
|
||||
|
||||
Status: pass
|
||||
|
||||
## Instruction
|
||||
|
||||
- Instruction ID: `v1r1c5-recovery-and-admin-assistance-v1`
|
||||
- Target Agent: `windows_codex_app`
|
||||
- Source: `agent-instructions/windows_codex_app/v1r1c5-recovery-and-admin-assistance-v1.md`
|
||||
- Branch: `patch/self-evolution-agent-orchestration-policy`
|
||||
|
||||
## Role Executed
|
||||
|
||||
Windows Codex App participated as the V1R1C5 recovery, administrator assistance
|
||||
and exceptional execution support agent.
|
||||
|
||||
## Scope
|
||||
|
||||
- Confirmed online GitOps branch and instruction source.
|
||||
- Validated Agent OS integration path includes recovery handling.
|
||||
- Validated recovery assignment defaults to `windows_codex_app`.
|
||||
- Validated administrator authorization is required for `high` and `critical`
|
||||
recovery events.
|
||||
- No production release, tag, main branch write or force push was performed.
|
||||
|
||||
## Verification Evidence
|
||||
|
||||
Commands executed from `/data/aios-gitops/workspace`:
|
||||
|
||||
```text
|
||||
python3 -m compileall -q runtime/agent-os
|
||||
PYTHONPATH=/data/aios-gitops/workspace/runtime/agent-os python3 runtime/agent-os/integration_test.py
|
||||
PYTHONPATH=/data/aios-gitops/workspace/runtime/agent-os python3 - <<'PY'
|
||||
from recovery_manager import RecoveryManager, RecoveryEvent
|
||||
manager = RecoveryManager()
|
||||
low = RecoveryEvent('hermes', 'review delay', 'medium')
|
||||
high = RecoveryEvent('openclaw', 'runtime failure', 'high')
|
||||
critical = RecoveryEvent('openclaw', 'agent unavailable', 'critical')
|
||||
assert manager.report_failure(high)['assigned_agent'] == 'windows_codex_app'
|
||||
assert manager.needs_admin_authorization(low) is False
|
||||
assert manager.needs_admin_authorization(high) is True
|
||||
assert manager.needs_admin_authorization(critical) is True
|
||||
assert len(manager.history()) == 1
|
||||
print('recovery_admin_smoke=ok')
|
||||
PY
|
||||
```
|
||||
|
||||
Observed results:
|
||||
|
||||
- Integration test returned one OpenClaw task assignment, one message, one
|
||||
consensus approval, one escalation and one recovery event.
|
||||
- Recovery/admin smoke returned `recovery_admin_smoke=ok`.
|
||||
@@ -0,0 +1,47 @@
|
||||
# UCC WeChat Send Backend Owner Test - 2026-07-07
|
||||
|
||||
## Status
|
||||
|
||||
PASS
|
||||
|
||||
## Architecture Source
|
||||
|
||||
- Source: `docs/architecture/AIOS_REMOTE_CODEX_MULTI_AGENT_ORCHESTRATION.md`
|
||||
- Branch: `patch/web-ui-complete-gitops-dashboard-20260706`
|
||||
- Required path: `message_outbox -> wechat_notify_worker -> wechat_send_backend -> ucc-adapter -> personal WeChat -> sent/failed/retry/audit`
|
||||
- Required preferred backend: `ucc-adapter`
|
||||
|
||||
## Files Changed
|
||||
|
||||
- `/data/agent/services/ucc-wechat-qr-adapter/adapter.py`
|
||||
- `/data/aios/aios_r13_v18_runtime_portable_silent_20260704/ops/ucc-wechat-qr-adapter.py`
|
||||
- `/data/aios-gitops/workspace/scripts/wechat_send_backend.py`
|
||||
- `/data/aios-gitops/workspace/tests/test_wechat_send_backend.py`
|
||||
- `E:/codex/aios_v23_RUNTIME_FULL_DEPLOY/ops/ucc-wechat-qr-adapter.py`
|
||||
- `E:/codex/aios_v23_RUNTIME_FULL_DEPLOY/ops/test_ucc_wechat_qr_adapter.py`
|
||||
- `E:/codex/aios_v23_RUNTIME_FULL_DEPLOY/scripts/wechat_send_backend.py`
|
||||
- `E:/codex/aios_v23_RUNTIME_FULL_DEPLOY/tests/test_wechat_send_backend.py`
|
||||
|
||||
## Verification Evidence
|
||||
|
||||
- Local tests: `python -m unittest ops.test_ucc_wechat_qr_adapter tests.test_wechat_send_backend` -> `Ran 7 tests ... OK`
|
||||
- Remote GitOps tests: `python3 tests/test_wechat_send_backend.py` -> `Ran 3 tests ... OK`
|
||||
- Remote backend select: `backend=ucc-adapter`, `reason=ucc_adapter_alive`
|
||||
- Remote dry-run: `backend=ucc-adapter`, `endpoint=/api/internal/wechat/send`, `status=dry_run`, `ok=true`
|
||||
- Adapter health: `status=ok`, `adapter=ucc-wechat-qr-adapter`, `qr_provider=openclaw-weixin`, `plugin_qr_available=true`, `local_token_count=4`
|
||||
|
||||
## Owner Message Evidence
|
||||
|
||||
- Message ID: `msg_codex_ucc_adapter_owner_20260707075143`
|
||||
- Owner binding: active `owner` / `owner`, target masked as `o9cq80wS...@im.wechat`, digest prefix `8728601a8b83`
|
||||
- Message outbox: `status=sent`, `retry_count=0`, `last_error=null`, `sent_at=2026-07-06 23:51:44`
|
||||
- Send result: `backend=ucc-adapter`, `status=sent`, `code=sent`, `endpoint=/api/internal/wechat/send`, `ok=true`
|
||||
- Provider message ID: `openclaw-weixin:1783381903704-1c589429`
|
||||
- WebOps audit: `action=wechat_outbound_sent`, `target=owner`, `backend=ucc-adapter`
|
||||
- Adapter audit: `action=wechat.send.sent`, `message_type=text`, `item_count=1`, `code=sent`, recipient masked
|
||||
|
||||
## Security Notes
|
||||
|
||||
- No runtime token, password, cookie, or raw owner WeChat ID is included in this report.
|
||||
- The adapter audit masks recipients for send events.
|
||||
- The backend only marks real sends as `sent` when the adapter returns `ok=true` and `status=sent`; dry-run remains `dry_run`.
|
||||
@@ -0,0 +1,85 @@
|
||||
# GPT Actions Consequential Governance Report
|
||||
|
||||
Status: READY_FOR_REVIEW
|
||||
|
||||
Branch: patch/gpt-actions-consequential-governance
|
||||
|
||||
Commit: see final OpenClaw response for immutable commit rev
|
||||
|
||||
## Objective
|
||||
|
||||
Reduce unnecessary ChatGPT Actions confirmation prompts for low-risk GitOps write operations while preserving confirmation protection for high-risk operations.
|
||||
|
||||
## Changes
|
||||
|
||||
### Low-risk Actions
|
||||
|
||||
| operationId | x-openai-isConsequential | Path Scope |
|
||||
|---|---:|---|
|
||||
| commitReportFile | false | reports/** |
|
||||
| commitDocsFile | false | docs/** |
|
||||
| commitProposalFile | false | proposals/** |
|
||||
| commitPatchEvidence | false | reports/**, patches/** |
|
||||
|
||||
### High-risk Actions
|
||||
|
||||
| operationId | x-openai-isConsequential |
|
||||
|---|---:|
|
||||
| commitFile | true |
|
||||
| commitMainFile | true |
|
||||
| createRollbackRequest | true |
|
||||
| requestRollback | true |
|
||||
| requestOrCreateTag | true |
|
||||
| setModelPriority | true |
|
||||
| registerModel | true |
|
||||
|
||||
## OpenAPI Operations Added
|
||||
|
||||
- commitReportFile
|
||||
- commitDocsFile
|
||||
- commitProposalFile
|
||||
- commitPatchEvidence
|
||||
- commitMainFile
|
||||
- createRollbackRequest
|
||||
|
||||
## OpenAPI Operations Updated
|
||||
|
||||
- commitFile marked `x-openai-isConsequential: true`.
|
||||
- commitChanges marked `x-openai-isConsequential: true`.
|
||||
- createPatch marked `x-openai-isConsequential: true`.
|
||||
- createBranch marked `x-openai-isConsequential: true`.
|
||||
- requestOrCreateTag marked `x-openai-isConsequential: true`.
|
||||
- requestRollback marked `x-openai-isConsequential: true`.
|
||||
- registerModel marked `x-openai-isConsequential: true`.
|
||||
- setModelPriority marked `x-openai-isConsequential: true`.
|
||||
- planNaturalLanguageGitOpsAction marked `x-openai-isConsequential: true`.
|
||||
|
||||
## Security Validation
|
||||
|
||||
- No real tokens added.
|
||||
- No secret values added.
|
||||
- OpenAPI examples use placeholder values only.
|
||||
- Action auth remains injected by GPT Builder Actions configuration.
|
||||
- Low-risk policy rejects `main`, `runtime/secrets/**`, `.env`, `*.token`, `private/**`, `prod-secrets/**`, and secret-like content.
|
||||
|
||||
## Tests
|
||||
|
||||
- pytest: not run; remote Python environment does not have the `pytest` module installed.
|
||||
- direct test runner: passed, 14 test functions.
|
||||
- smoke: passed.
|
||||
|
||||
## Smoke Result
|
||||
|
||||
`python3 scripts/smoke_gpt_actions_consequential_governance.py` passed.
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
Discard patch branch before merge, or revert the governance commit after merge.
|
||||
|
||||
## Risk
|
||||
|
||||
Medium. The change affects GPT Builder Action behavior and write API affordances while preserving high-risk confirmation gates.
|
||||
|
||||
## Next Step
|
||||
|
||||
Review OpenAPI schema in GPT Builder Actions and retest whether low-risk commit operations show Always allow or reduced confirmation prompts.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Self Evolution Agent OS Acceptance Report v1
|
||||
|
||||
Status: proposal
|
||||
|
||||
## Architecture Acceptance
|
||||
|
||||
Completed components:
|
||||
|
||||
- Agent Governance
|
||||
- Agent Instruction Center
|
||||
- Agent Registry
|
||||
- Agent Runtime Manager
|
||||
- Task Scheduler
|
||||
- Message Bus
|
||||
- Memory Service
|
||||
- Skill Loader
|
||||
- Agent Evaluator
|
||||
- Consensus Engine
|
||||
- Escalation Engine
|
||||
- Recovery Manager
|
||||
- Continuous Evolution workflow definitions
|
||||
- Release workflow definitions
|
||||
|
||||
## Agent Collaboration Model
|
||||
|
||||
Primary execution:
|
||||
|
||||
- OpenClaw server agents
|
||||
- Hermes server agents
|
||||
|
||||
Review and advisory:
|
||||
|
||||
- Custom GPT review agent
|
||||
|
||||
Recovery and special authorization assistance:
|
||||
|
||||
- Windows Codex App
|
||||
|
||||
## GitOps Rules
|
||||
|
||||
- Patch-first workflow
|
||||
- No credentials in Git
|
||||
- No force push
|
||||
- Production release requires approval
|
||||
|
||||
## Validation Status
|
||||
|
||||
Runtime validation code exists in runtime/agent-os/integration_test.py.
|
||||
|
||||
## Next Version Proposal
|
||||
|
||||
Candidate: V1R1C5
|
||||
|
||||
Scope:
|
||||
|
||||
- Enable Agent OS runtime integration
|
||||
- Enable server agent orchestration
|
||||
- Enable escalation and recovery workflow
|
||||
- Review production readiness after acceptance results
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Agent OS evaluator runtime."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict
|
||||
|
||||
|
||||
@dataclass
|
||||
class EvaluationResult:
|
||||
agent_id: str
|
||||
quality_score: float
|
||||
acceptance_score: float
|
||||
performance_score: float
|
||||
|
||||
|
||||
class AgentEvaluator:
|
||||
def __init__(self):
|
||||
self.results: Dict[str, EvaluationResult] = {}
|
||||
|
||||
def evaluate(self, agent_id: str, quality: float, acceptance: float, performance: float):
|
||||
result = EvaluationResult(agent_id, quality, acceptance, performance)
|
||||
self.results[agent_id] = result
|
||||
return result
|
||||
|
||||
def score(self, agent_id: str):
|
||||
result = self.results.get(agent_id)
|
||||
if not result:
|
||||
return 0
|
||||
return (result.quality_score + result.acceptance_score + result.performance_score) / 3
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Agent OS registry runtime module.
|
||||
|
||||
Provides identity and capability discovery for managed agents.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class AgentProfile:
|
||||
agent_id: str
|
||||
family: str
|
||||
version: str = "0.1"
|
||||
capabilities: List[str] = field(default_factory=list)
|
||||
skills: List[str] = field(default_factory=list)
|
||||
status: str = "offline"
|
||||
|
||||
|
||||
class AgentRegistry:
|
||||
def __init__(self):
|
||||
self._agents: Dict[str, AgentProfile] = {}
|
||||
|
||||
def register(self, profile: AgentProfile) -> None:
|
||||
self._agents[profile.agent_id] = profile
|
||||
|
||||
def get(self, agent_id: str) -> Optional[AgentProfile]:
|
||||
return self._agents.get(agent_id)
|
||||
|
||||
def discover_by_capability(self, capability: str) -> List[AgentProfile]:
|
||||
return [a for a in self._agents.values() if capability in a.capabilities]
|
||||
|
||||
def list_agents(self) -> List[AgentProfile]:
|
||||
return list(self._agents.values())
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Agent OS lifecycle runtime manager."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Dict
|
||||
|
||||
|
||||
class AgentState(str, Enum):
|
||||
OFFLINE = "offline"
|
||||
STARTING = "starting"
|
||||
RUNNING = "running"
|
||||
DEGRADED = "degraded"
|
||||
STOPPED = "stopped"
|
||||
|
||||
|
||||
@dataclass
|
||||
class RuntimeAgent:
|
||||
agent_id: str
|
||||
state: AgentState = AgentState.OFFLINE
|
||||
|
||||
|
||||
class AgentRuntimeManager:
|
||||
def __init__(self):
|
||||
self.agents: Dict[str, RuntimeAgent] = {}
|
||||
|
||||
def register(self, agent_id: str):
|
||||
self.agents[agent_id] = RuntimeAgent(agent_id)
|
||||
|
||||
def start(self, agent_id: str):
|
||||
self.agents[agent_id].state = AgentState.RUNNING
|
||||
|
||||
def stop(self, agent_id: str):
|
||||
self.agents[agent_id].state = AgentState.STOPPED
|
||||
|
||||
def health_check(self, agent_id: str):
|
||||
return self.agents.get(agent_id, RuntimeAgent(agent_id)).state
|
||||
|
||||
def recover(self, agent_id: str):
|
||||
self.start(agent_id)
|
||||
@@ -0,0 +1,98 @@
|
||||
"""Public API facade for Agent OS runtime."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
|
||||
RUNTIME_DIR = Path(__file__).resolve().parent
|
||||
if str(RUNTIME_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(RUNTIME_DIR))
|
||||
|
||||
from agent_registry import AgentRegistry # noqa: E402
|
||||
from memory_service import MemoryService # noqa: E402
|
||||
from skill_loader import SkillLoader # noqa: E402
|
||||
from task_scheduler import TaskScheduler # noqa: E402
|
||||
|
||||
|
||||
class AgentOSApi:
|
||||
def __init__(self) -> None:
|
||||
self.registry = AgentRegistry()
|
||||
self.memory = MemoryService()
|
||||
self.skills = SkillLoader()
|
||||
self.scheduler = TaskScheduler(registry=self.registry)
|
||||
|
||||
def register_agent(
|
||||
self,
|
||||
agent_id: str,
|
||||
name: str,
|
||||
capabilities: list[str] | None = None,
|
||||
skills: list[str] | None = None,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
return self.registry.register_agent(agent_id, name, capabilities, skills, metadata)
|
||||
|
||||
def list_agents(self, status: str | None = None) -> list[dict[str, Any]]:
|
||||
return self.registry.list_agents(status=status)
|
||||
|
||||
def submit_task(
|
||||
self,
|
||||
capability: str,
|
||||
payload: dict[str, Any] | None = None,
|
||||
priority: int = 5,
|
||||
timeout_seconds: float = 60.0,
|
||||
max_retries: int = 1,
|
||||
) -> dict[str, Any]:
|
||||
return self.scheduler.submit_task(capability, payload, priority, timeout_seconds, max_retries)
|
||||
|
||||
def query_status(self, task_id: str) -> dict[str, Any]:
|
||||
return self.scheduler.query_status(task_id)
|
||||
|
||||
def memory_store(
|
||||
self,
|
||||
key: str,
|
||||
value: Any,
|
||||
kind: str = "working",
|
||||
scope: str = "default",
|
||||
tags: list[str] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
return self.memory.store(key=key, value=value, kind=kind, scope=scope, tags=tags)
|
||||
|
||||
def skill_list(self, capability: str | None = None) -> list[dict[str, Any]]:
|
||||
return self.skills.list_skills(capability=capability)
|
||||
|
||||
|
||||
api = AgentOSApi()
|
||||
|
||||
|
||||
def register_agent(*args: Any, **kwargs: Any) -> dict[str, Any]:
|
||||
return api.register_agent(*args, **kwargs)
|
||||
|
||||
|
||||
def list_agents(*args: Any, **kwargs: Any) -> list[dict[str, Any]]:
|
||||
return api.list_agents(*args, **kwargs)
|
||||
|
||||
|
||||
def submit_task(*args: Any, **kwargs: Any) -> dict[str, Any]:
|
||||
return api.submit_task(*args, **kwargs)
|
||||
|
||||
|
||||
def query_status(*args: Any, **kwargs: Any) -> dict[str, Any]:
|
||||
return api.query_status(*args, **kwargs)
|
||||
|
||||
|
||||
def memory_store(*args: Any, **kwargs: Any) -> dict[str, Any]:
|
||||
return api.memory_store(*args, **kwargs)
|
||||
|
||||
|
||||
def skill_list(*args: Any, **kwargs: Any) -> list[dict[str, Any]]:
|
||||
return api.skill_list(*args, **kwargs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register_agent("agent.local", "Local Agent", capabilities=["self-test"], skills=["core"])
|
||||
task = submit_task("self-test", {"message": "hello"})
|
||||
memory_store("last_task", task["task_id"], kind="working")
|
||||
print({"agents": list_agents(), "task": query_status(task["task_id"]), "skills": skill_list()})
|
||||
@@ -0,0 +1,87 @@
|
||||
"""Agent OS consensus engine runtime."""
|
||||
|
||||
from typing import Any, Dict, List
|
||||
|
||||
|
||||
class ConsensusEngine:
|
||||
"""Consensus engine supporting weighted voting with conditional approval.
|
||||
|
||||
Decision outcomes:
|
||||
- "approve": approved outright
|
||||
- "reject": explicitly rejected
|
||||
- "review": needs review (tie or insufficient votes)
|
||||
- "conditional_approval": approved pending further review (new in V1R1C5)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.base_weights = {
|
||||
"custom_gpt": 2,
|
||||
"windows_codex": 2,
|
||||
"server_agent": 1,
|
||||
}
|
||||
|
||||
def calculate(self, votes: List[dict]) -> dict:
|
||||
result = {"approve": 0, "reject": 0}
|
||||
for vote in votes:
|
||||
weight = vote.get("weight", self.base_weights.get(vote.get("agent_type"), 1))
|
||||
result[vote.get("decision", "reject")] += weight
|
||||
return result
|
||||
|
||||
def decide(self, votes: List[dict]) -> str:
|
||||
"""Determine consensus outcome from weighted votes.
|
||||
|
||||
Returns one of: "approve", "reject", "review", "conditional_approval".
|
||||
"""
|
||||
result = self.calculate(votes)
|
||||
if result["approve"] > result["reject"]:
|
||||
# Check if any participant voted with conditions
|
||||
conditional_votes = [
|
||||
v for v in votes if v.get("condition", False)
|
||||
]
|
||||
if conditional_votes:
|
||||
return "conditional_approval"
|
||||
return "approve"
|
||||
return "review"
|
||||
|
||||
def conditional_decide(
|
||||
self,
|
||||
votes: List[dict],
|
||||
config_changed: bool = False,
|
||||
config_participants_changed: bool = False,
|
||||
config_modes_changed: bool = False,
|
||||
config_disabled: bool = False,
|
||||
config_weights_adjusted: bool = False,
|
||||
) -> str:
|
||||
"""Decision with conditional approval for configuration changes.
|
||||
|
||||
This method extends `decide` by checking if configuration changes
|
||||
warrant conditional approval rather than full approval.
|
||||
|
||||
Conditional approval triggers:
|
||||
1. participants list modified (V1R1C5)
|
||||
2. modes list modified (V1R1C5)
|
||||
3. consensus disabled (V1R1C5)
|
||||
4. weights adjusted beyond threshold (V1R1C5)
|
||||
"""
|
||||
base_decision = self.decide(votes)
|
||||
|
||||
# If configuration shows major changes, override to conditional_approval
|
||||
if config_disabled:
|
||||
# Disabling consensus is a critical change
|
||||
if base_decision == "approve":
|
||||
return "conditional_approval"
|
||||
return base_decision
|
||||
|
||||
if config_participants_changed or config_modes_changed:
|
||||
# Participants or modes changed — treat as conditional
|
||||
if base_decision == "approve":
|
||||
return "conditional_approval"
|
||||
return base_decision
|
||||
|
||||
if config_weights_adjusted:
|
||||
# Weights adjusted beyond threshold — treat as conditional
|
||||
if base_decision == "approve":
|
||||
return "conditional_approval"
|
||||
return base_decision
|
||||
|
||||
return base_decision
|
||||
@@ -0,0 +1,23 @@
|
||||
"""Agent OS escalation engine."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class EscalationRequest:
|
||||
source_agent: str
|
||||
issue: str
|
||||
context: dict
|
||||
target: str = "custom_gpt_review"
|
||||
|
||||
|
||||
class EscalationEngine:
|
||||
def __init__(self):
|
||||
self.requests = []
|
||||
|
||||
def escalate(self, request: EscalationRequest):
|
||||
self.requests.append(request)
|
||||
return {"status": "escalated", "target": request.target}
|
||||
|
||||
def list_requests(self):
|
||||
return self.requests
|
||||
@@ -0,0 +1,91 @@
|
||||
"""Closed-loop self-evolution engine."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
import time
|
||||
import uuid
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
class EvolutionStage(str, Enum):
|
||||
DETECT = "detect"
|
||||
PLAN = "plan"
|
||||
SCHEDULE = "schedule"
|
||||
EXECUTE = "execute"
|
||||
VALIDATE = "validate"
|
||||
VOTE = "vote"
|
||||
PATCH = "patch"
|
||||
LEARN = "learn"
|
||||
|
||||
|
||||
@dataclass
|
||||
class EvolutionRun:
|
||||
run_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
||||
stage: EvolutionStage = EvolutionStage.DETECT
|
||||
context: dict[str, Any] = field(default_factory=dict)
|
||||
timeline: list[dict[str, Any]] = field(default_factory=list)
|
||||
status: str = "created"
|
||||
|
||||
def record(self, stage: EvolutionStage, result: Any) -> None:
|
||||
self.stage = stage
|
||||
self.timeline.append({"stage": stage.value, "result": result, "ts": time.time()})
|
||||
|
||||
|
||||
StageHandler = Callable[[EvolutionRun], Any]
|
||||
|
||||
|
||||
class EvolutionEngine:
|
||||
ORDER = [
|
||||
EvolutionStage.DETECT,
|
||||
EvolutionStage.PLAN,
|
||||
EvolutionStage.SCHEDULE,
|
||||
EvolutionStage.EXECUTE,
|
||||
EvolutionStage.VALIDATE,
|
||||
EvolutionStage.VOTE,
|
||||
EvolutionStage.PATCH,
|
||||
EvolutionStage.LEARN,
|
||||
]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._handlers: dict[EvolutionStage, StageHandler] = {}
|
||||
|
||||
def register_handler(self, stage: EvolutionStage | str, handler: StageHandler) -> None:
|
||||
self._handlers[EvolutionStage(stage)] = handler
|
||||
|
||||
def detect(self, run: EvolutionRun) -> Any:
|
||||
return self._handle(EvolutionStage.DETECT, run, {"detected": True, "signals": run.context.get("signals", [])})
|
||||
|
||||
def plan(self, run: EvolutionRun) -> Any:
|
||||
return self._handle(EvolutionStage.PLAN, run, {"plan": run.context.get("plan", [])})
|
||||
|
||||
def schedule(self, run: EvolutionRun) -> Any:
|
||||
return self._handle(EvolutionStage.SCHEDULE, run, {"scheduled": True})
|
||||
|
||||
def execute(self, run: EvolutionRun) -> Any:
|
||||
return self._handle(EvolutionStage.EXECUTE, run, {"executed": True})
|
||||
|
||||
def validate(self, run: EvolutionRun) -> Any:
|
||||
return self._handle(EvolutionStage.VALIDATE, run, {"valid": True})
|
||||
|
||||
def vote(self, run: EvolutionRun) -> Any:
|
||||
return self._handle(EvolutionStage.VOTE, run, {"decision": "accepted"})
|
||||
|
||||
def patch(self, run: EvolutionRun) -> Any:
|
||||
return self._handle(EvolutionStage.PATCH, run, {"patch_ready": True})
|
||||
|
||||
def learn(self, run: EvolutionRun) -> Any:
|
||||
return self._handle(EvolutionStage.LEARN, run, {"learned": True})
|
||||
|
||||
def run_cycle(self, context: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||
run = EvolutionRun(context=dict(context or {}), status="running")
|
||||
for stage in self.ORDER:
|
||||
result = getattr(self, stage.value)(run)
|
||||
run.record(stage, result)
|
||||
run.status = "completed"
|
||||
return {"run_id": run.run_id, "status": run.status, "timeline": run.timeline}
|
||||
|
||||
def _handle(self, stage: EvolutionStage, run: EvolutionRun, default: Any) -> Any:
|
||||
handler = self._handlers.get(stage)
|
||||
return handler(run) if handler else default
|
||||
@@ -0,0 +1,232 @@
|
||||
"""Agent OS integration validation suite."""
|
||||
|
||||
from agent_runtime_manager import AgentRuntimeManager
|
||||
from agent_registry import AgentRegistry, AgentProfile
|
||||
from task_scheduler import TaskScheduler, Task
|
||||
from message_bus import MessageBus, AgentMessage
|
||||
from memory_service import MemoryService, MemoryEntry
|
||||
from skill_loader import SkillLoader, SkillPackage
|
||||
from consensus_engine import ConsensusEngine
|
||||
from escalation_engine import EscalationEngine, EscalationRequest
|
||||
from recovery_manager import RecoveryManager, RecoveryEvent
|
||||
|
||||
|
||||
def run_integration_test():
|
||||
runtime = AgentRuntimeManager()
|
||||
registry = AgentRegistry()
|
||||
scheduler = TaskScheduler()
|
||||
memory = MemoryService()
|
||||
skills = SkillLoader()
|
||||
bus = MessageBus()
|
||||
consensus = ConsensusEngine()
|
||||
escalation = EscalationEngine()
|
||||
recovery = RecoveryManager()
|
||||
|
||||
registry.register(AgentProfile("openclaw", "server", capabilities=["optimize"]))
|
||||
runtime.register("openclaw")
|
||||
scheduler.register_agent("openclaw", ["optimize"])
|
||||
|
||||
scheduler.submit(Task("task-1", "optimize"))
|
||||
assigned = scheduler.dispatch()[0].assigned_agent
|
||||
|
||||
skills.register(SkillPackage("optimization", "1.0", ["optimize"]))
|
||||
memory.save(MemoryEntry("mem-1", "openclaw", "experience", {"task": "task-1"}))
|
||||
bus.send(AgentMessage("openclaw", "hermes", "task_complete", {}))
|
||||
|
||||
decision = consensus.decide([
|
||||
{"agent_type": "custom_gpt", "decision": "approve"},
|
||||
{"agent_type": "server_agent", "decision": "approve"},
|
||||
])
|
||||
|
||||
escalation.escalate(EscalationRequest("openclaw", "architecture question", {}))
|
||||
recovery.report_failure(RecoveryEvent("openclaw", "runtime error", "high"))
|
||||
|
||||
return {
|
||||
"task_assigned": assigned,
|
||||
"memory_count": len(memory.query("openclaw")),
|
||||
"messages": len(bus.consume()),
|
||||
"decision": decision,
|
||||
"escalations": len(escalation.list_requests()),
|
||||
"recoveries": len(recovery.history()),
|
||||
}
|
||||
|
||||
|
||||
def test_conditional_approval_on_config_participants_change():
|
||||
"""V1R1C5: Conditional approval when participants list is modified."""
|
||||
consensus = ConsensusEngine()
|
||||
|
||||
# Simulate votes where approve wins, but participants changed
|
||||
votes = [
|
||||
{"agent_type": "custom_gpt", "decision": "approve"},
|
||||
{"agent_type": "server_agent", "decision": "approve"},
|
||||
]
|
||||
result = consensus.conditional_decide(
|
||||
votes,
|
||||
config_participants_changed=True,
|
||||
)
|
||||
assert result == "conditional_approval", (
|
||||
f"Expected conditional_approval, got {result}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def test_conditional_approval_on_config_modes_change():
|
||||
"""V1R1C5: Conditional approval when modes list is modified."""
|
||||
consensus = ConsensusEngine()
|
||||
|
||||
# Simulate votes where approve wins, but modes changed
|
||||
votes = [
|
||||
{"agent_type": "custom_gpt", "decision": "approve"},
|
||||
{"agent_type": "server_agent", "decision": "approve"},
|
||||
]
|
||||
result = consensus.conditional_decide(
|
||||
votes,
|
||||
config_modes_changed=True,
|
||||
)
|
||||
assert result == "conditional_approval", (
|
||||
f"Expected conditional_approval, got {result}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def test_conditional_approval_on_config_disabled():
|
||||
"""V1R1C5: Conditional approval when consensus is disabled."""
|
||||
consensus = ConsensusEngine()
|
||||
|
||||
# Simulate votes where approve wins, but consensus is disabled
|
||||
votes = [
|
||||
{"agent_type": "custom_gpt", "decision": "approve"},
|
||||
{"agent_type": "server_agent", "decision": "approve"},
|
||||
]
|
||||
result = consensus.conditional_decide(
|
||||
votes,
|
||||
config_disabled=True,
|
||||
)
|
||||
assert result == "conditional_approval", (
|
||||
f"Expected conditional_approval, got {result}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def test_conditional_approval_on_weights_adjusted():
|
||||
"""V1R1C5: Conditional approval when weights are significantly adjusted."""
|
||||
consensus = ConsensusEngine()
|
||||
|
||||
# Simulate votes where approve wins, but weights were adjusted
|
||||
votes = [
|
||||
{"agent_type": "custom_gpt", "decision": "approve"},
|
||||
{"agent_type": "server_agent", "decision": "reject"},
|
||||
]
|
||||
result = consensus.conditional_decide(
|
||||
votes,
|
||||
config_weights_adjusted=True,
|
||||
)
|
||||
# Here approve=2, reject=1 so approve wins, but conditional due to weight changes
|
||||
assert result == "conditional_approval", (
|
||||
f"Expected conditional_approval, got {result}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def test_conditional_approval_unrelated_change_approves():
|
||||
"""V1R1C5: Unrelated changes should still get full approval."""
|
||||
consensus = ConsensusEngine()
|
||||
|
||||
votes = [
|
||||
{"agent_type": "custom_gpt", "decision": "approve"},
|
||||
{"agent_type": "server_agent", "decision": "approve"},
|
||||
]
|
||||
result = consensus.conditional_decide(
|
||||
votes,
|
||||
config_participants_changed=False,
|
||||
config_modes_changed=False,
|
||||
config_disabled=False,
|
||||
config_weights_adjusted=False,
|
||||
)
|
||||
assert result == "approve", (
|
||||
f"Expected approve when no config changes, got {result}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def test_reject_on_majority_reject():
|
||||
"""Reject when majority votes reject, even without config changes."""
|
||||
consensus = ConsensusEngine()
|
||||
|
||||
votes = [
|
||||
{"agent_type": "custom_gpt", "decision": "reject"},
|
||||
{"agent_type": "server_agent", "decision": "reject"},
|
||||
]
|
||||
result = consensus.conditional_decide(
|
||||
votes,
|
||||
config_participants_changed=True, # even with changes
|
||||
)
|
||||
assert result == "review", (
|
||||
f"Expected review when majority rejects, got {result}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def test_conditional_vote_in_vote_payload():
|
||||
"""V1R1C5: A vote with condition=True triggers conditional approval."""
|
||||
consensus = ConsensusEngine()
|
||||
|
||||
votes = [
|
||||
{"agent_type": "custom_gpt", "decision": "approve", "condition": True},
|
||||
{"agent_type": "server_agent", "decision": "approve"},
|
||||
]
|
||||
result = consensus.decide(votes)
|
||||
assert result == "conditional_approval", (
|
||||
f"Expected conditional_approval for conditional vote, got {result}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def test_no_conditional_vote_approves_normally():
|
||||
"""Without any conditional votes, approval proceeds normally."""
|
||||
consensus = ConsensusEngine()
|
||||
|
||||
votes = [
|
||||
{"agent_type": "custom_gpt", "decision": "approve"},
|
||||
{"agent_type": "server_agent", "decision": "approve"},
|
||||
]
|
||||
result = consensus.decide(votes)
|
||||
assert result == "approve", (
|
||||
f"Expected approve without conditional votes, got {result}"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def run_all_tests():
|
||||
tests = [
|
||||
("conditional_approval_on_config_participants_change", test_conditional_approval_on_config_participants_change),
|
||||
("conditional_approval_on_config_modes_change", test_conditional_approval_on_config_modes_change),
|
||||
("conditional_approval_on_config_disabled", test_conditional_approval_on_config_disabled),
|
||||
("conditional_approval_on_weights_adjusted", test_conditional_approval_on_weights_adjusted),
|
||||
("conditional_approval_unrelated_change_approves", test_conditional_approval_unrelated_change_approves),
|
||||
("reject_on_majority_reject", test_reject_on_majority_reject),
|
||||
("conditional_vote_in_vote_payload", test_conditional_vote_in_vote_payload),
|
||||
("no_conditional_vote_approves_normally", test_no_conditional_vote_approves_normally),
|
||||
]
|
||||
results = {}
|
||||
for name, test_fn in tests:
|
||||
try:
|
||||
result = test_fn()
|
||||
results[name] = {"status": "PASS", "result": result}
|
||||
print(f" ✓ {name}: {result}")
|
||||
except AssertionError as e:
|
||||
results[name] = {"status": "FAIL", "error": str(e)}
|
||||
print(f" ✗ {name}: {e}")
|
||||
print(f"\n{'='*60}")
|
||||
passed = sum(1 for v in results.values() if v["status"] == "PASS")
|
||||
total = len(tests)
|
||||
print(f"V1R1C5 Conditional Approval Tests: {passed}/{total} passed")
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Integration Test Suite\n" + "=" * 60)
|
||||
result = run_integration_test()
|
||||
print(f"\nBase Integration Test Result: {result}")
|
||||
print("\n--- V1R1C5 Conditional Approval Tests ---")
|
||||
run_all_tests()
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Agent OS memory service runtime."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
@dataclass
|
||||
class MemoryEntry:
|
||||
memory_id: str
|
||||
agent_id: str
|
||||
memory_type: str
|
||||
content: dict
|
||||
quality_score: float = 0.0
|
||||
|
||||
|
||||
class MemoryService:
|
||||
def __init__(self):
|
||||
self.store: Dict[str, List[MemoryEntry]] = {}
|
||||
|
||||
def save(self, entry: MemoryEntry):
|
||||
self.store.setdefault(entry.agent_id, []).append(entry)
|
||||
|
||||
def query(self, agent_id: str, memory_type: str = None):
|
||||
items = self.store.get(agent_id, [])
|
||||
if memory_type:
|
||||
return [m for m in items if m.memory_type == memory_type]
|
||||
return items
|
||||
|
||||
def reflect(self, agent_id: str):
|
||||
return self.query(agent_id, "reflection")
|
||||
@@ -0,0 +1,29 @@
|
||||
"""Agent OS message bus runtime."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
|
||||
@dataclass
|
||||
class AgentMessage:
|
||||
sender: str
|
||||
receiver: str
|
||||
event: str
|
||||
payload: dict
|
||||
|
||||
|
||||
class MessageBus:
|
||||
def __init__(self):
|
||||
self.queue: List[AgentMessage] = []
|
||||
|
||||
def send(self, message: AgentMessage):
|
||||
self.queue.append(message)
|
||||
|
||||
def broadcast(self, sender: str, event: str, payload: dict, receivers: List[str]):
|
||||
for receiver in receivers:
|
||||
self.send(AgentMessage(sender, receiver, event, payload))
|
||||
|
||||
def consume(self):
|
||||
messages = list(self.queue)
|
||||
self.queue.clear()
|
||||
return messages
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Agent OS recovery manager."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
|
||||
@dataclass
|
||||
class RecoveryEvent:
|
||||
source: str
|
||||
issue: str
|
||||
severity: str
|
||||
assigned_agent: str = "windows_codex_app"
|
||||
|
||||
|
||||
class RecoveryManager:
|
||||
def __init__(self):
|
||||
self.events: List[RecoveryEvent] = []
|
||||
|
||||
def report_failure(self, event: RecoveryEvent):
|
||||
self.events.append(event)
|
||||
return {"status": "recovery_started", "assigned_agent": event.assigned_agent}
|
||||
|
||||
def history(self):
|
||||
return self.events
|
||||
|
||||
def needs_admin_authorization(self, event: RecoveryEvent):
|
||||
return event.severity in {"high", "critical"}
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Agent OS skill loader runtime."""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
@dataclass
|
||||
class SkillPackage:
|
||||
skill_id: str
|
||||
version: str
|
||||
capabilities: List[str] = field(default_factory=list)
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
class SkillLoader:
|
||||
def __init__(self):
|
||||
self.skills: Dict[str, SkillPackage] = {}
|
||||
|
||||
def register(self, skill: SkillPackage):
|
||||
self.skills[skill.skill_id] = skill
|
||||
|
||||
def validate(self, skill_id: str):
|
||||
skill = self.skills.get(skill_id)
|
||||
return bool(skill and skill.enabled and skill.version)
|
||||
|
||||
def load_for_capability(self, capability: str):
|
||||
return [s for s in self.skills.values() if capability in s.capabilities and s.enabled]
|
||||
|
||||
def disable(self, skill_id: str):
|
||||
if skill_id in self.skills:
|
||||
self.skills[skill_id].enabled = False
|
||||
@@ -0,0 +1,36 @@
|
||||
"""Agent OS task scheduler runtime."""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
@dataclass
|
||||
class Task:
|
||||
task_id: str
|
||||
capability: str
|
||||
priority: int = 0
|
||||
status: str = "queued"
|
||||
assigned_agent: str = ""
|
||||
|
||||
|
||||
class TaskScheduler:
|
||||
def __init__(self):
|
||||
self.tasks: Dict[str, Task] = {}
|
||||
self.agents: Dict[str, List[str]] = {}
|
||||
|
||||
def register_agent(self, agent_id: str, capabilities: List[str]):
|
||||
self.agents[agent_id] = capabilities
|
||||
|
||||
def submit(self, task: Task):
|
||||
self.tasks[task.task_id] = task
|
||||
|
||||
def dispatch(self):
|
||||
for task in self.tasks.values():
|
||||
if task.status != "queued":
|
||||
continue
|
||||
for agent, caps in self.agents.items():
|
||||
if task.capability in caps:
|
||||
task.assigned_agent = agent
|
||||
task.status = "assigned"
|
||||
break
|
||||
return list(self.tasks.values())
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Policy helpers for GPT Actions GitOps write operations.
|
||||
|
||||
The helpers intentionally avoid runtime credentials. They are used by tests and
|
||||
smoke checks to keep the OpenAPI consequential-action split aligned with server
|
||||
side write policy requirements.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import PurePosixPath
|
||||
import re
|
||||
|
||||
|
||||
LOW_RISK_PATH_SCOPES: dict[str, tuple[str, ...]] = {
|
||||
"commitReportFile": ("reports/",),
|
||||
"commitDocsFile": ("docs/",),
|
||||
"commitProposalFile": ("proposals/",),
|
||||
"commitPatchEvidence": ("reports/", "patches/"),
|
||||
}
|
||||
|
||||
HIGH_RISK_OPERATIONS: set[str] = {
|
||||
"commitFile",
|
||||
"commitMainFile",
|
||||
"createRollbackRequest",
|
||||
"requestRollback",
|
||||
"requestOrCreateTag",
|
||||
"commitRuntimeFile",
|
||||
"commitConfigFile",
|
||||
"mergeMain",
|
||||
"adjustProductionModelPriority",
|
||||
"setModelPriority",
|
||||
"registerModel",
|
||||
}
|
||||
|
||||
SECRET_PATH_PATTERNS: tuple[str, ...] = (
|
||||
"runtime/secrets/",
|
||||
".env",
|
||||
"private/",
|
||||
"prod-secrets/",
|
||||
)
|
||||
|
||||
SECRET_CONTENT_PATTERNS: tuple[re.Pattern[str], ...] = (
|
||||
re.compile(r"sk-[A-Za-z0-9_-]{8,}"),
|
||||
re.compile(r"Bearer\s+\S+", re.IGNORECASE),
|
||||
re.compile(r"BEGIN\s+PRIVATE\s+KEY", re.IGNORECASE),
|
||||
re.compile(r"password\s*=\s*\S+", re.IGNORECASE),
|
||||
re.compile(r"api_key\s*=\s*\S+", re.IGNORECASE),
|
||||
re.compile(r"access_token\s*=\s*\S+", re.IGNORECASE),
|
||||
re.compile(r"refresh_token\s*=\s*\S+", re.IGNORECASE),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PolicyResult:
|
||||
allowed: bool
|
||||
reason: str
|
||||
|
||||
|
||||
def normalize_path(path: str) -> str:
|
||||
raw = (path or "").replace("\\", "/").strip()
|
||||
if not raw:
|
||||
raise ValueError("path_required")
|
||||
posix = PurePosixPath(raw)
|
||||
if posix.is_absolute():
|
||||
raise ValueError("absolute_path_forbidden")
|
||||
if any(part in {"..", ".git"} for part in posix.parts):
|
||||
raise ValueError("unsafe_path_forbidden")
|
||||
return str(posix)
|
||||
|
||||
|
||||
def is_main_branch(branch: str | None) -> bool:
|
||||
value = (branch or "").strip()
|
||||
return value in {"main", "master"} or value.startswith("main/") or value.startswith("master/")
|
||||
|
||||
|
||||
def is_secret_path(path: str) -> bool:
|
||||
normalized = normalize_path(path)
|
||||
name = PurePosixPath(normalized).name
|
||||
if name.endswith(".token"):
|
||||
return True
|
||||
return any(normalized == pattern.rstrip("/") or normalized.startswith(pattern) for pattern in SECRET_PATH_PATTERNS)
|
||||
|
||||
|
||||
def contains_secret_like_content(content: str) -> bool:
|
||||
text = content or ""
|
||||
return any(pattern.search(text) for pattern in SECRET_CONTENT_PATTERNS)
|
||||
|
||||
|
||||
def path_in_scope(operation_id: str, path: str) -> bool:
|
||||
normalized = normalize_path(path)
|
||||
scopes = LOW_RISK_PATH_SCOPES.get(operation_id, ())
|
||||
return any(normalized.startswith(scope) for scope in scopes)
|
||||
|
||||
|
||||
def validate_low_risk_commit(operation_id: str, branch: str, path: str, content: str = "") -> PolicyResult:
|
||||
if operation_id not in LOW_RISK_PATH_SCOPES:
|
||||
return PolicyResult(False, "unknown_low_risk_operation")
|
||||
if is_main_branch(branch):
|
||||
return PolicyResult(False, "low_risk_action_rejects_main")
|
||||
try:
|
||||
normalized = normalize_path(path)
|
||||
except ValueError as exc:
|
||||
return PolicyResult(False, str(exc))
|
||||
if is_secret_path(normalized):
|
||||
return PolicyResult(False, "secret_path_rejected")
|
||||
if not path_in_scope(operation_id, normalized):
|
||||
return PolicyResult(False, "path_out_of_scope")
|
||||
if contains_secret_like_content(content):
|
||||
return PolicyResult(False, "secret_like_content_detected")
|
||||
return PolicyResult(True, "allowed")
|
||||
|
||||
|
||||
def validate_write_policy(operation_id: str, branch: str, path: str, content: str = "") -> PolicyResult:
|
||||
try:
|
||||
normalized = normalize_path(path)
|
||||
except ValueError as exc:
|
||||
return PolicyResult(False, str(exc))
|
||||
if is_secret_path(normalized):
|
||||
return PolicyResult(False, "secret_path_rejected")
|
||||
if contains_secret_like_content(content):
|
||||
return PolicyResult(False, "secret_like_content_detected")
|
||||
if operation_id in LOW_RISK_PATH_SCOPES:
|
||||
return validate_low_risk_commit(operation_id, branch, normalized, content)
|
||||
if operation_id in HIGH_RISK_OPERATIONS:
|
||||
return PolicyResult(True, "high_risk_requires_confirmation")
|
||||
return PolicyResult(False, "unknown_operation")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("GPT Actions GitOps policy helper loaded")
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCHEMA = ROOT / "docs" / "gitops" / "openapi-chatgpt-actions.yaml"
|
||||
|
||||
LOW_RISK_FALSE = {
|
||||
"commitReportFile",
|
||||
"commitDocsFile",
|
||||
"commitProposalFile",
|
||||
"commitPatchEvidence",
|
||||
}
|
||||
|
||||
HIGH_RISK_TRUE = {
|
||||
"commitFile",
|
||||
"commitMainFile",
|
||||
"createRollbackRequest",
|
||||
"requestRollback",
|
||||
"requestOrCreateTag",
|
||||
"setModelPriority",
|
||||
"registerModel",
|
||||
}
|
||||
|
||||
SECRET_LIKE = re.compile(
|
||||
r"sk-[A-Za-z0-9_-]{8,}|Bearer\s+\S+|BEGIN\s+PRIVATE\s+KEY|password\s*=|api_key\s*=|access_token\s*=|refresh_token\s*=",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def load_schema() -> dict:
|
||||
if not SCHEMA.exists():
|
||||
raise AssertionError(f"schema missing: {SCHEMA}")
|
||||
return yaml.safe_load(SCHEMA.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def operations(schema: dict) -> dict[str, dict]:
|
||||
found: dict[str, dict] = {}
|
||||
for path_item in schema.get("paths", {}).values():
|
||||
if not isinstance(path_item, dict):
|
||||
continue
|
||||
for method, operation in path_item.items():
|
||||
if method.lower() not in {"get", "post", "put", "patch", "delete"}:
|
||||
continue
|
||||
if isinstance(operation, dict) and operation.get("operationId"):
|
||||
found[operation["operationId"]] = operation
|
||||
return found
|
||||
|
||||
|
||||
def collect_examples(value):
|
||||
if isinstance(value, dict):
|
||||
for key, item in value.items():
|
||||
if key in {"example", "examples"}:
|
||||
yield item
|
||||
yield from collect_examples(item)
|
||||
elif isinstance(value, list):
|
||||
for item in value:
|
||||
yield from collect_examples(item)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
schema = load_schema()
|
||||
ops = operations(schema)
|
||||
failures: list[str] = []
|
||||
|
||||
for op_id in sorted(LOW_RISK_FALSE):
|
||||
operation = ops.get(op_id)
|
||||
if not operation:
|
||||
failures.append(f"missing low-risk operation: {op_id}")
|
||||
elif operation.get("x-openai-isConsequential") is not False:
|
||||
failures.append(f"{op_id} must set x-openai-isConsequential=false")
|
||||
|
||||
for op_id in sorted(HIGH_RISK_TRUE):
|
||||
operation = ops.get(op_id)
|
||||
if not operation:
|
||||
failures.append(f"missing high-risk operation: {op_id}")
|
||||
elif operation.get("x-openai-isConsequential") is not True:
|
||||
failures.append(f"{op_id} must set x-openai-isConsequential=true")
|
||||
|
||||
security_text = json.dumps(schema.get("components", {}).get("securitySchemes", {}), ensure_ascii=False)
|
||||
if SECRET_LIKE.search(security_text):
|
||||
failures.append("securitySchemes contain secret-like content")
|
||||
|
||||
for example in collect_examples(schema):
|
||||
if SECRET_LIKE.search(json.dumps(example, ensure_ascii=False)):
|
||||
failures.append("schema examples contain secret-like content")
|
||||
break
|
||||
|
||||
print("GPT Actions consequential governance smoke")
|
||||
print("schema:", SCHEMA)
|
||||
print("low-risk:", ", ".join(sorted(LOW_RISK_FALSE)))
|
||||
print("high-risk:", ", ".join(sorted(HIGH_RISK_TRUE)))
|
||||
if failures:
|
||||
print("status: failed")
|
||||
for failure in failures:
|
||||
print("-", failure)
|
||||
return 1
|
||||
print("status: passed")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,45 @@
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
APP = ROOT / "active-runtime" / "app" / "aios_chatgpt_actions_api.py"
|
||||
|
||||
|
||||
def module_tree():
|
||||
return ast.parse(APP.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def test_runtime_defines_low_risk_route_map():
|
||||
text = APP.read_text(encoding="utf-8")
|
||||
for route in [
|
||||
"/api/gitops/report-file",
|
||||
"/api/gitops/docs-file",
|
||||
"/api/gitops/proposal-file",
|
||||
"/api/gitops/patch-evidence",
|
||||
"/api/gitops/main-file",
|
||||
"/api/gitops/rollback-request",
|
||||
]:
|
||||
assert route in text
|
||||
|
||||
|
||||
def test_runtime_has_low_risk_and_main_handlers():
|
||||
names = {node.name for node in ast.walk(module_tree()) if isinstance(node, ast.FunctionDef)}
|
||||
assert "low_risk_file" in names
|
||||
assert "main_file" in names
|
||||
assert "secret_path" in names
|
||||
assert "path_in_scope" in names
|
||||
|
||||
|
||||
def test_runtime_policy_rejects_main_and_secret_paths_in_low_risk_handler():
|
||||
text = APP.read_text(encoding="utf-8")
|
||||
assert "low-risk write requires explicit non-main patch/request branch" in text
|
||||
assert "Secret-like path rejected. Use runtime secret store or secret_ref." in text
|
||||
assert "path_out_of_scope" in text
|
||||
assert "Secret-like content detected. Use runtime secret store or secret_ref." in text
|
||||
|
||||
|
||||
def test_runtime_preserves_legacy_commit_file_route():
|
||||
text = APP.read_text(encoding="utf-8")
|
||||
assert "if path == '/api/file': return self.file(data)" in text
|
||||
assert "def file(self,data):" in text
|
||||
@@ -0,0 +1,70 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
from gitops_actions_policy import validate_low_risk_commit, validate_write_policy # noqa: E402
|
||||
|
||||
|
||||
def assert_allowed(operation_id, branch, path, content="safe content"):
|
||||
result = validate_low_risk_commit(operation_id, branch, path, content)
|
||||
assert result.allowed, result.reason
|
||||
|
||||
|
||||
def assert_rejected(operation_id, branch, path, reason, content="safe content"):
|
||||
result = validate_low_risk_commit(operation_id, branch, path, content)
|
||||
assert not result.allowed
|
||||
assert result.reason == reason
|
||||
|
||||
|
||||
def test_commit_report_file_scope():
|
||||
assert_allowed("commitReportFile", "patch/gpt-actions-consequential-governance", "reports/example.md")
|
||||
assert_rejected("commitReportFile", "patch/gpt-actions-consequential-governance", "docs/example.md", "path_out_of_scope")
|
||||
|
||||
|
||||
def test_commit_docs_file_scope():
|
||||
assert_allowed("commitDocsFile", "patch/gpt-actions-consequential-governance", "docs/architecture/example.md")
|
||||
assert_rejected("commitDocsFile", "patch/gpt-actions-consequential-governance", "reports/example.md", "path_out_of_scope")
|
||||
|
||||
|
||||
def test_commit_proposal_and_patch_evidence_scopes():
|
||||
assert_allowed("commitProposalFile", "patch/gpt-actions-consequential-governance", "proposals/change.json")
|
||||
assert_allowed("commitPatchEvidence", "patch/gpt-actions-consequential-governance", "reports/evidence.md")
|
||||
assert_allowed("commitPatchEvidence", "patch/gpt-actions-consequential-governance", "patches/evidence.json")
|
||||
assert_rejected("commitPatchEvidence", "patch/gpt-actions-consequential-governance", "docs/evidence.md", "path_out_of_scope")
|
||||
|
||||
|
||||
def test_low_risk_actions_reject_main_branch():
|
||||
for operation_id in ["commitReportFile", "commitDocsFile", "commitProposalFile", "commitPatchEvidence"]:
|
||||
assert_rejected(operation_id, "main", "reports/example.md", "low_risk_action_rejects_main")
|
||||
|
||||
|
||||
def test_all_write_actions_reject_secret_paths():
|
||||
secret_paths = ["runtime/secrets/key.txt", ".env", "private/key.txt", "prod-secrets/config.json", "reports/value.token"]
|
||||
for operation_id in ["commitReportFile", "commitDocsFile", "commitPatchEvidence", "commitFile", "commitMainFile", "createRollbackRequest"]:
|
||||
for path in secret_paths:
|
||||
result = validate_write_policy(operation_id, "patch/gpt-actions-consequential-governance", path)
|
||||
assert not result.allowed
|
||||
assert result.reason == "secret_path_rejected"
|
||||
|
||||
|
||||
def test_secret_like_content_is_rejected():
|
||||
samples = [
|
||||
"Bearer abc",
|
||||
"password=abc",
|
||||
"api_key=abc",
|
||||
"BEGIN PRIVATE KEY",
|
||||
"access_token=abc",
|
||||
"refresh_token=abc",
|
||||
]
|
||||
for sample in samples:
|
||||
result = validate_low_risk_commit(
|
||||
"commitReportFile",
|
||||
"patch/gpt-actions-consequential-governance",
|
||||
"reports/example.md",
|
||||
sample,
|
||||
)
|
||||
assert not result.allowed
|
||||
assert result.reason == "secret_like_content_detected"
|
||||
@@ -0,0 +1,76 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCHEMA = ROOT / "docs" / "gitops" / "openapi-chatgpt-actions.yaml"
|
||||
|
||||
|
||||
def load_schema():
|
||||
return yaml.safe_load(SCHEMA.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def operations_by_id(schema):
|
||||
operations = {}
|
||||
for path_item in schema.get("paths", {}).values():
|
||||
if not isinstance(path_item, dict):
|
||||
continue
|
||||
for method, operation in path_item.items():
|
||||
if method.lower() not in {"get", "post", "put", "patch", "delete"}:
|
||||
continue
|
||||
if isinstance(operation, dict) and operation.get("operationId"):
|
||||
operations[operation["operationId"]] = operation
|
||||
return operations
|
||||
|
||||
|
||||
def _examples(value):
|
||||
if isinstance(value, dict):
|
||||
for key, item in value.items():
|
||||
if key in {"example", "examples"}:
|
||||
yield item
|
||||
yield from _examples(item)
|
||||
elif isinstance(value, list):
|
||||
for item in value:
|
||||
yield from _examples(item)
|
||||
|
||||
|
||||
def test_low_risk_operations_are_non_consequential():
|
||||
ops = operations_by_id(load_schema())
|
||||
for op_id in ["commitReportFile", "commitDocsFile", "commitProposalFile", "commitPatchEvidence"]:
|
||||
assert op_id in ops
|
||||
assert ops[op_id].get("x-openai-isConsequential") is False
|
||||
|
||||
|
||||
def test_high_risk_operations_remain_consequential():
|
||||
ops = operations_by_id(load_schema())
|
||||
for op_id in [
|
||||
"commitFile",
|
||||
"commitMainFile",
|
||||
"createRollbackRequest",
|
||||
"requestRollback",
|
||||
"requestOrCreateTag",
|
||||
"setModelPriority",
|
||||
"registerModel",
|
||||
]:
|
||||
assert op_id in ops
|
||||
assert ops[op_id].get("x-openai-isConsequential") is True
|
||||
|
||||
|
||||
def test_legacy_commit_file_is_preserved_as_consequential():
|
||||
ops = operations_by_id(load_schema())
|
||||
assert ops["commitFile"].get("x-openai-isConsequential") is True
|
||||
|
||||
|
||||
def test_security_schemes_and_examples_do_not_embed_real_credentials():
|
||||
schema = load_schema()
|
||||
secret_like = re.compile(
|
||||
r"sk-[A-Za-z0-9_-]{8,}|BEGIN\s+PRIVATE\s+KEY|password\s*=|api_key\s*=|access_token\s*=|refresh_token\s*=",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
checked = json.dumps(schema.get("components", {}).get("securitySchemes", {}), ensure_ascii=False)
|
||||
for value in _examples(schema):
|
||||
checked += "\n" + json.dumps(value, ensure_ascii=False)
|
||||
assert not secret_like.search(checked)
|
||||
Reference in New Issue
Block a user