@stackbilt/aegis-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +96 -0
- package/schema.sql +586 -0
- package/src/adapters/voice/cloudflare-agent.ts +34 -0
- package/src/auth.ts +124 -0
- package/src/bluesky.ts +464 -0
- package/src/claude-tools/content.ts +188 -0
- package/src/claude-tools/email.ts +69 -0
- package/src/claude-tools/github.ts +440 -0
- package/src/claude-tools/goals.ts +116 -0
- package/src/claude-tools/index.ts +353 -0
- package/src/claude-tools/web.ts +59 -0
- package/src/claude.ts +406 -0
- package/src/codebeast.ts +200 -0
- package/src/composite.ts +715 -0
- package/src/content/column.ts +80 -0
- package/src/content/hero-image.ts +47 -0
- package/src/content/index.ts +27 -0
- package/src/content/journal.ts +91 -0
- package/src/content/roundtable.ts +163 -0
- package/src/core.ts +309 -0
- package/src/dashboard.ts +620 -0
- package/src/decision-docs.ts +284 -0
- package/src/dispatch.ts +13 -0
- package/src/edge-env.ts +58 -0
- package/src/email.ts +850 -0
- package/src/exports.ts +156 -0
- package/src/github-projects.ts +312 -0
- package/src/github.ts +670 -0
- package/src/groq.ts +247 -0
- package/src/health-page.ts +578 -0
- package/src/index.ts +89 -0
- package/src/kernel/argus-actions.ts +397 -0
- package/src/kernel/argus-correlation.ts +639 -0
- package/src/kernel/board.ts +91 -0
- package/src/kernel/briefing.ts +177 -0
- package/src/kernel/classify-memory-topic.ts +166 -0
- package/src/kernel/cognition.ts +377 -0
- package/src/kernel/court-cards.ts +163 -0
- package/src/kernel/dispatch.ts +587 -0
- package/src/kernel/domain.ts +50 -0
- package/src/kernel/dynamic-tools.ts +322 -0
- package/src/kernel/executor-port.ts +45 -0
- package/src/kernel/executors/claude.ts +73 -0
- package/src/kernel/executors/direct.ts +237 -0
- package/src/kernel/executors/groq.ts +18 -0
- package/src/kernel/executors/index.ts +87 -0
- package/src/kernel/executors/tarotscript.ts +104 -0
- package/src/kernel/executors/workers-ai.ts +54 -0
- package/src/kernel/insight-cache.ts +76 -0
- package/src/kernel/memory/agenda.ts +200 -0
- package/src/kernel/memory/blocks.ts +188 -0
- package/src/kernel/memory/consolidation.ts +194 -0
- package/src/kernel/memory/episodic.ts +241 -0
- package/src/kernel/memory/goals.ts +156 -0
- package/src/kernel/memory/graph.ts +290 -0
- package/src/kernel/memory/index.ts +11 -0
- package/src/kernel/memory/insights.ts +316 -0
- package/src/kernel/memory/procedural.ts +467 -0
- package/src/kernel/memory/pruning.ts +67 -0
- package/src/kernel/memory/recall.ts +367 -0
- package/src/kernel/memory/semantic.ts +315 -0
- package/src/kernel/memory/synthesis.ts +161 -0
- package/src/kernel/memory-adapter.ts +369 -0
- package/src/kernel/memory-guardrails.ts +76 -0
- package/src/kernel/port.ts +23 -0
- package/src/kernel/resilience.ts +322 -0
- package/src/kernel/router.ts +471 -0
- package/src/kernel/scheduled/agent-dispatch.ts +252 -0
- package/src/kernel/scheduled/argus-analytics.ts +247 -0
- package/src/kernel/scheduled/argus-heartbeat.ts +320 -0
- package/src/kernel/scheduled/argus-notify.ts +348 -0
- package/src/kernel/scheduled/board-sync.ts +110 -0
- package/src/kernel/scheduled/ci-watcher.ts +125 -0
- package/src/kernel/scheduled/cognitive-metrics.ts +377 -0
- package/src/kernel/scheduled/consolidation.ts +229 -0
- package/src/kernel/scheduled/content-drip.ts +47 -0
- package/src/kernel/scheduled/content.ts +6 -0
- package/src/kernel/scheduled/conversation-facts.ts +204 -0
- package/src/kernel/scheduled/cost-report.ts +84 -0
- package/src/kernel/scheduled/curiosity.ts +219 -0
- package/src/kernel/scheduled/dev-activity.ts +44 -0
- package/src/kernel/scheduled/digest.ts +317 -0
- package/src/kernel/scheduled/dreaming/agenda-triage.ts +115 -0
- package/src/kernel/scheduled/dreaming/facts.ts +239 -0
- package/src/kernel/scheduled/dreaming/index.ts +8 -0
- package/src/kernel/scheduled/dreaming/llm.ts +33 -0
- package/src/kernel/scheduled/dreaming/pattern-synthesis.ts +124 -0
- package/src/kernel/scheduled/dreaming/persona.ts +75 -0
- package/src/kernel/scheduled/dreaming/symbolic.ts +31 -0
- package/src/kernel/scheduled/dreaming/task-proposals.ts +80 -0
- package/src/kernel/scheduled/dreaming.ts +66 -0
- package/src/kernel/scheduled/entropy.ts +149 -0
- package/src/kernel/scheduled/escalation.ts +192 -0
- package/src/kernel/scheduled/feed-watcher.ts +206 -0
- package/src/kernel/scheduled/goals.ts +214 -0
- package/src/kernel/scheduled/governance.ts +41 -0
- package/src/kernel/scheduled/heartbeat.ts +220 -0
- package/src/kernel/scheduled/inbox-processor.ts +174 -0
- package/src/kernel/scheduled/index.ts +245 -0
- package/src/kernel/scheduled/issue-proposer.ts +478 -0
- package/src/kernel/scheduled/issue-watcher.ts +128 -0
- package/src/kernel/scheduled/pr-automerge.ts +213 -0
- package/src/kernel/scheduled/product-health.ts +107 -0
- package/src/kernel/scheduled/reflection.ts +373 -0
- package/src/kernel/scheduled/self-improvement.ts +114 -0
- package/src/kernel/scheduled/social-engage.ts +175 -0
- package/src/kernel/scheduled/task-audit.ts +60 -0
- package/src/kernel/symbolic.ts +156 -0
- package/src/kernel/types.ts +145 -0
- package/src/landing.ts +1190 -0
- package/src/lib/audit-chain/chain.ts +28 -0
- package/src/lib/audit-chain/types.ts +12 -0
- package/src/lib/observability/errors.ts +55 -0
- package/src/markdown.ts +164 -0
- package/src/mcp/handlers.ts +647 -0
- package/src/mcp/server.ts +184 -0
- package/src/mcp/tools.ts +316 -0
- package/src/mcp-client.ts +275 -0
- package/src/mcp-server.ts +2 -0
- package/src/operator/config.example.ts +60 -0
- package/src/operator/config.ts +60 -0
- package/src/operator/index.ts +46 -0
- package/src/operator/persona.example.ts +34 -0
- package/src/operator/persona.ts +34 -0
- package/src/operator/prompt-builder.ts +190 -0
- package/src/operator/types.ts +43 -0
- package/src/pulse.ts +1179 -0
- package/src/routes/bluesky.ts +116 -0
- package/src/routes/cc-tasks.ts +328 -0
- package/src/routes/codebeast.ts +1 -0
- package/src/routes/content.ts +194 -0
- package/src/routes/conversations.ts +25 -0
- package/src/routes/dynamic-tools.ts +111 -0
- package/src/routes/feedback.ts +192 -0
- package/src/routes/health.ts +147 -0
- package/src/routes/messages.ts +228 -0
- package/src/routes/observability.ts +82 -0
- package/src/routes/operator-logs.ts +42 -0
- package/src/routes/pages.ts +96 -0
- package/src/routes/sessions.ts +54 -0
- package/src/sanitize.ts +73 -0
- package/src/schema-enums.ts +155 -0
- package/src/search.ts +112 -0
- package/src/task-intelligence.ts +497 -0
- package/src/types.ts +194 -0
- package/src/ui.ts +5 -0
- package/src/version.ts +3 -0
- package/src/workers-ai-chat.ts +333 -0
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
import { route } from './router.js';
|
|
2
|
+
import { recordEpisode, retrogradeEpisode, upsertProcedure, addRefinement, degradeProcedure, getProcedure, procedureKey } from './memory/index.js';
|
|
3
|
+
import { searchMemoryByKeywords, getAllMemoryForContext } from './memory-adapter.js';
|
|
4
|
+
import { generateBriefing, formatBriefing } from './briefing.js';
|
|
5
|
+
import { fetchRelevantInsights } from './insight-cache.js';
|
|
6
|
+
import { probeConsistency } from '../groq.js';
|
|
7
|
+
import { executeComposite } from '../composite.js';
|
|
8
|
+
import { buildGroqSystemPrompt } from '../operator/prompt-builder.js';
|
|
9
|
+
import type { KernelIntent, DispatchResult, Executor } from './types.js';
|
|
10
|
+
import {
|
|
11
|
+
executeClaude,
|
|
12
|
+
executeClaudeOpus,
|
|
13
|
+
executeClaudeStream,
|
|
14
|
+
executeGroq,
|
|
15
|
+
executeWorkersAi,
|
|
16
|
+
executeGptOss,
|
|
17
|
+
executeDirect,
|
|
18
|
+
executeCodeTask,
|
|
19
|
+
executeWithAnthropicFailover,
|
|
20
|
+
executeTarotScript,
|
|
21
|
+
buildMcpRegistry,
|
|
22
|
+
} from './executors/index.js';
|
|
23
|
+
// ─── Edge Environment ────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export interface EdgeEnv {
|
|
26
|
+
db: D1Database;
|
|
27
|
+
anthropicApiKey: string;
|
|
28
|
+
claudeModel: string;
|
|
29
|
+
opusModel: string;
|
|
30
|
+
gptOssModel: string;
|
|
31
|
+
groqApiKey: string;
|
|
32
|
+
groqModel: string;
|
|
33
|
+
groqResponseModel: string;
|
|
34
|
+
groqGptOssModel: string;
|
|
35
|
+
bizopsFetcher: Fetcher;
|
|
36
|
+
bizopsToken: string;
|
|
37
|
+
resendApiKey: string;
|
|
38
|
+
resendApiKeyPersonal: string;
|
|
39
|
+
githubToken: string;
|
|
40
|
+
githubRepo: string;
|
|
41
|
+
braveApiKey: string;
|
|
42
|
+
notifyEmail: string;
|
|
43
|
+
baseUrl: string;
|
|
44
|
+
ai?: Ai;
|
|
45
|
+
aiGatewayId?: string;
|
|
46
|
+
anthropicBaseUrl: string;
|
|
47
|
+
groqBaseUrl: string;
|
|
48
|
+
ctx?: ExecutionContext; // optional — used to background long-running tasks
|
|
49
|
+
roundtableDb?: D1Database;
|
|
50
|
+
cfAnalyticsToken?: string;
|
|
51
|
+
imgForgeFetcher?: Fetcher;
|
|
52
|
+
imgForgeSbSecret?: string;
|
|
53
|
+
memoryBinding?: import('../types.js').MemoryServiceBinding;
|
|
54
|
+
tarotscriptFetcher?: Fetcher;
|
|
55
|
+
maraFetcher?: Fetcher;
|
|
56
|
+
maraToken?: string;
|
|
57
|
+
codebeastFetcher?: Fetcher;
|
|
58
|
+
mindspringFetcher?: Fetcher;
|
|
59
|
+
mindspringToken?: string;
|
|
60
|
+
devtoApiKey?: string;
|
|
61
|
+
gaCredentials?: string;
|
|
62
|
+
blueskyHandle?: string;
|
|
63
|
+
blueskyAppPassword?: string;
|
|
64
|
+
authBinding?: import('../types.js').AuthServiceBinding;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── Intent Construction ─────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
export function createIntent(
|
|
70
|
+
threadId: string,
|
|
71
|
+
text: string,
|
|
72
|
+
overrides?: Partial<KernelIntent>,
|
|
73
|
+
): KernelIntent {
|
|
74
|
+
return {
|
|
75
|
+
source: { channel: 'web', threadId },
|
|
76
|
+
raw: text,
|
|
77
|
+
timestamp: Date.now(),
|
|
78
|
+
costCeiling: 'expensive',
|
|
79
|
+
...overrides,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ─── Self-consistency probe (#51) ────────────────────────────
|
|
84
|
+
// Before executing via workers_ai or gpt_oss for no-tool queries,
|
|
85
|
+
// fire 3 parallel Groq 8B calls. If all agree, skip the expensive executor.
|
|
86
|
+
|
|
87
|
+
const PROBE_SKIP_CLASSIFICATIONS = new Set([
|
|
88
|
+
'heartbeat', 'greeting', 'code_task', 'self_improvement', 'goal_execution',
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
function probeEligible(
|
|
92
|
+
executor: string,
|
|
93
|
+
intent: KernelIntent,
|
|
94
|
+
classification: string,
|
|
95
|
+
): boolean {
|
|
96
|
+
if (intent.needsTools) return false;
|
|
97
|
+
if (intent.source.channel === 'internal') return false;
|
|
98
|
+
if (PROBE_SKIP_CLASSIFICATIONS.has(classification)) return false;
|
|
99
|
+
return executor === 'workers_ai' || executor === 'gpt_oss';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── Shared post-execution bookkeeping ───────────────────────
|
|
103
|
+
|
|
104
|
+
async function recordOutcome(
|
|
105
|
+
env: EdgeEnv,
|
|
106
|
+
intent: KernelIntent,
|
|
107
|
+
classification: string,
|
|
108
|
+
procKey: string,
|
|
109
|
+
plan: Awaited<ReturnType<typeof route>>['plan'],
|
|
110
|
+
existingProcedure: Awaited<ReturnType<typeof getProcedure>>,
|
|
111
|
+
text: string,
|
|
112
|
+
cost: number,
|
|
113
|
+
latencyMs: number,
|
|
114
|
+
nearMiss: string | null | undefined,
|
|
115
|
+
outcome: 'success' | 'failure' | 'partial_failure',
|
|
116
|
+
reclassified?: boolean,
|
|
117
|
+
): Promise<void> {
|
|
118
|
+
await recordEpisode(env.db, {
|
|
119
|
+
intent_class: classification,
|
|
120
|
+
channel: intent.source.channel,
|
|
121
|
+
summary: `${classification} via ${plan.executor}${intent.classifierSource ? ` [clf:${intent.classifierSource}]` : ''}${intent.domain ? ` [domain:${intent.domain}/${(intent.domainConfidence ?? 0).toFixed(2)}]` : ''}: ${text.slice(0, 500)}`,
|
|
122
|
+
outcome: outcome === 'partial_failure' ? 'failure' : outcome,
|
|
123
|
+
cost,
|
|
124
|
+
latency_ms: latencyMs,
|
|
125
|
+
near_miss: nearMiss,
|
|
126
|
+
classifier_confidence: intent.confidence,
|
|
127
|
+
reclassified,
|
|
128
|
+
thread_id: intent.source.threadId,
|
|
129
|
+
executor: plan.executor,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
await upsertProcedure(env.db, procKey, plan.executor, JSON.stringify({ executor: plan.executor }), outcome, latencyMs, cost);
|
|
133
|
+
|
|
134
|
+
const isReplan = existingProcedure
|
|
135
|
+
&& (existingProcedure.status === 'degraded' || existingProcedure.status === 'broken')
|
|
136
|
+
&& !plan.procedureId;
|
|
137
|
+
|
|
138
|
+
if (isReplan && outcome === 'success' && existingProcedure) {
|
|
139
|
+
await addRefinement(env.db, procKey, {
|
|
140
|
+
timestamp: Date.now(),
|
|
141
|
+
what: `Replanned from ${existingProcedure.executor} to ${plan.executor}`,
|
|
142
|
+
why: `Previous executor had ${existingProcedure.consecutive_failures} consecutive failures (status: ${existingProcedure.status})`,
|
|
143
|
+
impact: 'pending',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ─── Shared Augmentation ─────────────────────────────────────
|
|
149
|
+
// Common pre-dispatch logic: insight injection, memory recall,
|
|
150
|
+
// implicit feedback, self-improvement ACK, and probe.
|
|
151
|
+
|
|
152
|
+
interface AugmentedContext {
|
|
153
|
+
plan: Awaited<ReturnType<typeof route>>['plan'];
|
|
154
|
+
nearMiss: string | null | undefined;
|
|
155
|
+
reclassified: boolean | undefined;
|
|
156
|
+
classification: string;
|
|
157
|
+
procKey: string;
|
|
158
|
+
existingProcedure: Awaited<ReturnType<typeof getProcedure>>;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function augmentIntent(intent: KernelIntent, env: EdgeEnv): Promise<AugmentedContext> {
|
|
162
|
+
// 1. Route
|
|
163
|
+
const { plan, nearMiss, reclassified } = await route(intent, env.db, env.groqApiKey, env.groqModel, env.groqBaseUrl, env.ai, env.tarotscriptFetcher);
|
|
164
|
+
const classification = intent.classified ?? 'unknown';
|
|
165
|
+
const procKey = procedureKey(classification, intent.complexity);
|
|
166
|
+
const existingProcedure = await getProcedure(env.db, procKey);
|
|
167
|
+
|
|
168
|
+
// 1.3. Cross-repo insight augmentation (CRIX #106)
|
|
169
|
+
if (env.memoryBinding && classification !== 'greeting' && classification !== 'heartbeat') {
|
|
170
|
+
try {
|
|
171
|
+
const insightContext = await fetchRelevantInsights(env, classification, intent.raw);
|
|
172
|
+
if (insightContext) {
|
|
173
|
+
intent.raw = `${insightContext}\n\n${intent.raw}`;
|
|
174
|
+
}
|
|
175
|
+
} catch (err) {
|
|
176
|
+
console.warn('[dispatch] Insight fetch failed (non-fatal):', err instanceof Error ? err.message : String(err));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 1.5. Memory-recall augmentation
|
|
181
|
+
if (classification === 'memory_recall') {
|
|
182
|
+
try {
|
|
183
|
+
if (env.memoryBinding) {
|
|
184
|
+
const relevant = await searchMemoryByKeywords(env.memoryBinding, intent.raw, 10);
|
|
185
|
+
if (relevant.length > 0) {
|
|
186
|
+
const memLines = relevant.map(m => `- [${m.topic}] ${m.fact} (confidence: ${m.confidence})`).join('\n');
|
|
187
|
+
intent.raw = `[Relevant memory entries matching this query]\n${memLines}\n\n[User's question]\n${intent.raw}`;
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
console.warn('[dispatch] Memory binding unavailable — skipping memory recall augmentation');
|
|
191
|
+
}
|
|
192
|
+
} catch (err) {
|
|
193
|
+
console.warn('[dispatch] Memory search failed (non-fatal):', err instanceof Error ? err.message : String(err));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 1.6. Implicit feedback — retrograde previous episode on user correction
|
|
198
|
+
if (classification === 'user_correction' && intent.source.threadId) {
|
|
199
|
+
try {
|
|
200
|
+
const retrograded = await retrogradeEpisode(env.db, intent.source.threadId);
|
|
201
|
+
if (retrograded) {
|
|
202
|
+
const badProcKey = procedureKey(retrograded.intent_class, intent.complexity);
|
|
203
|
+
await degradeProcedure(env.db, badProcKey, retrograded.executor ?? 'unknown');
|
|
204
|
+
console.log(`[dispatch] Implicit feedback: retrograded episode ${retrograded.id} (${retrograded.intent_class}/${retrograded.executor})`);
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
console.warn('[dispatch] Implicit feedback failed (non-fatal):', err instanceof Error ? err.message : String(err));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 1.7. Greeting → Re-entry Briefing
|
|
212
|
+
// Instead of a dumb "hi!", generate a situational briefing so AEGIS acts like
|
|
213
|
+
// a co-founder catching you up: what happened, what needs you, what's active.
|
|
214
|
+
if (classification === 'greeting') {
|
|
215
|
+
try {
|
|
216
|
+
const briefing = await generateBriefing(env);
|
|
217
|
+
const briefingText = formatBriefing(briefing);
|
|
218
|
+
intent.raw = `[The operator just opened a session. Generate a re-entry briefing — not a greeting. Lead with what needs their attention, then what shipped, then what you're working on. Be direct, no fluff. Use the data below.]\n\n${briefingText}\n\n[Operator said: ${intent.raw}]`;
|
|
219
|
+
} catch (err) {
|
|
220
|
+
console.warn('[dispatch] Briefing generation failed (non-fatal):', err instanceof Error ? err.message : String(err));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return { plan, nearMiss, reclassified, classification, procKey, existingProcedure };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ─── Self-Improvement ACK ────────────────────────────────────
|
|
228
|
+
// self_improvement is long-running — return immediate ACK, background the work.
|
|
229
|
+
|
|
230
|
+
function trySelfImprovementAck(
|
|
231
|
+
classification: string,
|
|
232
|
+
intent: KernelIntent,
|
|
233
|
+
env: EdgeEnv,
|
|
234
|
+
startMs: number,
|
|
235
|
+
): DispatchResult | null {
|
|
236
|
+
if (classification !== 'self_improvement' || intent.source.channel === 'internal') return null;
|
|
237
|
+
|
|
238
|
+
if (env.ctx) {
|
|
239
|
+
env.ctx.waitUntil(
|
|
240
|
+
(async () => {
|
|
241
|
+
const start = Date.now();
|
|
242
|
+
try {
|
|
243
|
+
await executeComposite(intent, env, buildMcpRegistry(env));
|
|
244
|
+
await env.db.prepare(
|
|
245
|
+
"INSERT INTO task_runs (task_name, status, duration_ms) VALUES ('self_improvement', 'ok', ?)"
|
|
246
|
+
).bind(Date.now() - start).run().catch(() => {});
|
|
247
|
+
} catch (err) {
|
|
248
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
249
|
+
console.error('[self_improvement] background run failed:', msg);
|
|
250
|
+
await env.db.prepare(
|
|
251
|
+
"INSERT INTO task_runs (task_name, status, duration_ms, error_message) VALUES ('self_improvement', 'error', ?, ?)"
|
|
252
|
+
).bind(Date.now() - start, msg).run().catch(() => {});
|
|
253
|
+
}
|
|
254
|
+
})(),
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
text: 'Self-improvement analysis queued. I\'ll review the codebase, check existing issues, and post any findings as GitHub issues + agenda items. This usually takes 1-2 minutes.',
|
|
260
|
+
executor: 'composite',
|
|
261
|
+
cost: 0,
|
|
262
|
+
latency_ms: Date.now() - startMs,
|
|
263
|
+
procedureHit: false,
|
|
264
|
+
classification,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ─── Probe + Execute ─────────────────────────────────────────
|
|
269
|
+
// Self-consistency probe, then executor dispatch. Shared between
|
|
270
|
+
// streaming and non-streaming paths.
|
|
271
|
+
|
|
272
|
+
interface ExecuteResult {
|
|
273
|
+
text: string;
|
|
274
|
+
cost: number;
|
|
275
|
+
meta?: unknown;
|
|
276
|
+
outcome: 'success' | 'failure' | 'partial_failure';
|
|
277
|
+
probeResult?: 'agreed' | 'split' | 'escalated';
|
|
278
|
+
earlyReturn?: DispatchResult; // probe agreed → skip executor
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function probeAndExecute(
|
|
282
|
+
ctx: AugmentedContext,
|
|
283
|
+
intent: KernelIntent,
|
|
284
|
+
env: EdgeEnv,
|
|
285
|
+
startMs: number,
|
|
286
|
+
onDelta?: (text: string) => void,
|
|
287
|
+
): Promise<ExecuteResult> {
|
|
288
|
+
const { plan, nearMiss, classification, procKey, existingProcedure } = ctx;
|
|
289
|
+
let probeResult: 'agreed' | 'split' | 'escalated' | undefined;
|
|
290
|
+
|
|
291
|
+
// Self-consistency probe
|
|
292
|
+
if (probeEligible(plan.executor, intent, classification)) {
|
|
293
|
+
try {
|
|
294
|
+
const probe = await probeConsistency(
|
|
295
|
+
env.groqApiKey, env.groqResponseModel, buildGroqSystemPrompt(), intent.raw, env.groqBaseUrl,
|
|
296
|
+
env.memoryBinding,
|
|
297
|
+
);
|
|
298
|
+
if (probe.sigma === 0 && probe.agreedText) {
|
|
299
|
+
console.log(`[dispatch] Probe agreed for "${classification}" — skipping ${plan.executor}`);
|
|
300
|
+
const text = probe.agreedText;
|
|
301
|
+
const cost = 0.0003;
|
|
302
|
+
probeResult = 'agreed';
|
|
303
|
+
plan.executor = 'groq';
|
|
304
|
+
if (onDelta) onDelta(text);
|
|
305
|
+
const latencyMs = Date.now() - startMs;
|
|
306
|
+
await recordOutcome(env, intent, classification, procKey, plan, existingProcedure, text, cost, latencyMs, nearMiss, 'success', ctx.reclassified);
|
|
307
|
+
return {
|
|
308
|
+
text, cost, outcome: 'success', probeResult,
|
|
309
|
+
earlyReturn: { text, executor: plan.executor, cost, latency_ms: latencyMs, procedureHit: !!plan.procedureId, classification, confidence: intent.confidence, reclassified: ctx.reclassified, probeResult, meta: undefined },
|
|
310
|
+
};
|
|
311
|
+
} else if (probe.sigma === 1.0) {
|
|
312
|
+
const prev = plan.executor;
|
|
313
|
+
plan.executor = plan.executor === 'workers_ai' ? 'gpt_oss' : 'composite';
|
|
314
|
+
probeResult = 'escalated';
|
|
315
|
+
console.log(`[dispatch] Probe disagreed for "${classification}" — escalating ${prev} → ${plan.executor}`);
|
|
316
|
+
} else {
|
|
317
|
+
probeResult = 'split';
|
|
318
|
+
}
|
|
319
|
+
} catch (err) {
|
|
320
|
+
console.warn('[dispatch] Probe failed (non-fatal):', err instanceof Error ? err.message : String(err));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Execute
|
|
325
|
+
let text: string;
|
|
326
|
+
let cost: number;
|
|
327
|
+
let meta: unknown;
|
|
328
|
+
let outcome: 'success' | 'failure' | 'partial_failure' = 'success';
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
let result: { text: string; cost: number; meta?: unknown };
|
|
332
|
+
|
|
333
|
+
// Streaming Claude path — uses executeClaudeStream for real-time deltas
|
|
334
|
+
if (onDelta && (plan.executor === 'claude' || plan.executor === 'claude_opus')) {
|
|
335
|
+
try {
|
|
336
|
+
// Tag intent so executeClaudeStream picks the right model
|
|
337
|
+
const savedClassified = intent.classified;
|
|
338
|
+
intent.classified = plan.executor;
|
|
339
|
+
const streamResult = await executeClaudeStream(intent, env, onDelta);
|
|
340
|
+
intent.classified = savedClassified;
|
|
341
|
+
result = streamResult;
|
|
342
|
+
} catch (streamErr) {
|
|
343
|
+
const msg = streamErr instanceof Error ? streamErr.message : String(streamErr);
|
|
344
|
+
if (msg.includes('Anthropic API error') || msg.includes('credit balance')) {
|
|
345
|
+
console.warn(`[dispatch] streaming ${plan.executor} failed, failover → gpt_oss: ${msg.slice(0, 120)}`);
|
|
346
|
+
const fallback = await executeGptOss(intent, env);
|
|
347
|
+
result = fallback;
|
|
348
|
+
plan.executor = 'gpt_oss';
|
|
349
|
+
onDelta(result.text);
|
|
350
|
+
} else {
|
|
351
|
+
throw streamErr;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
// Non-streaming or non-Claude executors
|
|
356
|
+
switch (plan.executor) {
|
|
357
|
+
case 'claude':
|
|
358
|
+
case 'claude_opus': {
|
|
359
|
+
const r = await executeWithAnthropicFailover(plan.executor, intent, env);
|
|
360
|
+
result = r;
|
|
361
|
+
plan.executor = r.actualExecutor;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case 'composite':
|
|
365
|
+
result = await executeComposite(intent, env, buildMcpRegistry(env));
|
|
366
|
+
break;
|
|
367
|
+
case 'gpt_oss':
|
|
368
|
+
result = await executeGptOss(intent, env);
|
|
369
|
+
break;
|
|
370
|
+
case 'workers_ai':
|
|
371
|
+
result = await executeWorkersAi(intent, env);
|
|
372
|
+
break;
|
|
373
|
+
case 'groq':
|
|
374
|
+
result = await executeGroq(intent, env);
|
|
375
|
+
break;
|
|
376
|
+
case 'direct':
|
|
377
|
+
result = await executeDirect(intent, env);
|
|
378
|
+
break;
|
|
379
|
+
case 'claude_code':
|
|
380
|
+
result = await executeCodeTask(intent, env);
|
|
381
|
+
break;
|
|
382
|
+
case 'tarotscript':
|
|
383
|
+
result = await executeTarotScript(intent, env);
|
|
384
|
+
break;
|
|
385
|
+
default:
|
|
386
|
+
throw new Error(`Unknown executor: ${plan.executor}`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// For streaming non-Claude executors, emit full text as single delta
|
|
390
|
+
if (onDelta) onDelta(result.text);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
text = result.text;
|
|
394
|
+
cost = result.cost;
|
|
395
|
+
meta = result.meta;
|
|
396
|
+
// Detect partial failure from composite executor (#71)
|
|
397
|
+
const partialMeta = result.meta as { partialFailure?: boolean } | undefined;
|
|
398
|
+
if (partialMeta?.partialFailure) {
|
|
399
|
+
outcome = 'partial_failure';
|
|
400
|
+
}
|
|
401
|
+
} catch (err) {
|
|
402
|
+
outcome = 'failure';
|
|
403
|
+
cost = 0;
|
|
404
|
+
text = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
405
|
+
if (onDelta) onDelta(text);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return { text, cost, meta, outcome, probeResult };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ─── Shadow Exploration (#290) ───────────────────────────────
|
|
412
|
+
// On ~5% of expensive dispatches, background-test a cheaper executor.
|
|
413
|
+
// If the cheaper tier produces quality output, record it as a candidate
|
|
414
|
+
// in procedural memory. Promotion happens automatically via the
|
|
415
|
+
// candidate_executor probation system in upsertProcedure().
|
|
416
|
+
|
|
417
|
+
const SHADOW_EXPLORATION_RATE = 0.05; // 5% of eligible dispatches
|
|
418
|
+
|
|
419
|
+
// Expensive → cheaper executor demotion map
|
|
420
|
+
const SHADOW_DEMOTION: Partial<Record<Executor, Executor>> = {
|
|
421
|
+
claude_opus: 'claude',
|
|
422
|
+
claude: 'gpt_oss',
|
|
423
|
+
composite: 'gpt_oss',
|
|
424
|
+
gpt_oss: 'workers_ai',
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// Skip shadow exploration for these classifications
|
|
428
|
+
const SHADOW_SKIP = new Set([
|
|
429
|
+
'heartbeat', 'greeting', 'code_task', 'self_improvement',
|
|
430
|
+
'goal_execution', 'user_correction',
|
|
431
|
+
]);
|
|
432
|
+
|
|
433
|
+
function shadowEligible(executor: Executor, classification: string, intent: KernelIntent): boolean {
|
|
434
|
+
if (SHADOW_SKIP.has(classification)) return false;
|
|
435
|
+
if (intent.source.channel === 'internal') return false;
|
|
436
|
+
if (!SHADOW_DEMOTION[executor]) return false;
|
|
437
|
+
return Math.random() < SHADOW_EXPLORATION_RATE;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Quality gate: is the shadow output good enough to count as a success?
|
|
441
|
+
function shadowQualityPass(primaryText: string, shadowText: string): boolean {
|
|
442
|
+
// Too short = likely error or refusal
|
|
443
|
+
if (shadowText.length < 20) return false;
|
|
444
|
+
// Length ratio: shadow should be at least 30% of primary length
|
|
445
|
+
if (shadowText.length < primaryText.length * 0.3) return false;
|
|
446
|
+
// Error patterns
|
|
447
|
+
const errorPatterns = /^error:|i cannot|i'm unable|as an ai|i don't have/i;
|
|
448
|
+
if (errorPatterns.test(shadowText.trim())) return false;
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async function tryShadowExploration(
|
|
453
|
+
ctx: AugmentedContext,
|
|
454
|
+
intent: KernelIntent,
|
|
455
|
+
env: EdgeEnv,
|
|
456
|
+
primaryText: string,
|
|
457
|
+
): Promise<void> {
|
|
458
|
+
const primaryExecutor = ctx.plan.executor;
|
|
459
|
+
const shadowExecutor = SHADOW_DEMOTION[primaryExecutor];
|
|
460
|
+
if (!shadowExecutor) return;
|
|
461
|
+
|
|
462
|
+
try {
|
|
463
|
+
// Clone intent to avoid mutation
|
|
464
|
+
const shadowIntent: KernelIntent = { ...intent, classified: shadowExecutor };
|
|
465
|
+
let result: { text: string; cost: number };
|
|
466
|
+
|
|
467
|
+
switch (shadowExecutor) {
|
|
468
|
+
case 'gpt_oss':
|
|
469
|
+
result = await executeGptOss(shadowIntent, env);
|
|
470
|
+
break;
|
|
471
|
+
case 'workers_ai':
|
|
472
|
+
result = await executeWorkersAi(shadowIntent, env);
|
|
473
|
+
break;
|
|
474
|
+
case 'claude':
|
|
475
|
+
result = await executeClaude(shadowIntent, env);
|
|
476
|
+
break;
|
|
477
|
+
default:
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const passed = shadowQualityPass(primaryText, result.text);
|
|
482
|
+
const outcome = passed ? 'success' : 'failure';
|
|
483
|
+
|
|
484
|
+
// Record to procedural memory as the shadow executor —
|
|
485
|
+
// upsertProcedure's probation system handles candidate tracking
|
|
486
|
+
await upsertProcedure(
|
|
487
|
+
env.db, ctx.procKey, shadowExecutor,
|
|
488
|
+
JSON.stringify({ executor: shadowExecutor }),
|
|
489
|
+
outcome, 0, result.cost,
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
console.log(
|
|
493
|
+
`[shadow] ${ctx.classification}: ${primaryExecutor} → ${shadowExecutor} = ${outcome}` +
|
|
494
|
+
` (primary: ${primaryText.length} chars, shadow: ${result.text.length} chars)`,
|
|
495
|
+
);
|
|
496
|
+
} catch (err) {
|
|
497
|
+
console.warn('[shadow] exploration failed (non-fatal):', err instanceof Error ? err.message : String(err));
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// ─── Shared Finalization ─────────────────────────────────────
|
|
502
|
+
|
|
503
|
+
function buildResult(
|
|
504
|
+
ctx: AugmentedContext,
|
|
505
|
+
intent: KernelIntent,
|
|
506
|
+
exec: ExecuteResult,
|
|
507
|
+
latencyMs: number,
|
|
508
|
+
): DispatchResult {
|
|
509
|
+
return {
|
|
510
|
+
text: exec.text,
|
|
511
|
+
executor: ctx.plan.executor,
|
|
512
|
+
cost: exec.cost,
|
|
513
|
+
latency_ms: latencyMs,
|
|
514
|
+
procedureHit: !!ctx.plan.procedureId,
|
|
515
|
+
classification: ctx.classification,
|
|
516
|
+
confidence: intent.confidence,
|
|
517
|
+
reclassified: ctx.reclassified,
|
|
518
|
+
probeResult: exec.probeResult,
|
|
519
|
+
meta: exec.meta,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ─── Main Dispatch Loop ──────────────────────────────────────
|
|
524
|
+
|
|
525
|
+
export async function dispatch(intent: KernelIntent, env: EdgeEnv): Promise<DispatchResult> {
|
|
526
|
+
const startMs = Date.now();
|
|
527
|
+
const ctx = await augmentIntent(intent, env);
|
|
528
|
+
|
|
529
|
+
// Self-improvement ACK (backgrounded)
|
|
530
|
+
const ack = trySelfImprovementAck(ctx.classification, intent, env, startMs);
|
|
531
|
+
if (ack) return ack;
|
|
532
|
+
|
|
533
|
+
const exec = await probeAndExecute(ctx, intent, env, startMs);
|
|
534
|
+
if (exec.earlyReturn) return exec.earlyReturn;
|
|
535
|
+
|
|
536
|
+
const latencyMs = Date.now() - startMs;
|
|
537
|
+
await recordOutcome(env, intent, ctx.classification, ctx.procKey, ctx.plan, ctx.existingProcedure, exec.text, exec.cost, latencyMs, ctx.nearMiss, exec.outcome, ctx.reclassified);
|
|
538
|
+
|
|
539
|
+
// Background: shadow exploration + shadow read
|
|
540
|
+
if (env.ctx) {
|
|
541
|
+
if (exec.outcome === 'success' && shadowEligible(ctx.plan.executor as Executor, ctx.classification, intent)) {
|
|
542
|
+
env.ctx.waitUntil(tryShadowExploration(ctx, intent, env, exec.text));
|
|
543
|
+
}
|
|
544
|
+
if (env.memoryBinding) {
|
|
545
|
+
env.ctx.waitUntil(
|
|
546
|
+
getAllMemoryForContext(env.memoryBinding).catch(err => {
|
|
547
|
+
console.warn('[dispatch] Background shadow read failed:', err instanceof Error ? err.message : String(err));
|
|
548
|
+
}),
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return buildResult(ctx, intent, exec, latencyMs);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// ─── Streaming Dispatch ──────────────────────────────────────
|
|
557
|
+
|
|
558
|
+
export async function dispatchStream(
|
|
559
|
+
intent: KernelIntent,
|
|
560
|
+
env: EdgeEnv,
|
|
561
|
+
onDelta: (text: string) => void,
|
|
562
|
+
): Promise<DispatchResult> {
|
|
563
|
+
const startMs = Date.now();
|
|
564
|
+
const ctx = await augmentIntent(intent, env);
|
|
565
|
+
|
|
566
|
+
const exec = await probeAndExecute(ctx, intent, env, startMs, onDelta);
|
|
567
|
+
if (exec.earlyReturn) return exec.earlyReturn;
|
|
568
|
+
|
|
569
|
+
const latencyMs = Date.now() - startMs;
|
|
570
|
+
await recordOutcome(env, intent, ctx.classification, ctx.procKey, ctx.plan, ctx.existingProcedure, exec.text, exec.cost, latencyMs, ctx.nearMiss, exec.outcome, ctx.reclassified);
|
|
571
|
+
|
|
572
|
+
// Background: shadow exploration + shadow read
|
|
573
|
+
if (env.ctx) {
|
|
574
|
+
if (exec.outcome === 'success' && shadowEligible(ctx.plan.executor as Executor, ctx.classification, intent)) {
|
|
575
|
+
env.ctx.waitUntil(tryShadowExploration(ctx, intent, env, exec.text));
|
|
576
|
+
}
|
|
577
|
+
if (env.memoryBinding) {
|
|
578
|
+
env.ctx.waitUntil(
|
|
579
|
+
getAllMemoryForContext(env.memoryBinding).catch(err => {
|
|
580
|
+
console.warn('[dispatch] Background shadow read failed:', err instanceof Error ? err.message : String(err));
|
|
581
|
+
}),
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return buildResult(ctx, intent, exec, latencyMs);
|
|
587
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ─── Domain Pre-Filter (Phase 0 — observe only) ────────────
|
|
2
|
+
// Tags queries with a domain slug from a fixed taxonomy.
|
|
3
|
+
// Does NOT affect routing — purely observational for data collection.
|
|
4
|
+
|
|
5
|
+
export interface DomainTag {
|
|
6
|
+
domain: string; // slug from taxonomy
|
|
7
|
+
confidence: number; // 0.0-1.0
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const DOMAIN_SIGNALS: Record<string, RegExp[]> = {
|
|
11
|
+
legal: [/\bllc\b/i, /\bcompliance\b/i, /\bfiling\b/i, /\bcontract\b/i, /\bcorporat/i, /\bein\b/i, /\bregistered agent/i, /\btrademark\b/i],
|
|
12
|
+
finance: [/\brevenue\b/i, /\bcost\b/i, /\bcogs\b/i, /\bmargin\b/i, /\btax\b/i, /\binvoice\b/i, /\bstripe\b/i, /\brunway\b/i, /\bburn\b/i],
|
|
13
|
+
technical: [/\binfra/i, /\bdevops\b/i, /\bdeploy/i, /\bworker\b/i, /\bcloudflare\b/i, /\bd1\b/i, /\bkv\b/i, /\bdurable object/i],
|
|
14
|
+
software: [/\brefactor/i, /\bfunction\b/i, /\bclass\b/i, /\bapi\b/i, /\bendpoint\b/i, /\btypescript\b/i, /\bbug\b/i],
|
|
15
|
+
ai_ml: [/\bmodel\b/i, /\bllm\b/i, /\binference\b/i, /\btraining\b/i, /\bagent\b/i, /\bprompt\b/i, /\bembedding/i, /\bvector/i],
|
|
16
|
+
creative: [/\bblog\b/i, /\bwriting\b/i, /\bdesign\b/i, /\bcontent\b/i, /\bmarketing\b/i],
|
|
17
|
+
operations: [/\bproject\b/i, /\bsprint\b/i, /\bpipeline\b/i, /\bprocess\b/i, /\bworkflow\b/i],
|
|
18
|
+
general: [], // fallback
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function domainPreFilter(query: string): DomainTag {
|
|
22
|
+
let bestDomain = 'general';
|
|
23
|
+
let bestCount = 0;
|
|
24
|
+
let tied = false;
|
|
25
|
+
|
|
26
|
+
for (const [domain, patterns] of Object.entries(DOMAIN_SIGNALS)) {
|
|
27
|
+
if (patterns.length === 0) continue;
|
|
28
|
+
let matchCount = 0;
|
|
29
|
+
for (const pattern of patterns) {
|
|
30
|
+
if (pattern.test(query)) matchCount++;
|
|
31
|
+
}
|
|
32
|
+
if (matchCount > bestCount) {
|
|
33
|
+
bestDomain = domain;
|
|
34
|
+
bestCount = matchCount;
|
|
35
|
+
tied = false;
|
|
36
|
+
} else if (matchCount > 0 && matchCount === bestCount) {
|
|
37
|
+
tied = true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// No matches or tie → general with low confidence
|
|
42
|
+
if (bestCount === 0 || tied) {
|
|
43
|
+
return { domain: 'general', confidence: 0.3 };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const totalPossible = DOMAIN_SIGNALS[bestDomain].length;
|
|
47
|
+
const confidence = totalPossible > 0 ? bestCount / totalPossible : 0.3;
|
|
48
|
+
|
|
49
|
+
return { domain: bestDomain, confidence };
|
|
50
|
+
}
|