Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
96282f9
docs: define Workers AI proxy deployment
kyoneken Aug 15, 2026
56cec31
docs: plan Workers AI proxy implementation
kyoneken Aug 15, 2026
5a7dd18
docs: link implementation tracking issues
kyoneken Aug 15, 2026
79d34cc
chore: ignore agent workspaces
kyoneken Aug 15, 2026
ecfa3aa
docs: make secret leak checks exact
kyoneken Aug 15, 2026
1212287
feat: validate Workers AI proxy requests
kyoneken Aug 15, 2026
eb1c0b0
fix: avoid recursive proxy request validation
kyoneken Aug 15, 2026
bc98705
feat: adapt Workers AI responses for OpenClaw
kyoneken Aug 15, 2026
710fde2
feat: expose authenticated Workers AI proxy
kyoneken Aug 15, 2026
1497d02
feat: configure OpenClaw for the Worker AI proxy
kyoneken Aug 15, 2026
e4ac311
docs: describe Workers AI proxy deployment
kyoneken Aug 15, 2026
7efa78c
fix: align OpenClaw runtime config path
kyoneken Aug 15, 2026
6b573d9
docs: clarify E2E storage credentials
kyoneken Aug 19, 2026
490edfd
fix: report R2 binding storage status
kyoneken Aug 19, 2026
9050cdb
chore: update vulnerable build dependencies
kyoneken Aug 19, 2026
844fb80
fix: keep basic-ftp override compatible
kyoneken Aug 19, 2026
ef287b2
fix: adapt OpenAI-compatible Workers AI responses
kyoneken Aug 22, 2026
24cb278
fix: show latest R2 backup time
kyoneken Aug 22, 2026
dc9ed2e
fix: preserve OpenClaw state across restarts
kyoneken Aug 22, 2026
49af6ed
fix: recover gateway after snapshot restart
kyoneken Aug 23, 2026
a9a0c40
fix: isolate gateway health probe failures
kyoneken Aug 23, 2026
7ba6631
fix: safely recreate sandbox from backup
kyoneken Aug 23, 2026
29e2d9a
Merge pull request #9 from feat/workers-ai-proxy-impl
kyoneken Aug 23, 2026
4ebe783
feat: support thread-first Slack conversations
kyoneken Aug 23, 2026
b66bd47
fix: secure Slack config migration
kyoneken Aug 23, 2026
f6a27c5
Merge pull request #21 from kyoneken/codex/issue-19-slack-threading
kyoneken Aug 23, 2026
6c7ed17
docs: design custom domain cutover
kyoneken Aug 24, 2026
887d0d1
docs: plan custom domain cutover
kyoneken Aug 24, 2026
6b47508
docs: design qwen workers ai model addition
kyoneken Aug 24, 2026
eebfea6
feat: add custom domain cutover configuration
kyoneken Aug 24, 2026
3623cd6
docs: plan qwen workers ai model implementation
kyoneken Aug 24, 2026
92c08e2
feat: add qwen model registry and listing
kyoneken Aug 24, 2026
ad03b23
feat: register qwen with openclaw
kyoneken Aug 24, 2026
80b3ac5
feat: normalize qwen reasoning and tools
kyoneken Aug 24, 2026
2d5adb7
fix: scope reasoning normalization to qwen
kyoneken Aug 24, 2026
3d81298
docs: add qwen production smoke workflow
kyoneken Aug 24, 2026
f02b00f
fix: require tools in qwen smoke checks
kyoneken Aug 24, 2026
4bbc9d2
style: format qwen response tests
kyoneken Aug 24, 2026
4db25d7
feat: add workers ai model addition skill
kyoneken Aug 24, 2026
c6307a0
fix: cover model registry container contract
kyoneken Aug 25, 2026
6314140
Merge pull request #23 from kyoneken/codex/issue-15-qwen-model
kyoneken Aug 25, 2026
0c34d45
fix: enable visible Slack group replies
kyoneken Aug 27, 2026
1c0983d
fix: normalize group chat config shapes
kyoneken Aug 27, 2026
5803960
chore: retire legacy worker URLs
kyoneken Aug 27, 2026
cc00c38
Merge origin/main into custom-domain feature
kyoneken Aug 27, 2026
fee39ed
Merge pull request #24 from kyoneken/codex/issue-16-custom-domain
kyoneken Aug 27, 2026
c820d83
feat: notify Slack when cold-start gateway is ready (#26)
codex-mcp-app[bot] Aug 30, 2026
f889ea8
feat: unify Auth0 Access authentication
codex-mcp-app[bot] Aug 31, 2026
598f191
feat: add implementation-ready issue workflow (#33)
codex-mcp-app[bot] Sep 2, 2026
7e5e127
docs: add workers.dev CDP Access bypass (#36)
codex-mcp-app[bot] Sep 2, 2026
418b146
feat: guard upstream writes with Codex Hooks (#37)
codex-mcp-app[bot] Sep 2, 2026
258f7ad
feat: prevent accidental writes to upstream repository (#38)
antigravity-github-app[bot] Sep 2, 2026
a019c83
docs(workflow): add easy-issue-workflow skill and visibility guidelin…
antigravity-github-app[bot] Sep 2, 2026
fad72cf
fix(guards): enforce human review hard-gate and prompt on PR merge (#40)
antigravity-github-app[bot] Sep 2, 2026
efeed0c
fix: restore reliable gateway cold starts (#31)
codex-mcp-app[bot] Sep 5, 2026
1bbc12a
feat: add Browser Run fetch and web diagnostics (#42)
codex-mcp-app[bot] Sep 5, 2026
be666f1
fix: restrict debug CLI commands (#25)
kyoneken Sep 5, 2026
7c31591
fix: remove unsupported web fetch SSRF config (#44)
codex-mcp-app[bot] Sep 5, 2026
50676f5
fix: upgrade OpenClaw past primary session eviction bug (#46)
codex-mcp-app[bot] Sep 5, 2026
34d58f7
feat(admin): model allowlist, session selection, usage panel (#14)
kyoneken Sep 5, 2026
4178506
feat(admin): session-model and usage API routes, inference no-fallbac…
kyoneken Sep 5, 2026
b0bb295
feat(admin): wire admin APIs and stable inference errors (#14)
kyoneken Sep 5, 2026
8b51e2d
feat(admin): add models, session-model, and usage admin routes (#14)
kyoneken Sep 5, 2026
ab0edde
feat(admin): mount model/usage routes on admin API (#14)
kyoneken Sep 5, 2026
64d8f93
feat(admin): model picker and usage panel in Admin UI (#14)
kyoneken Sep 5, 2026
59f46dc
feat(admin): render model catalog, picker, and usage on AdminPage (#14)
kyoneken Sep 5, 2026
becd8cc
feat(admin): mount ModelUsagePanel on AdminPage (#14)
kyoneken Sep 5, 2026
95ef568
fix(admin): restore AdminPage and mount ModelUsagePanel (#14)
kyoneken Sep 5, 2026
16bf56d
fix(admin): live AI Gateway usage and session model proxy default (#14)
kyoneken Sep 6, 2026
861366f
Merge pull request #51 from kyoneken/feat/admin-model-usage-14
kyoneken Sep 6, 2026
f59d63b
fix: rebuild proxy attribution headers for OpenClaw gateway
kyoneken Sep 6, 2026
6ff0c88
fix: trust CF Containers 10.0.0.0/8 for OpenClaw proxy attribution
kyoneken Sep 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .agents/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"description": "Guard moltworker upstream repository and GitHub operations before tool execution in Antigravity.",
"moltworker-guard": {
"PreToolUse": [
{
"matcher": "run_command|call_mcp_tool|mcp_.*",
"hooks": [
{
"type": "command",
"command": "/usr/bin/env node \"$(git rev-parse --show-toplevel)/.agents/hooks/agy-policy-guard.mjs\"",
"timeout": 10
}
]
}
]
}
}
216 changes: 216 additions & 0 deletions .agents/hooks/agy-policy-guard.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

import {
ALWAYS_DENIED_GITHUB_MUTATIONS,
CLOUDFLARE_ISSUE_PR_TOOLS,
findCommands,
READ_ONLY_GITHUB_TOOLS,
tokenizeShell,
} from '../../.codex/hooks/github-policy.mjs';

const UPSTREAM_GITHUB_URL = /(?:github\.com[:/]|api\.github\.com\/repos\/)cloudflare\/moltworker(?:\.git)?(?![a-z0-9_.-])/i;
const UPSTREAM_REMOTE_NAME = /^upstream$/i;
const HTTP_WRITE = /(?:--request|-X)\s*(?:POST|PUT|PATCH|DELETE)\b|(?:--data(?:-[a-z-]+)?|-d|--form|-F|--upload-file|-T)\b|\b(?:post|put|patch|delete)\s*\(/i;

const isRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
const sameIdentity = (actual, expected) => typeof actual === 'string' && actual.toLowerCase() === expected;

const deny = (reason) => ({ allowed: false, reason });

const firstNonOptionArgument = (args) => {
const optionsWithValues = new Set(['-C', '-c', '--config', '--config-env', '--exec-path', '--git-dir', '--namespace', '--super-prefix', '--work-tree']);
for (let index = 0; index < args.length; index += 1) {
const argument = args[index];
if (argument === '--') {
return args[index + 1];
}
if (optionsWithValues.has(argument)) {
index += 1;
continue;
}
if (argument.startsWith('-')) {
continue;
}
return argument;
}
return undefined;
};

const getGitPushTarget = (args) => {
const optionsWithValues = new Set(['--exec', '--push-option', '--receive-pack', '-o', '--repo']);
let index = 0;
while (index < args.length) {
const arg = args[index];
if (arg === '--') {
return args[index + 1];
}
if (arg === '--repo' && index + 1 < args.length) {
return args[index + 1];
}
if (arg.startsWith('--repo=')) {
return arg.split('=', 2)[1];
}
if (optionsWithValues.has(arg)) {
index += 2;
continue;
}
if (arg.startsWith('-')) {
index += 1;
continue;
}
return arg;
}
return undefined;
};

export function evaluateRunCommand(commandLine) {
if (typeof commandLine !== 'string') {
return deny('malformed command');
}

let commands;
try {
commands = findCommands(tokenizeShell(commandLine));
} catch {
return deny('malformed shell input');
}

for (const command of commands) {
const executable = command[0].slice(command[0].lastIndexOf('/') + 1);
const args = command.slice(1);

if (executable === 'gh') {
return deny('GitHub CLI use is forbidden');
}

if (executable === 'git') {
const gitSubcommand = firstNonOptionArgument(args);
if (gitSubcommand === 'push' || gitSubcommand === 'send-pack') {
const pushIndex = args.indexOf(gitSubcommand);
const pushArgs = args.slice(pushIndex + 1);
const target = getGitPushTarget(pushArgs);
if (target) {
if (UPSTREAM_REMOTE_NAME.test(target) || UPSTREAM_GITHUB_URL.test(target)) {
return deny('Push to upstream repository cloudflare/moltworker is forbidden');
}
}
}
}

if (['curl', 'wget', 'http', 'https'].includes(executable)) {
const commandStr = command.join(' ');
if (commandStr.includes('api.github.com') && (UPSTREAM_GITHUB_URL.test(commandStr) || HTTP_WRITE.test(commandStr))) {
if (UPSTREAM_GITHUB_URL.test(commandStr) || commandStr.includes('cloudflare/moltworker')) {
return deny('Direct HTTP write to upstream cloudflare/moltworker is forbidden');
}
}
}
}

return { allowed: true };
}

export function evaluateGitHubMcpCall(toolName, toolInput) {
if (!isRecord(toolInput)) {
return deny('malformed GitHub MCP input');
}

const cloudflareIssuePrTarget = CLOUDFLARE_ISSUE_PR_TOOLS.has(toolName)
&& sameIdentity(toolInput.owner, 'cloudflare');
const cloudflareSearchTarget = (toolName === 'search_issues' || toolName === 'search_pull_requests')
&& (typeof toolInput.query === 'string' && (
/(?:^|\s)(?:org|user):cloudflare(?:\s|$)/i.test(toolInput.query)
|| /(?:^|\s)repo:cloudflare\/\S*/i.test(toolInput.query)
));

if (cloudflareIssuePrTarget || cloudflareSearchTarget) {
return deny('Cloudflare Issue/PR access is forbidden');
}

if (READ_ONLY_GITHUB_TOOLS.has(toolName)) {
return { allowed: true };
}

if (toolName === 'merge_pull_request') {
return { decision: 'force_ask', reason: 'Merging a Pull Request requires explicit human confirmation.' };
}

if (ALWAYS_DENIED_GITHUB_MUTATIONS.has(toolName)) {
return deny('GitHub mutation is forbidden');
}

if (!sameIdentity(toolInput.owner, 'kyoneken') || !sameIdentity(toolInput.repo, 'moltworker')) {
return deny('GitHub mutation is forbidden outside the canonical repository');
}

return { allowed: true };
}

export function evaluateAgyEvent(event) {
if (!isRecord(event) || !isRecord(event.toolCall) || typeof event.toolCall.name !== 'string') {
return deny('malformed AGY Hook event');
}

const toolName = event.toolCall.name;
const toolArgs = event.toolCall.args || {};

if (toolName === 'run_command') {
return evaluateRunCommand(toolArgs.CommandLine);
}

if (toolName === 'call_mcp_tool') {
if (toolArgs.ServerName === 'github' && typeof toolArgs.ToolName === 'string') {
return evaluateGitHubMcpCall(toolArgs.ToolName, toolArgs.Arguments || {});
}
return { allowed: true };
}

if (toolName.startsWith('mcp_github_') || toolName.startsWith('mcp__github__')) {
const shortName = toolName.replace(/^mcp_github_|^mcp__github__/, '');
return evaluateGitHubMcpCall(shortName, toolArgs);
}

return { allowed: true };
}

export async function main() {
try {
process.stdin.setEncoding('utf8');
let input = '';
for await (const chunk of process.stdin) {
input += chunk;
}

if (!input.trim()) {
process.stdout.write(JSON.stringify({ decision: 'allow' }) + '\n');
return;
}

const event = JSON.parse(input);
const result = evaluateAgyEvent(event);

if (result.decision) {
process.stdout.write(JSON.stringify({
decision: result.decision,
reason: result.reason || 'Requires confirmation',
}) + '\n');
} else if (!result.allowed) {
process.stdout.write(JSON.stringify({
decision: 'deny',
reason: `Blocked by moltworker repository policy: ${result.reason}`,
}) + '\n');
} else {
process.stdout.write(JSON.stringify({
decision: 'allow',
}) + '\n');
}
} catch (err) {
process.stderr.write('Malformed AGY policy Hook input\n');
process.exitCode = 2;
}
}

if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
void main();
}
24 changes: 24 additions & 0 deletions .codex/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"description": "Guard moltworker GitHub operations before tool execution.",
"hooks": {
"PreToolUse": [
{
"matcher": "^Bash$|^mcp__github__.*",
"hooks": [
{
"type": "command",
"command": "/usr/bin/env node \"$(git rev-parse --show-toplevel)/.codex/hooks/github-policy.mjs\"",
"timeout": 10,
"statusMessage": "Checking repository GitHub policy"
},
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/upstream_write_guard.py\"",
"timeout": 10,
"statusMessage": "Checking the upstream read-only boundary"
}
]
}
]
}
}
Loading