autoreg-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
54
skills/conol-automation/references/auto-repair-daemon.md
Normal file
54
skills/conol-automation/references/auto-repair-daemon.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Conol Self-Healing Auto-Repair Daemon
|
||||
|
||||
`eni_repair_daemon.py` — непрерывный мониторинг и авто-ремонт пула аккаунтов.
|
||||
|
||||
## Запуск
|
||||
|
||||
```bash
|
||||
cd C:\Users\User\Desktop\conol_autoreg
|
||||
python eni_repair_daemon.py # непрерывный демон (каждые 5 мин)
|
||||
python eni_repair_daemon.py --once # разовый чек + ремонт
|
||||
```
|
||||
|
||||
Из `start.bat`: пункты **7** (демон) и **8** (разовый).
|
||||
|
||||
## Фазы цикла
|
||||
|
||||
### Phase 1: Health Check
|
||||
Проверяет `POST /api/sessions` на всех аккаунтах параллельно (5 конкурентов), timeout 15s.
|
||||
|
||||
### Phase 2: Server-Down Detection
|
||||
Если >80% не-cookie ошибок = HTTP 500 → **пропускает весь ремонт**. Это conol.ai серверная проблема, не наши куки. Логируется `server_down`. Когда сервер оживает — `server_up`.
|
||||
|
||||
**Отличие от per-account ошибок:**
|
||||
- 1-2 аккаунта 500 = индивидуальный сбой → repair
|
||||
- 80%+ аккаунтов 500 = сервер мёртв → ждать
|
||||
|
||||
### Phase 3: Individual Repair
|
||||
Аккаунт провалил `MAX_FAIL_STREAK=3` проверок подряд:
|
||||
|
||||
| Ошибка | Стратегия | Инструмент |
|
||||
|--------|-----------|------------|
|
||||
| HTTP 401 / "unauthorized" / "cookie" | Re-login через CDP | `relogin_account()` |
|
||||
| "no_cookies" (статус 0) | Re-register | `eni_conol.py reg 1` |
|
||||
|
||||
### Phase 4: Credit Monitoring
|
||||
Баланс < 100 cr → авто-фарм квестов на этом аккаунте.
|
||||
|
||||
### Phase 5: Gateway Health
|
||||
Если гейтвей не отвечает → `taskkill` старого + запуск `gateway.py`. До 3 попыток с паузой 30s. После успешного перезапуска — `POST /pool/reload` для подхвата изменений.
|
||||
|
||||
## Логи и состояние
|
||||
|
||||
- `eni_repair_log.jsonl` — все события (health_ok, health_fail, repair_login, repair_farm, server_down, gateway_restart, ...)
|
||||
- `eni_repair_state.json` — состояние между циклами (fail-счётчики, consecutive_server_downs, время последнего цикла)
|
||||
|
||||
## Пороги
|
||||
|
||||
| Параметр | Значение | ENV override |
|
||||
|----------|----------|-------------|
|
||||
| Интервал проверки | 300s (5 мин) | `ENI_REPAIR_INTERVAL` |
|
||||
| Низкий баланс | < 100 cr | `CREDIT_LOW_THRESHOLD` |
|
||||
| Мёртвый баланс | 0 cr | `CREDIT_DEAD_THRESHOLD` |
|
||||
| Попыток до ремонта | 3 | `MAX_FAIL_STREAK` |
|
||||
| Попыток перезапуска GW | 3 | `GATEWAY_RESTART_ATTEMPTS` |
|
||||
66
skills/conol-automation/references/backend-outage-500.md
Normal file
66
skills/conol-automation/references/backend-outage-500.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Conol.ai Session 500 — Backend Outage Diagnosis
|
||||
|
||||
## Symptom
|
||||
|
||||
Gateway returns HTTP 502 `Conol error: session HTTP 500: {"error":"Failed to create session"}`.
|
||||
Direct `httpx` to `POST /api/sessions` returns HTTP 500 on ALL accounts.
|
||||
|
||||
## Verify: Is it a backend outage or account issue?
|
||||
|
||||
```python
|
||||
import json, httpx
|
||||
|
||||
# Test 3 different accounts
|
||||
for fname in ['cookies_conol066427.json', 'cookies_conol127087.json', 'cookies_conol170189.json']:
|
||||
with open(fname) as f:
|
||||
cookies = {c['name']: c['value'] for c in json.load(f)}
|
||||
|
||||
client = httpx.Client(cookies=cookies, timeout=20)
|
||||
|
||||
# Auth check — should ALWAYS work if cookies are valid
|
||||
r = client.get('https://conol.ai/api/auth/get-session')
|
||||
user = r.json().get('user', {}) if r.status_code == 200 else {}
|
||||
name = user.get('name', '?')
|
||||
|
||||
# Session create — THIS is what fails
|
||||
r = client.post('https://conol.ai/api/sessions', json={
|
||||
'source': {'type': 'home'},
|
||||
'messages': [{'type': 'text', 'content': 'Say OK'}],
|
||||
'agentModel': 'gpt-5.5',
|
||||
'agentEffort': 'low',
|
||||
})
|
||||
|
||||
print(f'{name}: auth={user.get("emailVerified")} | session HTTP {r.status_code} | {r.text[:100]}')
|
||||
```
|
||||
|
||||
## Classification
|
||||
|
||||
| Auth `/get-session` | Session `/api/sessions` | Diagnosis | Action |
|
||||
|-----|-----|-----|-----|
|
||||
| 200 | 201 | ✅ Healthy | Normal operation |
|
||||
| 200 | 500 | ❌ Backend outage | Wait 30-60 min, retry |
|
||||
| 401 | 401 | ❌ Expired cookies | Re-login via Chrome CDP |
|
||||
| 200 | 400 | ❌ Missing fields | Check payload format |
|
||||
|
||||
## Key insight
|
||||
|
||||
When `/api/auth/get-session` returns 200 (cookie is valid) but `/api/sessions` returns 500, this is 100% a **Conol.ai server-side issue**. The cookie/auth is fine — their session creation infrastructure is degraded.
|
||||
|
||||
Do NOT:
|
||||
- Re-login accounts (unnecessary — cookies are valid)
|
||||
- Change code (API format hasn't changed)
|
||||
- Rotate cookies (won't help)
|
||||
|
||||
Just wait and retry.
|
||||
|
||||
## Recovery verification
|
||||
|
||||
```bash
|
||||
# Quick test after waiting
|
||||
curl -s -X POST http://127.0.0.1:9999/v1/chat/completions \
|
||||
-H "Authorization: Bearer test" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model":"gpt-5.5","messages":[{"role":"user","content":"test"}],"max_tokens":5}'
|
||||
```
|
||||
|
||||
If you see `"choices"` in response → backend recovered. If `"detail":"Conol error..."` → still down.
|
||||
137
skills/conol-automation/references/conol-tool-use.md
Normal file
137
skills/conol-automation/references/conol-tool-use.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Conol.ai Tool Use Protocol
|
||||
|
||||
Gateway v6.0+ supports OpenAI-compatible tool calling through conol.ai's SSE API.
|
||||
|
||||
## Sending Tools to Conol.ai
|
||||
|
||||
Tools are passed in the session creation body as an Anthropic-format array:
|
||||
|
||||
```python
|
||||
session_body = {
|
||||
"source": {"type": "home"},
|
||||
"messages": conol_msgs,
|
||||
"timezone": "Europe/Kyiv",
|
||||
"agentModel": model,
|
||||
"agentEffort": "low",
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_weather",
|
||||
"description": "Get current weather for a city",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {"city": {"type": "string"}},
|
||||
"required": ["city"],
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Both Anthropic-format (`name`, `input_schema`) and OpenAI-format (`type: "function"`, `function: {name, parameters}`) are accepted by conol.ai. The gateway converts OpenAI format to Anthropic before sending.
|
||||
|
||||
## Receiving Tool Calls from SSE
|
||||
|
||||
Conol.ai emits tool calls as SSE `history_delta` events with `tool_call` log entries:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "history_delta",
|
||||
"stages": [{
|
||||
"id": "...",
|
||||
"logs": [{
|
||||
"role": "assistant",
|
||||
"type": "tool_call",
|
||||
"content": [{
|
||||
"name": "WebSearch",
|
||||
"type": "tool_call",
|
||||
"input": {"query": "Paris weather"},
|
||||
"result": [{"text": "83°F, Overcast..."}]
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
Key fields:
|
||||
- `name` — tool name (string)
|
||||
- `input` — JSON arguments (dict)
|
||||
- `result` — optional: array of `{"text": "..."}` objects with tool output
|
||||
|
||||
## Converting to OpenAI Format
|
||||
|
||||
```python
|
||||
def conol_to_openai_tool_calls(events):
|
||||
tool_calls = []
|
||||
for ev in events:
|
||||
if ev.tool_name:
|
||||
tool_calls.append({
|
||||
"id": f"call_{uuid.uuid4().hex[:8]}",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": ev.tool_name,
|
||||
"arguments": json.dumps(ev.tool_input, ensure_ascii=False),
|
||||
},
|
||||
})
|
||||
return tool_calls
|
||||
```
|
||||
|
||||
## Built-in Tools (Auto-called by conol.ai)
|
||||
|
||||
Conol.ai models have built-in tools that fire automatically even when no user tools are provided:
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| `WebSearch` | Web search via Exa.ai |
|
||||
| `Fetch` | Fetch URL content with prompt |
|
||||
| `SetIntention` | Internal meta-tool for task routing |
|
||||
| `WriteNote` | Create Obsidian-style notes |
|
||||
| `ReadNote` | Read persisted notes |
|
||||
|
||||
These appear in SSE as `tool_call` logs alongside any user-provided tools.
|
||||
|
||||
## Streaming Tool Calls
|
||||
|
||||
In streaming mode, tool calls are emitted as SSE chunks with `delta.tool_calls[]`:
|
||||
|
||||
```json
|
||||
{
|
||||
"object": "chat.completion.chunk",
|
||||
"choices": [{
|
||||
"delta": {"tool_calls": [{"id": "call_abc", "type": "function", "function": {"name": "WebSearch", "arguments": "{\"query\":\"...\"}"}}]},
|
||||
"finish_reason": null
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
Then final chunk with `finish_reason: "tool_calls"` or `"stop"`.
|
||||
|
||||
## Tool Result Loop (Tool → Model → Response)
|
||||
|
||||
To continue a conversation after tool calls, send tool results back as messages with `role: "tool"`:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "gpt-5.6-luna",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Weather in Paris?"},
|
||||
{"role": "assistant", "content": null, "tool_calls": [{"id": "call_1", "function": {"name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}}]},
|
||||
{"role": "tool", "tool_call_id": "call_1", "content": "83°F, Overcast"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The gateway converts `role: "tool"` messages to text: `[Tool result #call_1]: 83°F, Overcast`.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
### Duplicate Tool Calls
|
||||
Conol.ai sometimes emits duplicate `tool_call` events in the SSE stream (same name, same arguments, different stage IDs). The gateway currently returns all of them. For production use, deduplicate by `(name, json.dumps(input))` key.
|
||||
|
||||
### Tool + Text in Same Response
|
||||
Conol.ai models may return BOTH text content AND tool calls in the same response. OpenAI convention is `content: null` when tool_calls are present. The gateway currently returns both — downstream clients expecting strict OpenAI protocol may need `content: null` when `tool_calls` is non-empty.
|
||||
|
||||
### Tool Choice
|
||||
`tool_choice: "auto"/"none"/"required"` is passed but conol.ai does not enforce strict tool choice — it always has its built-in tools available. For `tool_choice: "none"`, filter tool calls out in the gateway.
|
||||
|
||||
### Streaming + Tool Calls
|
||||
When streaming with tools, conol.ai sends thinking text first, then tool calls, then final text. The gateway collects all events and emits tool calls first, then text. This causes a delay before the first chunk — the full response must arrive before streaming begins. Future improvement: buffer and emit as events arrive.
|
||||
124
skills/conol-automation/references/dashboard-multi-reg.md
Normal file
124
skills/conol-automation/references/dashboard-multi-reg.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Dashboard + Multi-Reg System
|
||||
|
||||
New components added 2026-07-30 for the Conol pool distribution package.
|
||||
|
||||
## Architecture additions
|
||||
|
||||
```
|
||||
conol_autoreg/
|
||||
├── dashboard_server.py # FastAPI live dashboard (15 KB) — port 9988
|
||||
├── multi_reg.py # Multi-threaded parallel registration (20 KB)
|
||||
├── start_all.bat # Unified launcher with menu (3.3 KB)
|
||||
├── chrome_cdp.bat # Chrome CDP launcher (667 B)
|
||||
├── config.example.json # Template config for distribution (734 B)
|
||||
├── requirements.txt # Python dependencies
|
||||
└── README.md # User-facing guide (3 KB)
|
||||
```
|
||||
|
||||
## Dashboard (port 9988)
|
||||
|
||||
FastAPI server with dark HTML interface, auto-refresh every 3s.
|
||||
|
||||
**API endpoints:**
|
||||
- `GET /api/status` — full pool + gateway status
|
||||
- `GET /api/accounts` — account list
|
||||
- `GET /api/queue` — email queue
|
||||
- `POST /api/queue/add` — add email to queue (JSON body: `{email, password}`)
|
||||
- `POST /api/reload` — signal gateway to reload accounts
|
||||
- `GET /api/queue/clear` — clear queue
|
||||
|
||||
**HTML interface features:**
|
||||
- Stats cards: Alive, Dead, Total Credits, Avg Credits, Reg Today, Gateway
|
||||
- Full account table with credits bars
|
||||
- Email queue section with add form
|
||||
- Action buttons: Refresh, Start Registration, Farm Quests, Reload Gateway
|
||||
|
||||
**Startup:**
|
||||
```bash
|
||||
python dashboard_server.py
|
||||
# → http://127.0.0.1:9988
|
||||
```
|
||||
|
||||
## Multi-Threaded Registration (multi_reg.py)
|
||||
|
||||
Reads `email_queue.jsonl`, registers N accounts per email in parallel.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python multi_reg.py 3 2
|
||||
# 3 = accounts per email
|
||||
# 2 = max parallel threads
|
||||
```
|
||||
|
||||
**Flow per email:**
|
||||
1. Uses Chrome CDP via Playwright (`connect_over_cdp`)
|
||||
2. `asyncio.Semaphore` controls concurrency
|
||||
3. Per account: signup → recaptcha → send verification → IMAP wait → verify → login
|
||||
4. Saves cookies → appends to `conol_accounts_pool.jsonl`
|
||||
5. After registration: auto-farms all 6 quests
|
||||
6. Updates `email_queue.jsonl` status: `queued` → `registering` → `registered` → `quests_done`
|
||||
|
||||
**Gmail verification** runs in `ThreadPoolExecutor` (parallel across clients):
|
||||
```python
|
||||
loop.run_in_executor(None, wait_for_verify_link_sync, gmail_addr, gmail_app, alias, since_epoch)
|
||||
```
|
||||
|
||||
## Unified Launcher (start_all.bat)
|
||||
|
||||
Single `.bat` with 9-option menu:
|
||||
1. Start Gateway (9999)
|
||||
2. Start Dashboard (9988)
|
||||
3. Start BOTH + Chrome CDP
|
||||
4. Start Multi-Reg (from email queue)
|
||||
5. Start ALL (Gateway + Dash + CDP)
|
||||
6. Register N accounts (manual)
|
||||
7. Farm Quests (all accounts)
|
||||
8. Check Status / Health
|
||||
9. Kill All
|
||||
|
||||
Auto-checks: Python installed, deps available, config.json valid, Chrome CDP running, gateway alive.
|
||||
|
||||
## Distribution Package
|
||||
|
||||
For giving to other people:
|
||||
|
||||
1. They copy `conol_autoreg` folder (minus your `config.json`)
|
||||
2. Copy `config.example.json` → `config.json`, fill their gmail + app password
|
||||
3. Install deps: `pip install fastapi uvicorn httpx playwright requests pyyaml`
|
||||
4. Install Chromium: `playwright install chromium`
|
||||
5. Run `start_all.bat`
|
||||
6. Dashboard opens at `http://127.0.0.1:9988`
|
||||
7. Add emails via dashboard form → run multi_reg
|
||||
|
||||
## FastAPI Pitfall: Request Body Parsing
|
||||
|
||||
```python
|
||||
# ❌ WRONG — FastAPI treats `request: dict` as query parameter
|
||||
@app.post("/api/queue/add")
|
||||
async def queue_add(request: dict):
|
||||
email = request.get("email") # → None!
|
||||
|
||||
# ✅ CORRECT — use `from fastapi import Request` + await request.json()
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
|
||||
@app.post("/api/queue/add")
|
||||
async def queue_add(request: Request):
|
||||
body = await request.json()
|
||||
email = body.get("email") # → works!
|
||||
```
|
||||
|
||||
**Reason:** FastAPI only auto-parses JSON body into Pydantic models or when using `Body()`. A bare `dict` type annotation is treated as a query parameter, not a body parameter. The `Request` object's `.json()` method gives direct access.
|
||||
|
||||
## Port Conflict Resolution (Windows)
|
||||
|
||||
When a process dies but leaves port occupied:
|
||||
|
||||
```bash
|
||||
# Find PID on port
|
||||
netstat -ano | grep 9988
|
||||
|
||||
# Kill via PowerShell (taskkill fails in git-bash)
|
||||
powershell -Command "Stop-Process -Id <PID> -Force"
|
||||
```
|
||||
|
||||
Do NOT use `taskkill //PID` in git-bash — syntax fails. Use PowerShell `Stop-Process` instead.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Conol.ai Gateway — OMP Key Matching
|
||||
|
||||
## Problem
|
||||
|
||||
Gateway starts with a random API key but OMP expects a specific one.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
OMP (providers.yml) Gateway (port 9999)
|
||||
├─ apiKey: test → ├─ /health → api_key_prefix
|
||||
└─ baseUrl: ...9999/v1 ├─ /v1/chat/completions → Bearer auth
|
||||
└─ /v1/models → Bearer auth
|
||||
```
|
||||
|
||||
If the keys don't match: OMP gets HTTP 401 → falls back to next provider → "hermes-conol" NEVER works.
|
||||
|
||||
## Two Gateways, Two Key Mechanisms
|
||||
|
||||
### Gateway v3 (conol_github/conol_pool_gateway.py)
|
||||
Uses `ENI_POOL_KEY` env var. Old stable gateway (20+ hour uptime).
|
||||
|
||||
```bash
|
||||
cd C:\Users\User\Desktop\conol_github
|
||||
ENI_POOL_KEY=test /c/Users/User/AppData/Local/Programs/Python/Python311/python.exe conol_pool_gateway.py
|
||||
```
|
||||
|
||||
If `ENI_POOL_KEY` is not set, generates random `sha256(os.urandom(32))[:32]` → OMP will fail.
|
||||
|
||||
### Gateway v4 (conol_autoreg/gateway.py)
|
||||
Reads from `config.json` → `gateway.api_key`. Default: `test`.
|
||||
|
||||
```bash
|
||||
cd C:\Users\User\Desktop\conol_autoreg
|
||||
/c/Users/User/AppData/Local/Programs/Python/Python311/python.exe gateway.py
|
||||
```
|
||||
|
||||
Also respects `ENI_POOL_KEY` and `ENI_POOL_KEY_OVERRIDE` env vars (override priority: ENI_POOL_KEY_OVERRIDE > ENI_POOL_KEY > config.json).
|
||||
|
||||
## How to Verify Match
|
||||
|
||||
```bash
|
||||
# Get gateway's actual key prefix
|
||||
curl -s http://127.0.0.1:9999/health | python -c "import sys,json; print(json.load(sys.stdin)['api_key_prefix'])"
|
||||
|
||||
# Get OMP's expected key
|
||||
python -c "
|
||||
import yaml
|
||||
with open(r'C:\Users\User\.omp\agent\providers.yml') as f:
|
||||
c = yaml.safe_load(f)
|
||||
print(repr(c['providers']['hermes-conol']['apiKey']))
|
||||
"
|
||||
```
|
||||
|
||||
Both should show `test...`.
|
||||
|
||||
## Common Failure Mode
|
||||
|
||||
Gateway was started without `ENI_POOL_KEY` → random key generated (e.g., `bfae749f...`). OMP has `test`. Result: OMP calls return 401, "hermes-conol" provider silently fails, all traffic falls back to echogate.
|
||||
|
||||
## Fix
|
||||
|
||||
1. Kill old gateway process
|
||||
2. Restart with correct key:
|
||||
```bash
|
||||
# For conol_github gateway:
|
||||
ENI_POOL_KEY=test python conol_pool_gateway.py
|
||||
|
||||
# For conol_autoreg gateway:
|
||||
python gateway.py # reads test from config.json
|
||||
```
|
||||
3. OMP picks up immediately — no restart needed
|
||||
50
skills/conol-automation/references/httpx-cookie-pitfall.md
Normal file
50
skills/conol-automation/references/httpx-cookie-pitfall.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# httpx Concurrent Client Cookie Corruption
|
||||
|
||||
## Symptom
|
||||
All 30 parallel requests return HTTP 401, but individual tests succeed.
|
||||
|
||||
## Root Cause
|
||||
Sharing ONE `httpx.AsyncClient` instance across multiple concurrent `asyncio` tasks corrupts cookie handling. httpx reuses connection pools and cookie jars that are not thread/async-safe when multiple tasks set different cookies.
|
||||
|
||||
## Wrong Pattern
|
||||
```python
|
||||
# ❌ Creates one shared client — cookies from different accounts mix up
|
||||
async def main():
|
||||
async with httpx.AsyncClient(timeout=httpx.Timeout(30)) as shared_client:
|
||||
# All 30 concurrent tasks use the SAME client
|
||||
tasks = [test_account(shared_client, acc) for acc in accounts]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
async def test_account(client, acc):
|
||||
# Setting cookies on the shared client
|
||||
client.cookies = {name: value} # Gets overwritten by other tasks!
|
||||
r = await client.post(...)
|
||||
```
|
||||
|
||||
## Correct Pattern
|
||||
```python
|
||||
# ✅ Each concurrent task creates its OWN client
|
||||
async def test_account(acc):
|
||||
cookies = load_cookies(acc['cookies_path'])
|
||||
async with httpx.AsyncClient(cookies=cookies, timeout=httpx.Timeout(30)) as client:
|
||||
r = await client.post(...)
|
||||
|
||||
async def main():
|
||||
tasks = [test_account(acc) for acc in accounts]
|
||||
await asyncio.gather(*tasks)
|
||||
```
|
||||
|
||||
## Concurrency Control
|
||||
Use `asyncio.Semaphore(N)` to limit concurrent connections:
|
||||
```python
|
||||
sem = asyncio.Semaphore(3)
|
||||
|
||||
async def bounded(acc):
|
||||
async with sem:
|
||||
return await test_account(acc)
|
||||
|
||||
results = await asyncio.gather(*[bounded(a) for a in accounts])
|
||||
```
|
||||
|
||||
## Detection
|
||||
If you see HTTP 401 on all requests in parallel but HTTP 201 on individual sequential requests, this is almost certainly the shared client issue.
|
||||
72
skills/conol-automation/references/model-downgrades.md
Normal file
72
skills/conol-automation/references/model-downgrades.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Conol.ai Model Downgrade Behavior
|
||||
|
||||
## Discovery Session: 2026-07-30
|
||||
|
||||
After a ~4-hour outage (all models returning HTTP 500 `"Failed to create session"`), Conol.ai backend recovered but with GPT-5.5 unavailable. Sessions that requested `gpt-5.5` were silently downgraded.
|
||||
|
||||
### Raw Observations
|
||||
|
||||
**Payload sent:**
|
||||
```json
|
||||
{
|
||||
"source": {"type": "home"},
|
||||
"messages": [{"type": "text", "content": "Say OK"}],
|
||||
"timezone": "Europe/Kyiv",
|
||||
"agentModel": "gpt-5.5",
|
||||
"agentEffort": "low"
|
||||
}
|
||||
```
|
||||
|
||||
**Session created (HTTP 201):**
|
||||
```json
|
||||
{
|
||||
"sessionId": "j0c8y5kosm47bd1nsf2zlmrl",
|
||||
"modelDowngraded": true,
|
||||
"effectiveModel": "claude-haiku-4-5"
|
||||
}
|
||||
```
|
||||
|
||||
**Full chat response:** Model still answered "OK" correctly — the downgrade is transparent to the caller.
|
||||
|
||||
### Test Matrix (6 payload variants, all HTTP 201)
|
||||
|
||||
| Variant | Status | modelDowngraded | effectiveModel |
|
||||
|---------|--------|-----------------|----------------|
|
||||
| full (tz + effort) | 201 | true | claude-haiku-4-5 |
|
||||
| no timezone | 201 | true | claude-haiku-4-5 |
|
||||
| bare (model only) | 201 | true | claude-haiku-4-5 |
|
||||
| no model specified | 201 | true | claude-haiku-4-5 |
|
||||
| no effort | 201 | true | claude-haiku-4-5 |
|
||||
| source: direct | 201 | true | claude-haiku-4-5 |
|
||||
|
||||
**Key insight:** Even when NO model is specified (bare `source + messages`), Conol.ai downgrades to haiku. This means the downgrade is based on backend capacity, not the specific model requested.
|
||||
|
||||
### Gateway Transparency
|
||||
|
||||
The gateway (`gateway.py`) does NOT expose `modelDowngraded` or `effectiveModel` to the caller. The `/v1/chat/completions` response shows the original model name:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "gpt-5.5",
|
||||
"choices": [{"message": {"content": "OK"}}]
|
||||
}
|
||||
```
|
||||
|
||||
The caller sees `model: "gpt-5.5"` even though haiku generated the response. This is acceptable — the alternative (returning actual model) would break downstream OMP provider matching.
|
||||
|
||||
### Distinguishing Downgrade from Outage
|
||||
|
||||
| Signal | Downgrade | Outage |
|
||||
|--------|-----------|--------|
|
||||
| HTTP status | 201 | 500 |
|
||||
| `sessionId` | present | absent |
|
||||
| `modelDowngraded` | `true` | N/A (no response) |
|
||||
| `effectiveModel` | fallback model | N/A |
|
||||
| Chat works | ✅ Yes | ❌ No |
|
||||
| Action needed | None (transparent) | Wait + daemon auto-recovers |
|
||||
|
||||
### When This Matters
|
||||
|
||||
- **Don't panic on `modelDowngraded: true`.** It means the system works, just with a cheaper model.
|
||||
- **Check `modelDowngraded` before assuming server-down.** If you test one account, get 201, and see `modelDowngraded: true` — the server is alive, only the model is downgraded.
|
||||
- **The daemon correctly ignores downgrades.** It only triggers `server_down` on HTTP 500, not on `modelDowngraded`.
|
||||
@@ -0,0 +1,51 @@
|
||||
# Conol.ai reCAPTCHA v3 Block — 2026-07-30
|
||||
|
||||
## Status: BLOCKED
|
||||
|
||||
conol.ai has stopped accepting reCAPTCHA v3 tokens for the `/api/invites/register` endpoint. All registration attempts through automated means (real Chrome CDP, fresh tokens, multiple actions) fail with `CAPTCHA_VERIFICATION_FAILED` (HTTP 403).
|
||||
|
||||
## Diagnostic steps taken
|
||||
|
||||
### 1. Token freshness test
|
||||
- Generated 5+ fresh tokens via `grecaptcha.execute(siteKey, {action})` in real Chrome CDP
|
||||
- All returned `CAPTCHA_VERIFICATION_FAILED`
|
||||
- Site key: `6Lc3wmAtAAAAAB9YBPXQtT9uGGsH3ul6LQBc5AUu`
|
||||
|
||||
### 2. Action variation test
|
||||
Tested actions: `sign_up`, `register`, `submit`, `signup`, `login`, `homepage`, `verify`, empty string
|
||||
All failed identically with HTTP 403.
|
||||
|
||||
### 3. Fetch context test
|
||||
Tested `fetch()` call from browser `page.evaluate()` context (real browser headers, cookies, Origin/Referer) — still 403.
|
||||
|
||||
### 4. Header test
|
||||
- Without `x-captcha-response` header → 403
|
||||
- With `x-captcha-response` header (fresh token) → 403
|
||||
- With `x-turnstile-response` header → 403 (wrong captcha type)
|
||||
|
||||
### 5. JS bundle inspection
|
||||
- Searched ALL Next.js chunks for captcha references
|
||||
- Found no Turnstile, no hCaptcha, no client-side captcha at all
|
||||
- reCAPTCHA v3 script loads in body but is invisible (no user interaction)
|
||||
- New endpoint `/api/auth/register` found in JS bundles → returns 404 (not yet deployed)
|
||||
|
||||
### 6. Browser form test
|
||||
- Filled signup form via real Chrome
|
||||
- Form submit blocked by Next.js client-side validation
|
||||
- `button[type="submit"]` disabled until checkbox checked
|
||||
- Even with checkbox checked, no API calls triggered
|
||||
- React fiber traversal (`__reactFiber` onClick) failed to trigger submission
|
||||
|
||||
## Root cause
|
||||
Server-side anti-bot detection, NOT token quality. conol.ai backend rejects registrations based on fingerprint signals (IP reputation, request pattern, timing) regardless of captcha token validity.
|
||||
|
||||
## Current workaround status
|
||||
**None working.** The 31 existing accounts in the pool (~24,800 credits) are functional for chat/API use through the gateway. New registrations require:
|
||||
1. Manual browser registration (human solves whatever anti-bot conol.ai uses)
|
||||
2. Google OAuth via signup page button
|
||||
3. Wait for conol.ai to deploy their new `/api/auth/register` endpoint
|
||||
|
||||
## Impact on codebase
|
||||
- `multi_reg.py v2.0` — properly reports `CAPTCHA_VERIFICATION_FAILED` with full error context
|
||||
- `eni_conol.py reg N` — also fails with clear error messages
|
||||
- Gateway remains fully operational with existing accounts
|
||||
82
skills/conol-automation/references/recaptcha-cdp.md
Normal file
82
skills/conol-automation/references/recaptcha-cdp.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Native reCAPTCHA v3 via Chrome CDP
|
||||
|
||||
Conol.ai uses reCAPTCHA v3 (site key: `6Lc3wmAtAAAAAB9YBPXQtT9uGGsH3ul6LQBc5AUu`).
|
||||
The captcha is **domain-locked** — external solvers (2Captcha, AntiCaptcha, CapSolver) do NOT work.
|
||||
|
||||
## Solution: Chrome CDP Native Execution
|
||||
|
||||
The browser already has the reCAPTCHA script loaded. Execute `grecaptcha.execute()` directly in the page context.
|
||||
|
||||
### Generic reCAPTCHA executor (playwright)
|
||||
|
||||
```python
|
||||
async def recaptcha(page, action: str) -> str:
|
||||
"""Execute reCAPTCHA v3 and return token."""
|
||||
return await page.evaluate(
|
||||
"""async ({siteKey, action}) => {
|
||||
// Ensure grecaptcha is loaded
|
||||
if (!window.grecaptcha) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const old = document.getElementById('eni-recaptcha');
|
||||
if (old) old.remove();
|
||||
const script = document.createElement('script');
|
||||
script.id = 'eni-recaptcha';
|
||||
script.src = `https://www.recaptcha.net/recaptcha/api.js?render=${encodeURIComponent(siteKey)}`;
|
||||
script.onload = resolve;
|
||||
script.onerror = () => reject(new Error('recaptcha load failed'));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
await new Promise((resolve) => window.grecaptcha.ready(resolve));
|
||||
return await window.grecaptcha.execute(siteKey, {action});
|
||||
}""",
|
||||
{"siteKey": SITE_KEY, "action": action},
|
||||
)
|
||||
```
|
||||
|
||||
### Usage in registration flow
|
||||
|
||||
```python
|
||||
# Step 1: Sign Up
|
||||
signup_token = await recaptcha(page, "sign_up")
|
||||
r = await json_fetch(page, "/api/invites/register", method="POST",
|
||||
captcha=signup_token, body={...})
|
||||
|
||||
# Step 2: Send Verification Email
|
||||
verify_token = await recaptcha(page, "send_verification_email")
|
||||
r = await json_fetch(page, "/api/auth/send-verification-email", method="POST",
|
||||
captcha=verify_token, body={...})
|
||||
|
||||
# Step 3: Sign In
|
||||
signin_token = await recaptcha(page, "sign_in")
|
||||
r = await json_fetch(page, "/api/auth/sign-in/email", method="POST",
|
||||
captcha=signin_token, body={...})
|
||||
```
|
||||
|
||||
### JSON fetch helper (via page.evaluate)
|
||||
|
||||
```python
|
||||
async def json_fetch(page, path: str, *, method="GET", body=None, captcha=None):
|
||||
"""Fetch API endpoint from page context (uses browser's cookies)."""
|
||||
return await page.evaluate(
|
||||
"""async ({path, method, body, captcha}) => {
|
||||
const headers = {Accept: 'application/json'};
|
||||
if (body !== null) headers['Content-Type'] = 'application/json';
|
||||
if (captcha) headers['x-captcha-response'] = captcha;
|
||||
const r = await fetch(path, {method, headers, credentials: 'include',
|
||||
body: body === null ? undefined : JSON.stringify(body)});
|
||||
const text = await r.text();
|
||||
let data = null;
|
||||
try { data = JSON.parse(text); } catch (_) {}
|
||||
return {status: r.status, url: r.url, text: text.slice(0, 2000), data};
|
||||
}""",
|
||||
{"path": path, "method": method, "body": body, "captcha": captcha},
|
||||
)
|
||||
```
|
||||
|
||||
## Key Points
|
||||
|
||||
- Captcha token is sent via `x-captcha-response` header (NOT in body)
|
||||
- Use `page.evaluate` to call browser APIs within the origin context
|
||||
- Always clear cookies/storage between registrations to avoid session conflicts
|
||||
- Increasing delays between accounts (8s + 3s per index) reduces HTTP 429 errors
|
||||
104
skills/conol-automation/references/sse-protocol.md
Normal file
104
skills/conol-automation/references/sse-protocol.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Conol.ai SSE Protocol (Agent Sessions)
|
||||
|
||||
Conol.ai uses a custom SSE protocol over `/api/sessions`. This is NOT standard OpenAI SSE.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### 1. Create Session
|
||||
```
|
||||
POST https://conol.ai/api/sessions
|
||||
Content-Type: application/json
|
||||
Cookies: __Secure-better-auth.session_token=<token>
|
||||
__Secure-better-auth.better-auth-passkey=<key>
|
||||
|
||||
Body: {
|
||||
"source": {"type": "home"},
|
||||
"messages": [{"type": "text", "content": "user prompt"}],
|
||||
"timezone": "Europe/Kyiv",
|
||||
"agentModel": "gpt-5.5",
|
||||
"agentEffort": "low"
|
||||
}
|
||||
|
||||
Response: 201 Created
|
||||
{
|
||||
"sessionId": "dh2btg2juiqtjfuv5m02by2v"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Stream SSE Messages
|
||||
```
|
||||
GET https://conol.ai/api/sessions/{sessionId}/messages?logDeltas=1
|
||||
```
|
||||
|
||||
## SSE Event Types
|
||||
|
||||
### `history_delta` — Main content event
|
||||
This is the PRIMARY event type. Content is nested: `stages[] → logs[] → content[] → {type: "text", text: "..."}`
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "history_delta",
|
||||
"runtimeVersion": "orcai",
|
||||
"stages": [{
|
||||
"id": "hjbl6q...",
|
||||
"logs": [{
|
||||
"role": "assistant",
|
||||
"type": "thinking",
|
||||
"content": [{"type": "text", "text": "The user is asking..."}]
|
||||
}],
|
||||
"preview": [{
|
||||
"role": "assistant",
|
||||
"type": "thinking",
|
||||
"content": [{"text": "..."}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### `done` — End of stream
|
||||
```json
|
||||
{"type": "done"}
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### ❌ Looking for `text-delta` events
|
||||
Conol.ai does NOT emit `text-delta`. All content comes via `history_delta`.
|
||||
|
||||
### ❌ Direct `POST /api/chat`
|
||||
There is NO `/api/chat` endpoint. The only way to interact is through `/api/sessions`.
|
||||
|
||||
### ❌ Parsing only top-level content
|
||||
Content is always nested: `stages[].logs[].content[]`. Must iterate through all levels.
|
||||
|
||||
## Correct Parser (Python)
|
||||
|
||||
```python
|
||||
async with client.stream("GET", f"https://conol.ai/api/sessions/{sid}/messages?logDeltas=1") as stream:
|
||||
async for line in stream.aiter_lines():
|
||||
if not line or not line.startswith("data:"):
|
||||
continue
|
||||
ev = json.loads(line[5:].strip())
|
||||
|
||||
if ev.get("type") == "done":
|
||||
break
|
||||
|
||||
if ev.get("type") == "history_delta":
|
||||
for stage in ev.get("stages", []):
|
||||
for log in stage.get("logs", []):
|
||||
content = log.get("content", [])
|
||||
if isinstance(content, list):
|
||||
for part in content:
|
||||
if isinstance(part, dict) and part.get("type") == "text":
|
||||
full_text = part.get("text", full_text)
|
||||
```
|
||||
|
||||
## Cookies
|
||||
|
||||
Conol.ai uses `better-auth` (not next-auth):
|
||||
- `__Secure-better-auth.session_token` — session token
|
||||
- `__Secure-better-auth.better-auth-passkey` — passkey
|
||||
|
||||
Both are `Secure`, `HttpOnly`, `SameSite=Lax`, domain `.conol.ai`.
|
||||
|
||||
Cookies expire and return HTTP 401 — re-login via Chrome CDP.
|
||||
Reference in New Issue
Block a user