autoreg-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok60915d52e8425
2026-10-20 02:16:32 +03:00
commit f58eafeb48
565 changed files with 60064 additions and 0 deletions

View 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` |

View 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.

View 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.

View 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.

View File

@@ -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

View 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.

View 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`.

View File

@@ -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

View 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

View 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.