Loading…
Loading…
An autonomous operations agent that ingests signals, triages work, drafts communications, generates specs, implements code, and evolves itself, managing 6 products for a single engineering leader.
A pipeline-based system where raw signals flow through classification, specification, and implementation. An AI brain orchestrates decisions at every stage.
Gmail, Calendar, Drive, Google Chat, Jira, Kayako, Read.AI → unified Signal model
Cursor CLI agents + Anthropic Claude API fallback + 5-tier model router
Google Chat bot, Web UI, email drafts, GitHub PRs, notifications
Gmail, Calendar, Drive, Google Chat, Jira, Kayako, Read.AI → unified Signal model
Cursor CLI agents + Anthropic Claude API fallback + 5-tier model router
Google Chat bot, Web UI, email drafts, GitHub PRs, notifications
Six autonomous systems working in concert, from raw signal ingestion through to shipped code and published content.
Monitors 7 sources, classifies signals into actionable work with urgency scoring.
Converts approved work into technical specs, then autonomously implements via Cursor CLI.
Generates voice-calibrated email drafts and clarification requests.
Prepares contextual briefs and enriches them post-meeting with Read.AI transcripts.
Drafts social content from inspiration sources with engagement-based self-revision.
Multi-turn chat with 24 intents, entity tracking, and multi-modal vision support.
Ciel learns from corrections, recalibrates its behavior, and codifies patterns directly into its own source code.
Follow a single Jira ticket from arrival to shipped code. Every step fully autonomous.
A 5-tier model router ensures every LLM call uses the cheapest model that can do the job, from nano-tier intent parsing to code-tier implementations.
A nervous system of background processes: ingesting signals, classifying work, generating specs, and maintaining the correlation graph around the clock.
Production-grade patterns built for reliability, observability, and autonomous operation.
The daily AI Top 10, plus deep dives. A curated ranking of the signals that matter, with takeaways for product teams and engineers.
Mikko corrects a classification or provides a new rule. Ciel stores it with scope and pattern.
Non-codified rules stay in classifier, product_mapping, and summarization prompts (including codify_pending while a PR is open). promptInfluenceCount tracks classifier injections.
Brain daemon analyzes 30-day override patterns. Adjusts voice model, goal priorities, and rhythm baselines.
At maturity, self-evolve enqueues a planned EU; ciel-evolution-controller opens the PR. T2 scopes await human merge; promoted scopes (e.g. product_mapping T4) auto-merge when gates pass. Rules enter codify_pending at pr_open until merge.
A Jira ticket update hits ciel-monitor during its 5-minute sweep across 7 integrated sources.
source: jira type: ticket_update product: CRM Pad priority: medium
ciel-triage classifies the signal as spec_ready with urgency: review, injecting 3 learned rules into the LLM prompt.
status: spec_ready urgency: review partner: Acme Corp (resolved via domain rule) learned_rules_injected: 3
ciel-specs produces a technical implementation plan with problem statement, approach, file targets, and test strategy.
buildMode: auto sections: [problem, approach, files, tests] model: composer-2-fast estimated_files: 3
Every state write checks version, retries on conflict with linear backoff.
async function setCielState<K>(key: K, value: V) {
for (let attempt = 0; attempt < 3; attempt++) {
const current = await prisma.cielState.findUnique({
where: { key },
});
const updated = await prisma.cielState.upsert({
where: { key, version: current?.version ?? 0 },
update: { valueJson: value, version: { increment: 1 } },
create: { key, valueJson: value, version: 1 },
});
if (updated) return updated;
await sleep(10 * (attempt + 1));
}
}Maps each of 24 intents to the cheapest model tier capable of handling it.
const TIER_DEFAULTS = {
skip: { model: 'gpt-5.4-nano-none', timeout: 10_000 },
nano: { model: 'gpt-5.4-nano-none', timeout: 10_000 },
fast: { model: 'composer-2.5-fast', timeout: 30_000 },
mid: { model: 'claude-4.6-sonnet-medium', timeout: 120_000 },
power:{ model: 'claude-4.6-sonnet-medium', timeout: 120_000 },
code: { model: 'claude-4.6-sonnet-medium', timeout: 300_000 },
};
function routeIntent(intent: string): RouteConfig {
return {
...TIER_DEFAULTS[INTENT_TIER_MAP[intent]],
lane: isInteractive(intent) ? 'interactive' : 'background',
workspace: INTENT_WORKSPACE_MAP[intent],
};
}Ensures interactive chat stays responsive while daemons process in the background.
const LANES = {
interactive: { max: 2, queue: [] },
background: { max: 1, queue: [] },
};
async function enqueue(lane: Lane, task: AgentTask) {
if (running[lane] < LANES[lane].max) {
running[lane]++;
try { return await task(); }
finally { running[lane]--; drain(lane); }
}
return new Promise((resolve) => {
LANES[lane].queue.push(() => resolve(task()));
});
}Typed edges between any two entities with confidence scoring and temporal decay.
interface EntityCorrelation {
sourceType: EntityType;
sourceId: string;
targetType: EntityType;
targetId: string;
relationship: RelationType;
confidence: number; // 0-1, decays over time
decayScore: number; // exponential decay (0.99^days)
lastVerified: Date;
}
type EntityType =
| 'signal' | 'triage_item' | 'partner'
| 'meeting' | 'spec' | 'implementation'
| 'idea' | 'kb_entry' | 'product';Return 200 immediately, process in background. Prevents platform retry storms.
export async function POST(req: Request) {
const body = await req.json();
after(async () => {
await processCielMessage(body.text, {
platform: 'gchat',
images: attachedImages,
});
});
return NextResponse.json({ ok: true });
}Graduated response from retry → backoff → stop with GChat alerts at each stage.
async function runDaemonLoop(opts: DaemonLoopOptions) {
let consecutiveErrors = 0;
while (true) {
try {
await opts.cycle();
consecutiveErrors = 0;
if (cycleCount % 5 === 0) writeHeartbeat('healthy');
} catch (err) {
consecutiveErrors++;
if (consecutiveErrors <= 9) alertDaemonError(err);
if (consecutiveErrors === 10) prisma.$disconnect();
if (consecutiveErrors >= 50) intervalMs = 300_000;
if (consecutiveErrors >= 200) {
writeHeartbeat('stopped');
return;
}
}
await sleep(intervalMs);
}
}