@stackbilt/aegis-core 0.6.5 → 0.7.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/cli/aegis.mjs +356 -0
- package/package.json +9 -1
- package/schema.sql +4 -0
- package/src/adapters/voice/cloudflare-agent.ts +0 -0
- package/src/auth.ts +13 -5
- package/src/bluesky.ts +0 -0
- package/src/claude-tools/content.ts +0 -0
- package/src/claude-tools/email.ts +0 -0
- package/src/claude.ts +133 -268
- package/src/codebeast.ts +0 -0
- package/src/composite.ts +49 -79
- package/src/content/column.ts +0 -0
- package/src/content/hero-image.ts +0 -0
- package/src/content/index.ts +0 -0
- package/src/content/journal.ts +0 -0
- package/src/content/roundtable.ts +0 -0
- package/src/contracts/agenda-item.contract.ts +0 -0
- package/src/contracts/cc-task.contract.ts +0 -0
- package/src/contracts/goal.contract.ts +0 -0
- package/src/contracts/memory-entry.contract.ts +0 -0
- package/src/core.ts +5 -0
- package/src/dashboard.ts +0 -0
- package/src/decision-docs.ts +0 -0
- package/src/dispatch.ts +0 -0
- package/src/durable-objects/chat-session-auth.ts +20 -0
- package/src/durable-objects/chat-session.ts +251 -0
- package/src/edge-env.ts +0 -0
- package/src/exports.ts +0 -0
- package/src/github-projects.ts +0 -0
- package/src/groq.ts +61 -113
- package/src/index.ts +4 -0
- package/src/kernel/argus-actions.ts +0 -0
- package/src/kernel/argus-correlation.ts +0 -0
- package/src/kernel/board.ts +0 -0
- package/src/kernel/classify-memory-topic.ts +0 -0
- package/src/kernel/disambiguation.ts +55 -0
- package/src/kernel/dispatch.ts +59 -44
- package/src/kernel/dynamic-tools.ts +30 -52
- package/src/kernel/executor-port.ts +0 -0
- package/src/kernel/executor-router.ts +0 -0
- package/src/kernel/executors/claude.ts +1 -0
- package/src/kernel/executors/direct.ts +14 -0
- package/src/kernel/executors/workers-ai.ts +5 -0
- package/src/kernel/grounding/fabrication-detector.ts +0 -0
- package/src/kernel/grounding/fanout.ts +0 -0
- package/src/kernel/grounding/semantic-sanhedrin.ts +0 -0
- package/src/kernel/grounding/verify.ts +0 -0
- package/src/kernel/grounding-layer.ts +0 -0
- package/src/kernel/insight-cache.ts +0 -0
- package/src/kernel/memory/episodic.ts +3 -1
- package/src/kernel/memory/insights.ts +0 -0
- package/src/kernel/memory-guardrails.ts +0 -0
- package/src/kernel/memory-service.ts +0 -0
- package/src/kernel/patterns.ts +0 -0
- package/src/kernel/port.ts +0 -0
- package/src/kernel/provider-factory.ts +0 -0
- package/src/kernel/resilience.ts +0 -0
- package/src/kernel/router.ts +33 -11
- package/src/kernel/scheduled/agent-dispatch.ts +0 -0
- package/src/kernel/scheduled/argus-analytics.ts +0 -0
- package/src/kernel/scheduled/argus-heartbeat.ts +0 -0
- package/src/kernel/scheduled/argus-notify.ts +0 -0
- package/src/kernel/scheduled/board-sync.ts +0 -0
- package/src/kernel/scheduled/ci-watcher.ts +0 -0
- package/src/kernel/scheduled/content-drip.ts +0 -0
- package/src/kernel/scheduled/content.ts +0 -0
- package/src/kernel/scheduled/conversation-facts.ts +9 -7
- package/src/kernel/scheduled/cost-report.ts +0 -0
- package/src/kernel/scheduled/dev-activity.ts +0 -0
- package/src/kernel/scheduled/digest.ts +30 -3
- package/src/kernel/scheduled/dreaming/agenda-triage.ts +0 -0
- package/src/kernel/scheduled/dreaming/facts.ts +0 -0
- package/src/kernel/scheduled/dreaming/index.ts +0 -0
- package/src/kernel/scheduled/dreaming/llm.ts +9 -5
- package/src/kernel/scheduled/dreaming/pattern-synthesis.ts +0 -0
- package/src/kernel/scheduled/dreaming/persona.ts +0 -0
- package/src/kernel/scheduled/dreaming/symbolic.ts +0 -0
- package/src/kernel/scheduled/dreaming/task-proposals.ts +0 -0
- package/src/kernel/scheduled/entropy.ts +0 -0
- package/src/kernel/scheduled/feed-watcher.ts +0 -0
- package/src/kernel/scheduled/inbox-processor.ts +0 -0
- package/src/kernel/scheduled/issue-proposer.ts +0 -0
- package/src/kernel/scheduled/issue-watcher.ts +0 -0
- package/src/kernel/scheduled/pr-automerge.ts +0 -0
- package/src/kernel/scheduled/product-health.ts +0 -0
- package/src/kernel/scheduled/self-improvement.ts +0 -0
- package/src/kernel/scheduled/social-engage.ts +12 -8
- package/src/kernel/scheduled/task-audit.ts +0 -0
- package/src/kernel/types.ts +6 -0
- package/src/landing.ts +0 -0
- package/src/lib/audit-chain/chain.ts +0 -0
- package/src/lib/audit-chain/types.ts +0 -0
- package/src/lib/observability/errors.ts +0 -0
- package/src/operator/config.ts +0 -0
- package/src/operator/persona.ts +0 -0
- package/src/operator/prompt-builder.ts +3 -0
- package/src/pulse.ts +0 -0
- package/src/routes/bluesky.ts +0 -0
- package/src/routes/chat-ws.ts +17 -0
- package/src/routes/codebeast.ts +0 -0
- package/src/routes/content.ts +0 -0
- package/src/routes/dynamic-tools.ts +0 -0
- package/src/routes/observability.ts +0 -0
- package/src/routes/operator-logs.ts +0 -0
- package/src/routes/pages.ts +5 -1
- package/src/schema-enums.ts +0 -0
- package/src/task-intelligence.ts +0 -0
- package/src/types.ts +6 -0
- package/src/ui.ts +594 -2
- package/src/version.ts +3 -3
- package/src/wiki/client.ts +0 -0
- package/src/wiki/types.ts +0 -0
package/src/groq.ts
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
// Edge-native Groq
|
|
1
|
+
// Edge-native Groq helpers backed by @stackbilt/llm-providers
|
|
2
|
+
|
|
3
|
+
import { createLLMProviderFactory, type LLMMessage } from '@stackbilt/llm-providers';
|
|
4
|
+
import { tokenize, jaccardSimilarity } from './kernel/memory/index.js';
|
|
5
|
+
import { cosineSimilarity } from './kernel/memory/semantic.js';
|
|
6
|
+
import type { MemoryServiceBinding } from './types.js';
|
|
7
|
+
|
|
8
|
+
function buildGroqFactory(apiKey: string, baseUrl: string) {
|
|
9
|
+
return createLLMProviderFactory({
|
|
10
|
+
groq: { apiKey, baseUrl },
|
|
11
|
+
fallbackRules: [],
|
|
12
|
+
enableCircuitBreaker: true,
|
|
13
|
+
enableRetries: true,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function coerceText(content: unknown): string {
|
|
18
|
+
if (typeof content === 'string') return content;
|
|
19
|
+
if (content == null) return '';
|
|
20
|
+
return typeof content === 'object' ? JSON.stringify(content) : String(content);
|
|
21
|
+
}
|
|
2
22
|
|
|
3
23
|
export async function askGroq(
|
|
4
24
|
apiKey: string,
|
|
@@ -7,45 +27,27 @@ export async function askGroq(
|
|
|
7
27
|
userPrompt: string,
|
|
8
28
|
baseUrl = 'https://api.groq.com',
|
|
9
29
|
): Promise<string> {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
headers: {
|
|
13
|
-
'Content-Type': 'application/json',
|
|
14
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
15
|
-
},
|
|
16
|
-
body: JSON.stringify({
|
|
30
|
+
try {
|
|
31
|
+
const result = await buildGroqFactory(apiKey, baseUrl).generateResponse({
|
|
17
32
|
model,
|
|
33
|
+
systemPrompt,
|
|
18
34
|
temperature: 0.3,
|
|
19
|
-
|
|
35
|
+
maxTokens: 500,
|
|
20
36
|
messages: [
|
|
21
|
-
{ role: 'system', content: systemPrompt },
|
|
22
37
|
{ role: 'user', content: userPrompt },
|
|
23
38
|
],
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
throw new Error(`Groq API error ${response.status}: ${errText}`);
|
|
39
|
+
});
|
|
40
|
+
return coerceText(result.message);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43
|
+
throw new Error(`Groq API error: ${msg}`);
|
|
30
44
|
}
|
|
31
|
-
|
|
32
|
-
const data = await response.json<{
|
|
33
|
-
choices: { message: { content: unknown } }[];
|
|
34
|
-
usage?: { total_tokens: number };
|
|
35
|
-
}>();
|
|
36
|
-
|
|
37
|
-
const content = data.choices[0]?.message?.content;
|
|
38
|
-
if (typeof content === 'string') return content;
|
|
39
|
-
if (content == null) return '';
|
|
40
|
-
// Some Groq-routed models (notably gpt-oss tool-calling variants) return content
|
|
41
|
-
// as an array of content blocks. Coerce so downstream string operations don't crash.
|
|
42
|
-
return typeof content === 'object' ? JSON.stringify(content) : String(content);
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
// ───
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
47
|
+
// ─── Classification confidence helper ────────────────────────
|
|
48
|
+
// Provider-backed replacement for the former raw Groq logprobs call.
|
|
49
|
+
// llm-providers does not expose provider logprob request options yet, so
|
|
50
|
+
// tokenConfidence mirrors self-reported confidence until that contract lands.
|
|
49
51
|
|
|
50
52
|
export interface LogprobClassification {
|
|
51
53
|
pattern: string;
|
|
@@ -62,65 +64,20 @@ export async function askGroqWithLogprobs(
|
|
|
62
64
|
userPrompt: string,
|
|
63
65
|
baseUrl = 'https://api.groq.com',
|
|
64
66
|
): Promise<LogprobClassification> {
|
|
65
|
-
const
|
|
66
|
-
method: 'POST',
|
|
67
|
-
headers: {
|
|
68
|
-
'Content-Type': 'application/json',
|
|
69
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
70
|
-
},
|
|
71
|
-
body: JSON.stringify({
|
|
72
|
-
model,
|
|
73
|
-
temperature: 0.1,
|
|
74
|
-
max_tokens: 200,
|
|
75
|
-
response_format: { type: 'json_object' },
|
|
76
|
-
logprobs: true,
|
|
77
|
-
top_logprobs: 3,
|
|
78
|
-
messages: [
|
|
79
|
-
{ role: 'system', content: systemPrompt },
|
|
80
|
-
{ role: 'user', content: userPrompt },
|
|
81
|
-
],
|
|
82
|
-
}),
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
if (!response.ok) {
|
|
86
|
-
const errText = await response.text();
|
|
87
|
-
throw new Error(`Groq logprobs API error ${response.status}: ${errText}`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
interface LogprobToken {
|
|
91
|
-
token: string;
|
|
92
|
-
logprob: number;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const data = await response.json<{
|
|
96
|
-
choices: {
|
|
97
|
-
message: { content: string };
|
|
98
|
-
logprobs?: { content?: LogprobToken[] };
|
|
99
|
-
}[];
|
|
100
|
-
}>();
|
|
101
|
-
|
|
102
|
-
const raw = data.choices[0]?.message?.content ?? '{}';
|
|
103
|
-
const parsed = JSON.parse(raw) as {
|
|
67
|
+
const { parsed } = await askGroqJson<{
|
|
104
68
|
pattern?: string;
|
|
105
69
|
complexity?: number;
|
|
106
70
|
needs_tools?: boolean;
|
|
107
71
|
confidence?: number;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// Compute geometric mean of token logprobs
|
|
111
|
-
let tokenConfidence = 0.5; // fallback if no logprobs
|
|
112
|
-
const logprobTokens = data.choices[0]?.logprobs?.content;
|
|
113
|
-
if (logprobTokens && logprobTokens.length > 0) {
|
|
114
|
-
const sumLogprobs = logprobTokens.reduce((sum, t) => sum + t.logprob, 0);
|
|
115
|
-
tokenConfidence = Math.exp(sumLogprobs / logprobTokens.length);
|
|
116
|
-
}
|
|
72
|
+
}>(apiKey, model, systemPrompt, userPrompt, baseUrl, { maxTokens: 200, temperature: 0.1 });
|
|
73
|
+
const confidence = parsed.confidence ?? 0.5;
|
|
117
74
|
|
|
118
75
|
return {
|
|
119
76
|
pattern: (parsed.pattern ?? 'general_knowledge').toLowerCase().replace(/[^a-z_]/g, ''),
|
|
120
77
|
complexity: parsed.complexity ?? 2,
|
|
121
78
|
needs_tools: parsed.needs_tools ?? false,
|
|
122
|
-
selfReportedConfidence:
|
|
123
|
-
tokenConfidence,
|
|
79
|
+
selfReportedConfidence: confidence,
|
|
80
|
+
tokenConfidence: confidence,
|
|
124
81
|
};
|
|
125
82
|
}
|
|
126
83
|
|
|
@@ -130,10 +87,6 @@ export async function askGroqWithLogprobs(
|
|
|
130
87
|
// Jaccard when memoryBinding is unavailable.
|
|
131
88
|
// Returns σ metric: 0=all agree, 0.5=partial, 1.0=disagree.
|
|
132
89
|
|
|
133
|
-
import { tokenize, jaccardSimilarity } from './kernel/memory/index.js';
|
|
134
|
-
import { cosineSimilarity } from './kernel/memory/semantic.js';
|
|
135
|
-
import type { MemoryServiceBinding } from './types.js';
|
|
136
|
-
|
|
137
90
|
const PROBE_TIMEOUT_MS = 3_000;
|
|
138
91
|
const JACCARD_AGREEMENT_THRESHOLD = 0.5;
|
|
139
92
|
const COSINE_AGREEMENT_THRESHOLD = 0.85;
|
|
@@ -210,8 +163,7 @@ export async function askGroqJson<T = unknown>(
|
|
|
210
163
|
baseUrl = 'https://api.groq.com',
|
|
211
164
|
options?: { maxTokens?: number; temperature?: number; prefill?: string },
|
|
212
165
|
): Promise<{ parsed: T; raw: string; usage?: { prompt_tokens: number; completion_tokens: number } }> {
|
|
213
|
-
const messages:
|
|
214
|
-
{ role: 'system', content: systemPrompt },
|
|
166
|
+
const messages: LLMMessage[] = [
|
|
215
167
|
{ role: 'user', content: userPrompt },
|
|
216
168
|
];
|
|
217
169
|
// Prefilling: seed the assistant response to steer tone/format
|
|
@@ -219,34 +171,30 @@ export async function askGroqJson<T = unknown>(
|
|
|
219
171
|
messages.push({ role: 'assistant', content: options.prefill });
|
|
220
172
|
}
|
|
221
173
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
headers: {
|
|
225
|
-
'Content-Type': 'application/json',
|
|
226
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
227
|
-
},
|
|
228
|
-
body: JSON.stringify({
|
|
174
|
+
try {
|
|
175
|
+
const result = await buildGroqFactory(apiKey, baseUrl).generateResponse({
|
|
229
176
|
model,
|
|
177
|
+
systemPrompt,
|
|
230
178
|
temperature: options?.temperature ?? 0.2,
|
|
231
|
-
|
|
179
|
+
maxTokens: options?.maxTokens ?? 2000,
|
|
232
180
|
response_format: { type: 'json_object' },
|
|
233
181
|
messages,
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const completion = result.message ?? '{}';
|
|
185
|
+
// If prefilled, the model continues from the prefill — concatenate for valid JSON
|
|
186
|
+
const raw = options?.prefill ? options.prefill + completion : completion;
|
|
187
|
+
const parsed = JSON.parse(raw) as T;
|
|
188
|
+
return {
|
|
189
|
+
parsed,
|
|
190
|
+
raw,
|
|
191
|
+
usage: {
|
|
192
|
+
prompt_tokens: result.usage.inputTokens,
|
|
193
|
+
completion_tokens: result.usage.outputTokens,
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
} catch (err) {
|
|
197
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
198
|
+
throw new Error(`Groq API error: ${msg}`);
|
|
240
199
|
}
|
|
241
|
-
|
|
242
|
-
const data = await response.json<{
|
|
243
|
-
choices: { message: { content: string } }[];
|
|
244
|
-
usage?: { prompt_tokens: number; completion_tokens: number };
|
|
245
|
-
}>();
|
|
246
|
-
|
|
247
|
-
const completion = data.choices[0]?.message?.content ?? '{}';
|
|
248
|
-
// If prefilled, the model continues from the prefill — concatenate for valid JSON
|
|
249
|
-
const raw = options?.prefill ? options.prefill + completion : completion;
|
|
250
|
-
const parsed = JSON.parse(raw) as T;
|
|
251
|
-
return { parsed, raw, usage: data.usage };
|
|
252
200
|
}
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { observability } from './routes/observability.js';
|
|
|
24
24
|
import { pages } from './routes/pages.js';
|
|
25
25
|
import { ccTasks } from './routes/cc-tasks.js';
|
|
26
26
|
import { messages } from './routes/messages.js';
|
|
27
|
+
import { chatWs } from './routes/chat-ws.js';
|
|
27
28
|
import { content } from './routes/content.js';
|
|
28
29
|
import { codebeast } from './routes/codebeast.js';
|
|
29
30
|
import { bluesky } from './routes/bluesky.js';
|
|
@@ -44,6 +45,7 @@ app.route('/', observability);
|
|
|
44
45
|
app.route('/', pages);
|
|
45
46
|
app.route('/', ccTasks);
|
|
46
47
|
app.route('/', messages);
|
|
48
|
+
app.route('/', chatWs);
|
|
47
49
|
app.route('/', content);
|
|
48
50
|
app.route('/', codebeast);
|
|
49
51
|
app.route('/', bluesky);
|
|
@@ -87,3 +89,5 @@ export default {
|
|
|
87
89
|
ctx.waitUntil(runScheduledTasks(buildEdgeEnv(env)));
|
|
88
90
|
},
|
|
89
91
|
};
|
|
92
|
+
|
|
93
|
+
export { ChatSession } from './durable-objects/chat-session.js';
|
|
File without changes
|
|
File without changes
|
package/src/kernel/board.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface DisambiguationRequest {
|
|
2
|
+
concept: string;
|
|
3
|
+
question: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const DATA_INTENT =
|
|
7
|
+
/\b(what|show|calculate|report|compare|analy[sz]e|trend|metric|dashboard|how many|how much|rate)\b/i;
|
|
8
|
+
|
|
9
|
+
const KNOWN_DATA_SURFACES =
|
|
10
|
+
/\b(compliance|deadline|deadlines|filing|filings|document|documents|project|projects|agenda|goal|goals|task|tasks|health|heartbeat)\b/i;
|
|
11
|
+
|
|
12
|
+
const DISAMBIGUATORS =
|
|
13
|
+
/\b(by|defined as|definition|seat count|seats|mrr|arr|gross|net|monthly|annual|period|last \d+|past \d+|count|rate)\b/i;
|
|
14
|
+
|
|
15
|
+
const AMBIGUOUS_CONCEPTS: Array<{ name: string; pattern: RegExp; question: string }> = [
|
|
16
|
+
{
|
|
17
|
+
name: 'churn',
|
|
18
|
+
pattern: /\bchurn\b/i,
|
|
19
|
+
question: 'Do you mean churn by seat count, account count, revenue, MRR, or another defined basis?',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'retention',
|
|
23
|
+
pattern: /\bretention\b/i,
|
|
24
|
+
question: 'Do you mean retention by user, account, seat, revenue, cohort, or another defined basis?',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'active users',
|
|
28
|
+
pattern: /\b(active users?|users?)\b/i,
|
|
29
|
+
question: 'Do you mean active users by login, event activity, paid seat, account membership, or another defined rule?',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'conversion',
|
|
33
|
+
pattern: /\b(conversion|activation)\b/i,
|
|
34
|
+
question: 'Do you mean conversion by visit, signup, qualified lead, paid account, or another funnel step?',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'revenue',
|
|
38
|
+
pattern: /\b(revenue|growth)\b/i,
|
|
39
|
+
question: 'Do you mean revenue by gross, net, MRR, ARR, cash receipts, or another defined basis?',
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export function detectDisambiguationNeed(message: string): DisambiguationRequest | null {
|
|
44
|
+
if (!DATA_INTENT.test(message)) return null;
|
|
45
|
+
if (KNOWN_DATA_SURFACES.test(message)) return null;
|
|
46
|
+
if (DISAMBIGUATORS.test(message)) return null;
|
|
47
|
+
|
|
48
|
+
for (const concept of AMBIGUOUS_CONCEPTS) {
|
|
49
|
+
if (concept.pattern.test(message)) {
|
|
50
|
+
return { concept: concept.name, question: concept.question };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|
package/src/kernel/dispatch.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { route } from './router.js';
|
|
2
2
|
import { recordEpisode, retrogradeEpisode, upsertProcedure, addRefinement, degradeProcedure, getProcedure, procedureKey } from './memory/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { getAllMemoryForContext } from './memory-adapter.js';
|
|
4
4
|
import { generateBriefing, formatBriefing } from './briefing.js';
|
|
5
|
-
import { fetchRelevantInsights } from './insight-cache.js';
|
|
6
5
|
import { probeConsistency } from '../groq.js';
|
|
7
6
|
import { executeComposite } from '../composite.js';
|
|
8
7
|
import { buildGroqSystemPrompt } from '../operator/prompt-builder.js';
|
|
9
8
|
import type { KernelIntent, DispatchResult, Executor } from './types.js';
|
|
9
|
+
import type { GroundingEnvelope } from './grounding/fanout.js';
|
|
10
|
+
import {
|
|
11
|
+
augmentWithEntityGrounding,
|
|
12
|
+
augmentWithInsights,
|
|
13
|
+
augmentWithMemoryRecall,
|
|
14
|
+
applyFabricationCheck,
|
|
15
|
+
applyGapSignal,
|
|
16
|
+
applyGroundingProof,
|
|
17
|
+
} from './grounding-layer.js';
|
|
10
18
|
import {
|
|
11
19
|
executeGptOss,
|
|
12
20
|
executeClaudeStream,
|
|
@@ -113,6 +121,7 @@ async function recordOutcome(
|
|
|
113
121
|
latencyMs: number,
|
|
114
122
|
nearMiss: string | null | undefined,
|
|
115
123
|
outcome: 'success' | 'failure' | 'partial_failure',
|
|
124
|
+
groundingGap: boolean,
|
|
116
125
|
reclassified?: boolean,
|
|
117
126
|
): Promise<void> {
|
|
118
127
|
await recordEpisode(env.db, {
|
|
@@ -127,6 +136,7 @@ async function recordOutcome(
|
|
|
127
136
|
reclassified,
|
|
128
137
|
thread_id: intent.source.threadId,
|
|
129
138
|
executor: plan.executor,
|
|
139
|
+
grounding_gap: groundingGap,
|
|
130
140
|
});
|
|
131
141
|
|
|
132
142
|
await upsertProcedure(env.db, procKey, plan.executor, JSON.stringify({ executor: plan.executor }), outcome, latencyMs, cost);
|
|
@@ -156,43 +166,29 @@ interface AugmentedContext {
|
|
|
156
166
|
classification: string;
|
|
157
167
|
procKey: string;
|
|
158
168
|
existingProcedure: Awaited<ReturnType<typeof getProcedure>>;
|
|
169
|
+
grounding?: GroundingEnvelope;
|
|
159
170
|
}
|
|
160
171
|
|
|
161
172
|
async function augmentIntent(intent: KernelIntent, env: EdgeEnv): Promise<AugmentedContext> {
|
|
162
173
|
// 1. Route
|
|
163
174
|
const { plan, nearMiss, reclassified } = await route(intent, env.db, env.groqApiKey, env.groqModel, env.groqBaseUrl, env.ai, env.tarotscriptFetcher);
|
|
175
|
+
if (intent.forcedExecutor) {
|
|
176
|
+
plan.executor = intent.forcedExecutor;
|
|
177
|
+
plan.reasoning = `Forced by channel frame: ${intent.forcedExecutor}`;
|
|
178
|
+
plan.costCeiling = intent.forcedExecutor === 'direct' ? 'free'
|
|
179
|
+
: intent.forcedExecutor === 'workers_ai' || intent.forcedExecutor === 'gpt_oss' || intent.forcedExecutor === 'groq'
|
|
180
|
+
? 'cheap'
|
|
181
|
+
: 'expensive';
|
|
182
|
+
}
|
|
164
183
|
const classification = intent.classified ?? 'unknown';
|
|
165
184
|
const procKey = procedureKey(classification, intent.complexity);
|
|
166
185
|
const existingProcedure = await getProcedure(env.db, procKey);
|
|
167
186
|
|
|
168
|
-
// 1.3.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
}
|
|
187
|
+
// 1.3-1.5. Grounding-layer augmentations: cross-repo insights, entity
|
|
188
|
+
// grounding, and memory recall. Each helper is non-fatal by contract.
|
|
189
|
+
await augmentWithInsights(intent, classification, env);
|
|
190
|
+
const grounding = await augmentWithEntityGrounding(intent, classification, env);
|
|
191
|
+
await augmentWithMemoryRecall(intent, classification, env);
|
|
196
192
|
|
|
197
193
|
// 1.6. Implicit feedback — retrograde previous episode on user correction
|
|
198
194
|
if (classification === 'user_correction' && intent.source.threadId) {
|
|
@@ -221,7 +217,7 @@ async function augmentIntent(intent: KernelIntent, env: EdgeEnv): Promise<Augmen
|
|
|
221
217
|
}
|
|
222
218
|
}
|
|
223
219
|
|
|
224
|
-
return { plan, nearMiss, reclassified, classification, procKey, existingProcedure };
|
|
220
|
+
return { plan, nearMiss, reclassified, classification, procKey, existingProcedure, grounding };
|
|
225
221
|
}
|
|
226
222
|
|
|
227
223
|
// ─── Self-Improvement ACK ────────────────────────────────────
|
|
@@ -275,7 +271,6 @@ interface ExecuteResult {
|
|
|
275
271
|
meta?: unknown;
|
|
276
272
|
outcome: 'success' | 'failure' | 'partial_failure';
|
|
277
273
|
probeResult?: 'agreed' | 'split' | 'escalated';
|
|
278
|
-
earlyReturn?: DispatchResult; // probe agreed → skip executor
|
|
279
274
|
}
|
|
280
275
|
|
|
281
276
|
async function probeAndExecute(
|
|
@@ -302,11 +297,8 @@ async function probeAndExecute(
|
|
|
302
297
|
probeResult = 'agreed';
|
|
303
298
|
plan.executor = 'groq';
|
|
304
299
|
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
300
|
return {
|
|
308
301
|
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
302
|
};
|
|
311
303
|
} else if (probe.sigma === 1.0) {
|
|
312
304
|
const prev = plan.executor;
|
|
@@ -489,7 +481,7 @@ function buildResult(
|
|
|
489
481
|
exec: ExecuteResult,
|
|
490
482
|
latencyMs: number,
|
|
491
483
|
): DispatchResult {
|
|
492
|
-
|
|
484
|
+
const result: DispatchResult = {
|
|
493
485
|
text: exec.text,
|
|
494
486
|
executor: ctx.plan.executor,
|
|
495
487
|
cost: exec.cost,
|
|
@@ -501,6 +493,29 @@ function buildResult(
|
|
|
501
493
|
probeResult: exec.probeResult,
|
|
502
494
|
meta: exec.meta,
|
|
503
495
|
};
|
|
496
|
+
if (ctx.grounding) {
|
|
497
|
+
result.grounded = ctx.grounding.grounded;
|
|
498
|
+
result.sources = ctx.grounding.sources;
|
|
499
|
+
result.unknowns = ctx.grounding.unknowns;
|
|
500
|
+
result.searched = ctx.grounding.searched;
|
|
501
|
+
}
|
|
502
|
+
return result;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async function finalizeGrounding(
|
|
506
|
+
ctx: AugmentedContext,
|
|
507
|
+
intent: KernelIntent,
|
|
508
|
+
env: EdgeEnv,
|
|
509
|
+
exec: ExecuteResult,
|
|
510
|
+
latencyMs: number,
|
|
511
|
+
): Promise<{ result: DispatchResult; outcome: 'success' | 'failure' | 'partial_failure'; groundingGap: boolean }> {
|
|
512
|
+
const result = buildResult(ctx, intent, exec, latencyMs);
|
|
513
|
+
await applyFabricationCheck(result, exec.text, env);
|
|
514
|
+
const outcome = applyGroundingProof(exec.outcome, ctx.classification, result);
|
|
515
|
+
const groundingGap =
|
|
516
|
+
(result.unverified_claims?.length ?? 0) > 0 ||
|
|
517
|
+
(result.unknowns?.length ?? 0) > 0;
|
|
518
|
+
return { result, outcome, groundingGap };
|
|
504
519
|
}
|
|
505
520
|
|
|
506
521
|
// ─── Main Dispatch Loop ──────────────────────────────────────
|
|
@@ -514,10 +529,10 @@ export async function dispatch(intent: KernelIntent, env: EdgeEnv): Promise<Disp
|
|
|
514
529
|
if (ack) return ack;
|
|
515
530
|
|
|
516
531
|
const exec = await probeAndExecute(ctx, intent, env, startMs);
|
|
517
|
-
if (exec.earlyReturn) return exec.earlyReturn;
|
|
518
|
-
|
|
519
532
|
const latencyMs = Date.now() - startMs;
|
|
520
|
-
|
|
533
|
+
const { result, outcome, groundingGap } = await finalizeGrounding(ctx, intent, env, exec, latencyMs);
|
|
534
|
+
await recordOutcome(env, intent, ctx.classification, ctx.procKey, ctx.plan, ctx.existingProcedure, exec.text, exec.cost, latencyMs, ctx.nearMiss, outcome, groundingGap, ctx.reclassified);
|
|
535
|
+
await applyGapSignal(result, ctx.procKey, ctx.classification, env);
|
|
521
536
|
|
|
522
537
|
// Background: shadow exploration + shadow read
|
|
523
538
|
if (env.ctx) {
|
|
@@ -533,7 +548,7 @@ export async function dispatch(intent: KernelIntent, env: EdgeEnv): Promise<Disp
|
|
|
533
548
|
}
|
|
534
549
|
}
|
|
535
550
|
|
|
536
|
-
return
|
|
551
|
+
return result;
|
|
537
552
|
}
|
|
538
553
|
|
|
539
554
|
// ─── Streaming Dispatch ──────────────────────────────────────
|
|
@@ -547,10 +562,10 @@ export async function dispatchStream(
|
|
|
547
562
|
const ctx = await augmentIntent(intent, env);
|
|
548
563
|
|
|
549
564
|
const exec = await probeAndExecute(ctx, intent, env, startMs, onDelta);
|
|
550
|
-
if (exec.earlyReturn) return exec.earlyReturn;
|
|
551
|
-
|
|
552
565
|
const latencyMs = Date.now() - startMs;
|
|
553
|
-
|
|
566
|
+
const { result, outcome, groundingGap } = await finalizeGrounding(ctx, intent, env, exec, latencyMs);
|
|
567
|
+
await recordOutcome(env, intent, ctx.classification, ctx.procKey, ctx.plan, ctx.existingProcedure, exec.text, exec.cost, latencyMs, ctx.nearMiss, outcome, groundingGap, ctx.reclassified);
|
|
568
|
+
await applyGapSignal(result, ctx.procKey, ctx.classification, env);
|
|
554
569
|
|
|
555
570
|
// Background: shadow exploration + shadow read
|
|
556
571
|
if (env.ctx) {
|
|
@@ -566,5 +581,5 @@ export async function dispatchStream(
|
|
|
566
581
|
}
|
|
567
582
|
}
|
|
568
583
|
|
|
569
|
-
return
|
|
584
|
+
return result;
|
|
570
585
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// No eval(). No code execution. Just parameterized prompts.
|
|
4
4
|
|
|
5
5
|
import { type EdgeEnv } from './dispatch.js';
|
|
6
|
+
import { buildLLMProviderFactory } from './provider-factory.js';
|
|
6
7
|
import type { ToolExecutor, ToolStatus } from '../schema-enums.js';
|
|
7
8
|
|
|
8
9
|
// ─── Types ──────────────────────────────────────────────────
|
|
@@ -160,58 +161,20 @@ export async function executeDynamicTool(
|
|
|
160
161
|
): Promise<ToolInvocationResult> {
|
|
161
162
|
const rendered = renderPrompt(tool.prompt_template, inputs);
|
|
162
163
|
const start = Date.now();
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} else if (tool.executor === 'groq' && env.groqApiKey) {
|
|
178
|
-
// Groq — fast, cheap
|
|
179
|
-
const res = await fetch('https://api.groq.com/openai/v1/chat/completions', {
|
|
180
|
-
method: 'POST',
|
|
181
|
-
headers: {
|
|
182
|
-
'Authorization': `Bearer ${env.groqApiKey}`,
|
|
183
|
-
'Content-Type': 'application/json',
|
|
184
|
-
},
|
|
185
|
-
body: JSON.stringify({
|
|
186
|
-
model: env.groqModel ?? 'llama-3.3-70b-versatile',
|
|
187
|
-
messages: [
|
|
188
|
-
{ role: 'system', content: 'You are a focused tool. Answer precisely. No preamble.' },
|
|
189
|
-
{ role: 'user', content: rendered },
|
|
190
|
-
],
|
|
191
|
-
max_tokens: 1024,
|
|
192
|
-
}),
|
|
193
|
-
signal: AbortSignal.timeout(15_000),
|
|
194
|
-
});
|
|
195
|
-
if (!res.ok) throw new Error(`Groq error: ${res.status}`);
|
|
196
|
-
const data = await res.json() as { choices: Array<{ message: { content: string } }> };
|
|
197
|
-
text = data.choices[0]?.message?.content ?? '';
|
|
198
|
-
cost = 0.001; // ~$0.001 per Groq call
|
|
199
|
-
} else {
|
|
200
|
-
// Default: Workers AI fallback (always available on CF Workers)
|
|
201
|
-
if (env.ai) {
|
|
202
|
-
const result = await env.ai.run('@cf/meta/llama-3.1-8b-instruct' as Parameters<Ai['run']>[0], {
|
|
203
|
-
messages: [
|
|
204
|
-
{ role: 'system', content: 'You are a focused tool. Answer precisely. No preamble.' },
|
|
205
|
-
{ role: 'user', content: rendered },
|
|
206
|
-
],
|
|
207
|
-
max_tokens: 1024,
|
|
208
|
-
}) as { response?: string };
|
|
209
|
-
text = result.response ?? '';
|
|
210
|
-
cost = 0;
|
|
211
|
-
} else {
|
|
212
|
-
throw new Error(`Executor "${tool.executor}" not available — no API key or binding`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
164
|
+
const executor = tool.executor as ToolExecutor;
|
|
165
|
+
const model = resolveDynamicToolModel(executor, env);
|
|
166
|
+
const factory = buildLLMProviderFactory(env);
|
|
167
|
+
const result = await factory.generateResponse({
|
|
168
|
+
model,
|
|
169
|
+
messages: [
|
|
170
|
+
{ role: 'system', content: 'You are a focused tool. Answer precisely. No preamble.' },
|
|
171
|
+
{ role: 'user', content: rendered },
|
|
172
|
+
],
|
|
173
|
+
maxTokens: 1024,
|
|
174
|
+
temperature: 0.2,
|
|
175
|
+
});
|
|
176
|
+
const text = result.message ?? '';
|
|
177
|
+
const cost = result.usage.cost;
|
|
215
178
|
|
|
216
179
|
const latencyMs = Date.now() - start;
|
|
217
180
|
|
|
@@ -229,6 +192,21 @@ export async function executeDynamicTool(
|
|
|
229
192
|
return { text, cost, latency_ms: latencyMs, executor: tool.executor };
|
|
230
193
|
}
|
|
231
194
|
|
|
195
|
+
function resolveDynamicToolModel(executor: ToolExecutor, env: EdgeEnv): string {
|
|
196
|
+
if (executor === 'workers_ai') {
|
|
197
|
+
if (!env.ai) throw new Error('Executor "workers_ai" not available — no AI binding');
|
|
198
|
+
return '@cf/meta/llama-3.1-8b-instruct';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (executor === 'groq') {
|
|
202
|
+
if (!env.groqApiKey) throw new Error('Executor "groq" not available — no Groq API key');
|
|
203
|
+
return env.groqModel ?? 'llama-3.3-70b-versatile';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!env.ai) throw new Error(`Executor "${executor}" not available — no AI binding`);
|
|
207
|
+
return env.gptOssModel;
|
|
208
|
+
}
|
|
209
|
+
|
|
232
210
|
// ─── Lifecycle (GC + Promotion) ─────────────────────────────
|
|
233
211
|
|
|
234
212
|
const MAX_ACTIVE_TOOLS = 50;
|
|
File without changes
|
|
File without changes
|
|
@@ -90,6 +90,20 @@ export async function executeDirect(
|
|
|
90
90
|
intent: KernelIntent,
|
|
91
91
|
env: EdgeEnv,
|
|
92
92
|
): Promise<{ text: string; cost: number; meta?: unknown }> {
|
|
93
|
+
if (intent.classified === 'request_clarification') {
|
|
94
|
+
const question = intent.disambiguation?.question
|
|
95
|
+
?? 'This data concept is ambiguous. Which exact definition should I use?';
|
|
96
|
+
return {
|
|
97
|
+
text: question,
|
|
98
|
+
cost: 0,
|
|
99
|
+
meta: {
|
|
100
|
+
clarificationRequired: true,
|
|
101
|
+
concept: intent.disambiguation?.concept ?? 'unknown',
|
|
102
|
+
source: 'disambiguation_firewall',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
if (intent.classified === 'heartbeat') {
|
|
94
108
|
// Edge heartbeat: call BizOps dashboard + CF infrastructure in parallel, triage with structured JSON
|
|
95
109
|
const mcpClient = new McpClient({
|