@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,497 @@
|
|
|
1
|
+
export interface TaskPreflight {
|
|
2
|
+
repo?: string;
|
|
3
|
+
repo_exists?: boolean;
|
|
4
|
+
repo_path?: string | null;
|
|
5
|
+
git_repo?: boolean;
|
|
6
|
+
authority?: string;
|
|
7
|
+
category?: string;
|
|
8
|
+
base_branch?: string | null;
|
|
9
|
+
test_command?: string | null;
|
|
10
|
+
warnings?: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TaskFailureAutopsy {
|
|
14
|
+
kind: string;
|
|
15
|
+
retryable: boolean;
|
|
16
|
+
summary: string;
|
|
17
|
+
recommended_action: string;
|
|
18
|
+
signals: string[];
|
|
19
|
+
system_contract?: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface FailureInput {
|
|
23
|
+
title?: string | null;
|
|
24
|
+
repo?: string | null;
|
|
25
|
+
category?: string | null;
|
|
26
|
+
error?: string | null;
|
|
27
|
+
result?: string | null;
|
|
28
|
+
exitCode?: number | null;
|
|
29
|
+
preflight?: TaskPreflight | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface FailureRowLike {
|
|
33
|
+
id?: string;
|
|
34
|
+
repo?: string;
|
|
35
|
+
completed_at?: string | null;
|
|
36
|
+
autopsy_json?: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseJsonObject<T>(raw: string | Record<string, unknown> | null | undefined): T | null {
|
|
40
|
+
if (!raw) return null;
|
|
41
|
+
if (typeof raw === 'string') {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(raw) as T;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (typeof raw === 'object') return raw as T;
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createAutopsy(
|
|
53
|
+
kind: string,
|
|
54
|
+
retryable: boolean,
|
|
55
|
+
summary: string,
|
|
56
|
+
recommendedAction: string,
|
|
57
|
+
signals: string[],
|
|
58
|
+
systemContract?: string | null,
|
|
59
|
+
): TaskFailureAutopsy {
|
|
60
|
+
return {
|
|
61
|
+
kind,
|
|
62
|
+
retryable,
|
|
63
|
+
summary,
|
|
64
|
+
recommended_action: recommendedAction,
|
|
65
|
+
signals,
|
|
66
|
+
system_contract: systemContract ?? null,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function parseTaskPreflight(raw: string | Record<string, unknown> | null | undefined): TaskPreflight | null {
|
|
71
|
+
return parseJsonObject<TaskPreflight>(raw);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function parseTaskAutopsy(raw: string | Record<string, unknown> | null | undefined): TaskFailureAutopsy | null {
|
|
75
|
+
return parseJsonObject<TaskFailureAutopsy>(raw);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Environment failure patterns — tool/dependency/infra issues that exit code 3 can mask
|
|
79
|
+
const ENVIRONMENT_FAILURE_PATTERNS = [
|
|
80
|
+
/npm\s+(err|error|warn).*install/i,
|
|
81
|
+
/enoent.*npm/i,
|
|
82
|
+
/cannot find module/i,
|
|
83
|
+
/module not found/i,
|
|
84
|
+
/permission denied/i,
|
|
85
|
+
/eacces/i,
|
|
86
|
+
/network\s+(error|timeout|unreachable)/i,
|
|
87
|
+
/connection\s+(refused|reset|timed?\s*out)/i,
|
|
88
|
+
/no\s+such\s+file\s+or\s+directory/i,
|
|
89
|
+
/spawn\s+\S+\s+enoent/i,
|
|
90
|
+
/command\s+failed.*install/i,
|
|
91
|
+
/failed\s+to\s+(fetch|download|install)/i,
|
|
92
|
+
/dependency\s+(resolution|install)\s+fail/i,
|
|
93
|
+
/exit\s+code\s+1.*npm\s+install/i,
|
|
94
|
+
/errno\s+\d+/i,
|
|
95
|
+
/segmentation\s+fault/i,
|
|
96
|
+
/out\s+of\s+memory/i,
|
|
97
|
+
/disk\s+(full|space)/i,
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
function isEnvironmentFailure(haystack: string): boolean {
|
|
101
|
+
return ENVIRONMENT_FAILURE_PATTERNS.some(p => p.test(haystack));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function classifyTaskFailure(input: FailureInput): TaskFailureAutopsy {
|
|
105
|
+
const warnings = input.preflight?.warnings ?? [];
|
|
106
|
+
const signals = [
|
|
107
|
+
input.error ? `error:${input.error}` : '',
|
|
108
|
+
input.result ? `result:${input.result.slice(0, 240)}` : '',
|
|
109
|
+
...warnings.map(w => `preflight:${w}`),
|
|
110
|
+
].filter(Boolean);
|
|
111
|
+
|
|
112
|
+
const haystack = [
|
|
113
|
+
input.title,
|
|
114
|
+
input.repo,
|
|
115
|
+
input.category,
|
|
116
|
+
input.error,
|
|
117
|
+
input.result,
|
|
118
|
+
warnings.join(' '),
|
|
119
|
+
input.preflight?.test_command ?? '',
|
|
120
|
+
].filter(Boolean).join(' ').toLowerCase();
|
|
121
|
+
|
|
122
|
+
if (haystack.includes('repo not found') || input.preflight?.repo_exists === false) {
|
|
123
|
+
return createAutopsy(
|
|
124
|
+
'repo_missing',
|
|
125
|
+
false,
|
|
126
|
+
`Task targeted a repo that is unavailable on this runner${input.repo ? ` (${input.repo})` : ''}.`,
|
|
127
|
+
'Fix the repo alias/path or create/sync the target repo before retrying.',
|
|
128
|
+
signals,
|
|
129
|
+
'repo_target_missing',
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (haystack.includes('locked by another runner')) {
|
|
134
|
+
return createAutopsy(
|
|
135
|
+
'repo_locked',
|
|
136
|
+
true,
|
|
137
|
+
`Task could not acquire the repo lock${input.repo ? ` for ${input.repo}` : ''}.`,
|
|
138
|
+
'Retry later or reduce concurrent automation against the same repo.',
|
|
139
|
+
signals,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (haystack.includes('could not determine base branch')) {
|
|
144
|
+
return createAutopsy(
|
|
145
|
+
'base_branch_unknown',
|
|
146
|
+
false,
|
|
147
|
+
'Task could not determine the repository base branch for worktree isolation.',
|
|
148
|
+
'Set or repair the default branch or remote HEAD before retrying autonomous work.',
|
|
149
|
+
signals,
|
|
150
|
+
'repo_branch_contract_unknown',
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (
|
|
155
|
+
(haystack.includes('exists on remote') && haystack.includes('open pr')) ||
|
|
156
|
+
(haystack.includes('branch') && haystack.includes('already exists') && !haystack.includes('repo not found'))
|
|
157
|
+
) {
|
|
158
|
+
return createAutopsy(
|
|
159
|
+
'branch_conflict',
|
|
160
|
+
true,
|
|
161
|
+
'Task branch already exists on the remote from a prior run. The taskrunner now auto-closes stale PRs and cleans up branches on retry.',
|
|
162
|
+
'Retry the task — the taskrunner will clean up the stale branch automatically.',
|
|
163
|
+
signals,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (haystack.includes('missing script: "test"') || haystack.includes("missing script: 'test'")) {
|
|
168
|
+
return createAutopsy(
|
|
169
|
+
'test_command_missing',
|
|
170
|
+
false,
|
|
171
|
+
'Task attempted to run a test command that the repo does not expose.',
|
|
172
|
+
'Point the task at the correct test surface or add an explicit verification command to the task prompt.',
|
|
173
|
+
signals,
|
|
174
|
+
'repo_test_surface_missing',
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (input.category === 'tests' && warnings.some(w => w.toLowerCase().includes('no obvious test command detected'))) {
|
|
179
|
+
return createAutopsy(
|
|
180
|
+
'test_command_missing',
|
|
181
|
+
false,
|
|
182
|
+
'Test task was queued without an obvious verification command in the repo surface.',
|
|
183
|
+
'Add or specify the correct test runner before retrying the task.',
|
|
184
|
+
signals,
|
|
185
|
+
'repo_test_surface_missing',
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Credit/billing exhaustion — runner hit API spend limits
|
|
190
|
+
if (
|
|
191
|
+
haystack.includes('credit balance') ||
|
|
192
|
+
haystack.includes('credit limit') ||
|
|
193
|
+
haystack.includes('insufficient credits') ||
|
|
194
|
+
haystack.includes('billing') ||
|
|
195
|
+
haystack.includes('payment required') ||
|
|
196
|
+
haystack.includes('rate limit') && haystack.includes('credit')
|
|
197
|
+
) {
|
|
198
|
+
return createAutopsy(
|
|
199
|
+
'credit_exhausted',
|
|
200
|
+
false,
|
|
201
|
+
'Task failed because the LLM provider credit balance was exhausted or billing limit was reached.',
|
|
202
|
+
'Top up credits or adjust the runner configuration (e.g. switch from --bare API to Claude Code OAuth).',
|
|
203
|
+
signals,
|
|
204
|
+
'runner_credit_exhausted',
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Authentication failures — invalid or expired API keys/tokens
|
|
209
|
+
if (
|
|
210
|
+
(haystack.includes('unauthorized') || haystack.includes('401') || haystack.includes('authentication failed') || haystack.includes('api key') && haystack.includes('invalid')) &&
|
|
211
|
+
!haystack.includes('repo') // avoid false matches on repo auth
|
|
212
|
+
) {
|
|
213
|
+
return createAutopsy(
|
|
214
|
+
'auth_failure',
|
|
215
|
+
false,
|
|
216
|
+
'Task failed due to an authentication or authorization error with an external service.',
|
|
217
|
+
'Check and rotate the relevant API key or token, then retry.',
|
|
218
|
+
signals,
|
|
219
|
+
'runner_auth_degraded',
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (input.exitCode === 127 || haystack.includes('command not found')) {
|
|
224
|
+
return createAutopsy(
|
|
225
|
+
'command_missing',
|
|
226
|
+
false,
|
|
227
|
+
'Task failed because a required command was unavailable in the runner environment.',
|
|
228
|
+
'Install the missing tool or change the task to use commands already available on the runner.',
|
|
229
|
+
signals,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
(haystack.includes('404') || haystack.includes('no route')) &&
|
|
235
|
+
(haystack.includes('draft') || haystack.includes('unpublished') || haystack.includes('post'))
|
|
236
|
+
) {
|
|
237
|
+
return createAutopsy(
|
|
238
|
+
'route_contract_gap',
|
|
239
|
+
false,
|
|
240
|
+
'Task surfaced a content contract mismatch: draft or unpublished content was treated as publicly reachable.',
|
|
241
|
+
'Repair the route or API contract so drafts use a non-public preview or edit flow instead of a live URL.',
|
|
242
|
+
signals,
|
|
243
|
+
'content_public_route_drift',
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Exit code 3 with environment failure signals → environment_failure (not retryable)
|
|
248
|
+
// These are tool/dependency/infra issues, not missing completion signals.
|
|
249
|
+
// Real examples: npm install failures, missing CLI tools, network timeouts.
|
|
250
|
+
if (input.exitCode === 3 && isEnvironmentFailure(haystack)) {
|
|
251
|
+
return createAutopsy(
|
|
252
|
+
'environment_failure',
|
|
253
|
+
false,
|
|
254
|
+
'Task failed due to an environment or tool-availability issue on the runner.',
|
|
255
|
+
'Investigate the runner environment: check tool versions, network access, and dependency availability before retrying.',
|
|
256
|
+
signals,
|
|
257
|
+
'runner_environment_degraded',
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// max_turns_exceeded — Claude hit the turn limit before completing.
|
|
262
|
+
// This is retryable (with higher max_turns or a simpler task scope).
|
|
263
|
+
// Must come before completion_signal_missing since both can have exit code 3,
|
|
264
|
+
// but max_turns is a distinct, actionable failure with a clear fix.
|
|
265
|
+
if (haystack.includes('max_turns') || haystack.includes('error_max_turns') || haystack.includes('ran out of turns')) {
|
|
266
|
+
const hasPr = haystack.includes('[taskrunner] pr:') || haystack.includes('pr created') || haystack.includes('pull request');
|
|
267
|
+
return createAutopsy(
|
|
268
|
+
'max_turns_exceeded',
|
|
269
|
+
true,
|
|
270
|
+
hasPr
|
|
271
|
+
? 'Task hit the turn limit but created a PR — work was likely completed, signal was not emitted before timeout.'
|
|
272
|
+
: 'Task hit the turn limit before completing. Claude ran out of turns without emitting a completion signal.',
|
|
273
|
+
hasPr
|
|
274
|
+
? 'Review the PR — task may be complete. If so, mark as success. Otherwise, retry with higher max_turns or split the task.'
|
|
275
|
+
: 'Retry with higher max_turns (current limit may be too low for the task scope) or split into smaller subtasks.',
|
|
276
|
+
signals,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Hallucinated task — agent determined the target doesn't exist
|
|
281
|
+
if (
|
|
282
|
+
haystack.includes("doesn't exist") && haystack.includes('hallucinated') ||
|
|
283
|
+
haystack.includes('does not exist') && (haystack.includes('dreaming') || haystack.includes('self-improvement')) ||
|
|
284
|
+
haystack.includes('code that doesn\'t exist')
|
|
285
|
+
) {
|
|
286
|
+
return createAutopsy(
|
|
287
|
+
'hallucinated_task',
|
|
288
|
+
false,
|
|
289
|
+
'Task referenced code or components that do not exist — likely generated by dreaming/self-improvement without verification.',
|
|
290
|
+
'Improve task source (dreaming/self-improvement) to verify targets exist before queuing.',
|
|
291
|
+
signals,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// "Nothing to do" — agent determined work was already done but didn't signal completion
|
|
296
|
+
if (
|
|
297
|
+
haystack.includes('already resolved') ||
|
|
298
|
+
haystack.includes('already complete') ||
|
|
299
|
+
haystack.includes('already confirmed') ||
|
|
300
|
+
haystack.includes('already processed') ||
|
|
301
|
+
haystack.includes('nothing to do') ||
|
|
302
|
+
haystack.includes('no action needed')
|
|
303
|
+
) {
|
|
304
|
+
return createAutopsy(
|
|
305
|
+
'work_already_done',
|
|
306
|
+
false,
|
|
307
|
+
'Agent determined the work was already completed or unnecessary, but did not emit a completion signal.',
|
|
308
|
+
'Task should be marked as success — the agent correctly identified no work was needed. Consider improving the taskrunner to recognize "already done" as a valid completion.',
|
|
309
|
+
signals,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (haystack.includes('completion signal not found') || input.exitCode === 3) {
|
|
314
|
+
return createAutopsy(
|
|
315
|
+
'completion_signal_missing',
|
|
316
|
+
false,
|
|
317
|
+
'Task exited without proving completion via the expected completion signal.',
|
|
318
|
+
'Tighten the task prompt or completion signal so autonomous runs can verify success deterministically.',
|
|
319
|
+
signals,
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return createAutopsy(
|
|
324
|
+
'generic_task_failure',
|
|
325
|
+
false,
|
|
326
|
+
'Task failed without a more specific classifier match.',
|
|
327
|
+
'Review the captured result or error output and decide whether to retry, rescope, or escalate.',
|
|
328
|
+
signals,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ─── PR Utility Scoring (#289) ──────────────────────────────
|
|
333
|
+
// Two scores for completed autonomous tasks:
|
|
334
|
+
// - Impact: functional value relative to size (0-1)
|
|
335
|
+
// - Novelty: does this address a new constraint or re-churn? (0-1)
|
|
336
|
+
|
|
337
|
+
export interface TaskUtilityScore {
|
|
338
|
+
impact: number; // 0-1: functional value of the change
|
|
339
|
+
novelty: number; // 0-1: responds to new constraint vs re-churn
|
|
340
|
+
signals: string[]; // human-readable scoring rationale
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Categories with inherently high/low impact
|
|
344
|
+
const HIGH_IMPACT_CATEGORIES = new Set(['bugfix', 'feature']);
|
|
345
|
+
const LOW_IMPACT_CATEGORIES = new Set(['docs', 'refactor']);
|
|
346
|
+
|
|
347
|
+
// Patterns in result text that signal real work
|
|
348
|
+
const IMPACT_POSITIVE_PATTERNS = [
|
|
349
|
+
/fix(?:ed|es)?\s+(?:bug|crash|error|issue|race|leak)/i,
|
|
350
|
+
/add(?:ed|s)?\s+(?:endpoint|route|handler|table|column|feature)/i,
|
|
351
|
+
/implement(?:ed|s)?\s/i,
|
|
352
|
+
/resolv(?:ed|es)?\s+#?\d+/i,
|
|
353
|
+
/pr.*created|pull request/i,
|
|
354
|
+
/test.*pass/i,
|
|
355
|
+
];
|
|
356
|
+
|
|
357
|
+
// Patterns that signal low-value churn
|
|
358
|
+
const CHURN_PATTERNS = [
|
|
359
|
+
/updat(?:ed|es?)?\s+(?:comment|docstring|readme|changelog)/i,
|
|
360
|
+
/renam(?:ed|es?)?\s/i,
|
|
361
|
+
/reformat|lint|style|whitespace/i,
|
|
362
|
+
/no\s+(?:functional|behavioral)\s+change/i,
|
|
363
|
+
/minor\s+cleanup/i,
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
export function scoreTaskUtility(input: {
|
|
367
|
+
title: string;
|
|
368
|
+
category: string;
|
|
369
|
+
result: string | null;
|
|
370
|
+
created_by: string;
|
|
371
|
+
pr_url: string | null;
|
|
372
|
+
github_issue_number: number | null;
|
|
373
|
+
recentAutoTitles: string[]; // titles of recent autonomous tasks for novelty check
|
|
374
|
+
}): TaskUtilityScore {
|
|
375
|
+
const signals: string[] = [];
|
|
376
|
+
let impact = 0.5; // baseline
|
|
377
|
+
let novelty = 0.5;
|
|
378
|
+
|
|
379
|
+
const resultText = (input.result ?? '').toLowerCase();
|
|
380
|
+
const titleText = input.title.toLowerCase();
|
|
381
|
+
const combined = `${titleText} ${resultText}`;
|
|
382
|
+
|
|
383
|
+
// ── Impact scoring ─────────────────────────────────────────
|
|
384
|
+
if (HIGH_IMPACT_CATEGORIES.has(input.category)) {
|
|
385
|
+
impact += 0.15;
|
|
386
|
+
signals.push(`category:${input.category} (+impact)`);
|
|
387
|
+
} else if (LOW_IMPACT_CATEGORIES.has(input.category)) {
|
|
388
|
+
impact -= 0.1;
|
|
389
|
+
signals.push(`category:${input.category} (-impact)`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Issue-linked = responding to a real problem
|
|
393
|
+
if (input.github_issue_number) {
|
|
394
|
+
impact += 0.15;
|
|
395
|
+
novelty += 0.2;
|
|
396
|
+
signals.push(`linked:issue#${input.github_issue_number} (+impact,+novelty)`);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// PR created = shipped work
|
|
400
|
+
if (input.pr_url) {
|
|
401
|
+
impact += 0.1;
|
|
402
|
+
signals.push('pr_created (+impact)');
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Positive patterns in result
|
|
406
|
+
for (const pattern of IMPACT_POSITIVE_PATTERNS) {
|
|
407
|
+
if (pattern.test(combined)) {
|
|
408
|
+
impact += 0.05;
|
|
409
|
+
signals.push(`pattern:${pattern.source.slice(0, 30)} (+impact)`);
|
|
410
|
+
break; // only count one positive pattern
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Churn patterns in result
|
|
415
|
+
for (const pattern of CHURN_PATTERNS) {
|
|
416
|
+
if (pattern.test(combined)) {
|
|
417
|
+
impact -= 0.15;
|
|
418
|
+
novelty -= 0.2;
|
|
419
|
+
signals.push(`churn:${pattern.source.slice(0, 30)} (-impact,-novelty)`);
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Result is just "TASK_COMPLETE" with no detail = likely low substance
|
|
425
|
+
if (resultText.trim() === 'task_complete') {
|
|
426
|
+
impact -= 0.1;
|
|
427
|
+
signals.push('bare_completion_signal (-impact)');
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ── Novelty scoring ────────────────────────────────────────
|
|
431
|
+
// Check if this task's title overlaps with recent autonomous work
|
|
432
|
+
if (input.recentAutoTitles.length > 0) {
|
|
433
|
+
const titleWords = new Set(
|
|
434
|
+
titleText.replace(/[^a-z0-9\s]/g, ' ').split(/\s+/).filter(w => w.length > 3),
|
|
435
|
+
);
|
|
436
|
+
for (const recentTitle of input.recentAutoTitles) {
|
|
437
|
+
const recentWords = recentTitle.toLowerCase().replace(/[^a-z0-9\s]/g, ' ').split(/\s+/).filter(w => w.length > 3);
|
|
438
|
+
const overlap = recentWords.filter(w => titleWords.has(w)).length;
|
|
439
|
+
const overlapRatio = titleWords.size > 0 ? overlap / titleWords.size : 0;
|
|
440
|
+
if (overlapRatio > 0.4) {
|
|
441
|
+
novelty -= 0.3;
|
|
442
|
+
signals.push(`title_overlap:${overlapRatio.toFixed(2)} with "${recentTitle.slice(0, 40)}" (-novelty)`);
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Operator-created tasks are inherently novel (responding to real need)
|
|
449
|
+
if (input.created_by === 'operator') {
|
|
450
|
+
novelty += 0.2;
|
|
451
|
+
signals.push('operator_created (+novelty)');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Clamp to [0, 1]
|
|
455
|
+
impact = Math.max(0, Math.min(1, impact));
|
|
456
|
+
novelty = Math.max(0, Math.min(1, novelty));
|
|
457
|
+
|
|
458
|
+
return {
|
|
459
|
+
impact: Math.round(impact * 100) / 100,
|
|
460
|
+
novelty: Math.round(novelty * 100) / 100,
|
|
461
|
+
signals,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export function collectContractAlerts(rows: FailureRowLike[]): Array<{
|
|
466
|
+
contract: string;
|
|
467
|
+
repo: string;
|
|
468
|
+
task_id: string;
|
|
469
|
+
summary: string;
|
|
470
|
+
completed_at: string | null;
|
|
471
|
+
}> {
|
|
472
|
+
const alerts: Array<{
|
|
473
|
+
contract: string;
|
|
474
|
+
repo: string;
|
|
475
|
+
task_id: string;
|
|
476
|
+
summary: string;
|
|
477
|
+
completed_at: string | null;
|
|
478
|
+
}> = [];
|
|
479
|
+
const seen = new Set<string>();
|
|
480
|
+
|
|
481
|
+
for (const row of rows) {
|
|
482
|
+
const autopsy = parseTaskAutopsy(row.autopsy_json);
|
|
483
|
+
if (!autopsy?.system_contract) continue;
|
|
484
|
+
const key = `${autopsy.system_contract}:${row.repo ?? ''}`;
|
|
485
|
+
if (seen.has(key)) continue;
|
|
486
|
+
seen.add(key);
|
|
487
|
+
alerts.push({
|
|
488
|
+
contract: autopsy.system_contract,
|
|
489
|
+
repo: row.repo ?? '',
|
|
490
|
+
task_id: row.id ?? '',
|
|
491
|
+
summary: autopsy.summary,
|
|
492
|
+
completed_at: row.completed_at ?? null,
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return alerts;
|
|
497
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import type { OAuthHelpers } from '@cloudflare/workers-oauth-provider';
|
|
2
|
+
|
|
3
|
+
export interface Env {
|
|
4
|
+
DB: D1Database;
|
|
5
|
+
AI: Ai;
|
|
6
|
+
AEGIS_TOKEN: string;
|
|
7
|
+
|
|
8
|
+
// OAuth 2.1 (injected by OAuthProvider wrapper at runtime)
|
|
9
|
+
OAUTH_PROVIDER: OAuthHelpers;
|
|
10
|
+
OAUTH_KV: KVNamespace;
|
|
11
|
+
AEGIS_ENV: string;
|
|
12
|
+
|
|
13
|
+
// Anthropic (Claude executor)
|
|
14
|
+
ANTHROPIC_API_KEY: string;
|
|
15
|
+
CLAUDE_MODEL: string;
|
|
16
|
+
CLAUDE_OPUS_MODEL: string;
|
|
17
|
+
|
|
18
|
+
// GPT-OSS (standard executor — tool-capable, cheap)
|
|
19
|
+
GPT_OSS_MODEL: string;
|
|
20
|
+
|
|
21
|
+
// Groq (classification + greeting executor + composite orchestration)
|
|
22
|
+
GROQ_API_KEY: string;
|
|
23
|
+
GROQ_MODEL: string;
|
|
24
|
+
GROQ_RESPONSE_MODEL: string;
|
|
25
|
+
GROQ_GPT_OSS_MODEL: string;
|
|
26
|
+
|
|
27
|
+
// AI Gateway (observability, caching, rate limiting)
|
|
28
|
+
AI_GATEWAY_ID: string;
|
|
29
|
+
CF_ACCOUNT_ID: string;
|
|
30
|
+
|
|
31
|
+
// BizOps MCP — Service Binding (Worker-to-Worker)
|
|
32
|
+
BIZOPS: Fetcher;
|
|
33
|
+
BIZOPS_TOKEN: string;
|
|
34
|
+
|
|
35
|
+
// Resend (proactive email alerts)
|
|
36
|
+
RESEND_API_KEY: string;
|
|
37
|
+
RESEND_API_KEY_PERSONAL: string;
|
|
38
|
+
|
|
39
|
+
// GitHub (self-improvement analysis)
|
|
40
|
+
GITHUB_TOKEN: string;
|
|
41
|
+
GITHUB_REPO: string;
|
|
42
|
+
|
|
43
|
+
// Brave Search (web research)
|
|
44
|
+
BRAVE_API_KEY: string;
|
|
45
|
+
|
|
46
|
+
// Bluesky AT Protocol (content distribution)
|
|
47
|
+
BLUESKY_HANDLE: string;
|
|
48
|
+
BLUESKY_APP_PASSWORD: string;
|
|
49
|
+
|
|
50
|
+
// Proactive notifications (Stage 2)
|
|
51
|
+
AEGIS_NOTIFY_EMAIL: string;
|
|
52
|
+
AEGIS_BASE_URL: string;
|
|
53
|
+
|
|
54
|
+
// Roundtable content platform (shared D1 binding)
|
|
55
|
+
ROUNDTABLE_DB: D1Database;
|
|
56
|
+
|
|
57
|
+
// Cloudflare Observability (infrastructure monitoring)
|
|
58
|
+
CF_ANALYTICS_TOKEN: string;
|
|
59
|
+
|
|
60
|
+
// img-forge (hero image generation via Service Binding)
|
|
61
|
+
IMG_FORGE: Fetcher;
|
|
62
|
+
IMG_FORGE_SB_SECRET: string;
|
|
63
|
+
|
|
64
|
+
// Memory Worker (Service Binding RPC)
|
|
65
|
+
MEMORY: MemoryServiceBinding;
|
|
66
|
+
|
|
67
|
+
// TarotScript Worker (symbolic consultation via Service Binding)
|
|
68
|
+
TAROTSCRIPT: Fetcher;
|
|
69
|
+
|
|
70
|
+
// Colony OS MARA Governor (MCP via Service Binding)
|
|
71
|
+
MARA: Fetcher;
|
|
72
|
+
MARA_TOKEN: string;
|
|
73
|
+
|
|
74
|
+
// CodeBeast (adversarial code review + fix drain via Service Binding)
|
|
75
|
+
CODEBEAST: Fetcher;
|
|
76
|
+
|
|
77
|
+
// MindSpring (semantic search over 2k+ Claude Code conversations via Service Binding)
|
|
78
|
+
MINDSPRING: Fetcher;
|
|
79
|
+
MINDSPRING_TOKEN: string;
|
|
80
|
+
|
|
81
|
+
// dev.to syndication (cross-post blog content)
|
|
82
|
+
DEVTO_API_KEY: string;
|
|
83
|
+
|
|
84
|
+
// ElevenLabs voice funnel webhook secret (#99)
|
|
85
|
+
ELEVENLABS_WEBHOOK_SECRET: string;
|
|
86
|
+
|
|
87
|
+
// ARGUS: Webhook HMAC secrets for unified ingestion
|
|
88
|
+
GITHUB_WEBHOOK_SECRET: string;
|
|
89
|
+
STRIPE_WEBHOOK_SECRET: string;
|
|
90
|
+
|
|
91
|
+
// Google Analytics Data API (OAuth2 refresh token flow)
|
|
92
|
+
GA_CREDENTIALS: string; // JSON: { client_id, client_secret, refresh_token, property_id }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ─── Memory Worker RPC interface (mirrors MemoryService)
|
|
96
|
+
export interface MemoryServiceBinding {
|
|
97
|
+
store(tenantId: string, fragments: MemoryStoreRequest[]): Promise<MemoryStoreResult>;
|
|
98
|
+
recall(tenantId: string, query: MemoryRecallQuery): Promise<MemoryFragmentResult[]>;
|
|
99
|
+
forget(tenantId: string, filter: MemoryForgetFilter): Promise<number>;
|
|
100
|
+
decay(tenantId: string): Promise<{ decayed: number; pruned: number; promoted: number }>;
|
|
101
|
+
consolidate(tenantId: string): Promise<{ processed: number; merged: number; pruned: number; high_water_mark: string }>;
|
|
102
|
+
health(): Promise<{ status: string; version: string; tenants: number; total_fragments: number; active_fragments: number }>;
|
|
103
|
+
stats(tenantId: string): Promise<MemoryStatsResult>;
|
|
104
|
+
embed(tenantId: string, texts: string[]): Promise<{ embeddings: number[][] }>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface MemoryStoreRequest {
|
|
108
|
+
content: string;
|
|
109
|
+
topic: string;
|
|
110
|
+
confidence?: number;
|
|
111
|
+
source?: string;
|
|
112
|
+
metadata?: Record<string, unknown>;
|
|
113
|
+
lifecycle?: 'observed' | 'confirmed' | 'core' | 'archived';
|
|
114
|
+
// CRIX insight metadata (all optional, mirrors Memory Worker StoreRequest)
|
|
115
|
+
insight_type?: 'pattern' | 'bug_signature' | 'perf_win' | 'arch_improvement' | 'gotcha';
|
|
116
|
+
origin_repo?: string;
|
|
117
|
+
origin_commit?: string;
|
|
118
|
+
applicable_repos?: string[];
|
|
119
|
+
applicability_confidence?: number;
|
|
120
|
+
keywords?: string[];
|
|
121
|
+
cross_repo_impact?: 'unknown' | 'confirmed' | 'tested' | 'deployed';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface MemoryStoreResult {
|
|
125
|
+
stored: number;
|
|
126
|
+
merged: number;
|
|
127
|
+
duplicates: number;
|
|
128
|
+
fragment_ids: string[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface MemoryRecallQuery {
|
|
132
|
+
id?: string;
|
|
133
|
+
topic?: string;
|
|
134
|
+
topics?: string[];
|
|
135
|
+
keywords?: string;
|
|
136
|
+
lifecycle?: Array<'observed' | 'confirmed' | 'core' | 'archived'>;
|
|
137
|
+
min_confidence?: number;
|
|
138
|
+
limit?: number;
|
|
139
|
+
include_archived?: boolean;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface MemoryFragmentResult {
|
|
143
|
+
id: string;
|
|
144
|
+
tenant_id: string;
|
|
145
|
+
content: string;
|
|
146
|
+
topic: string;
|
|
147
|
+
confidence: number;
|
|
148
|
+
lifecycle: string;
|
|
149
|
+
strength: number;
|
|
150
|
+
last_accessed_at: string;
|
|
151
|
+
access_count: number;
|
|
152
|
+
created_at: string;
|
|
153
|
+
updated_at: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface MemoryForgetFilter {
|
|
157
|
+
ids?: string[];
|
|
158
|
+
topic?: string;
|
|
159
|
+
before?: string;
|
|
160
|
+
min_confidence_below?: number;
|
|
161
|
+
hard_delete?: boolean;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface MemoryStatsResult {
|
|
165
|
+
total_active: number;
|
|
166
|
+
topics: Array<{ topic: string; count: number }>;
|
|
167
|
+
recalled_last_24h: number;
|
|
168
|
+
strength_distribution: { low: number; medium: number; high: number };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// --- Auth Worker RPC interface (service binding)
|
|
172
|
+
export interface AuthServiceBinding {
|
|
173
|
+
getDeveloperActivity(): Promise<DevActivityReport>;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface DevActivityReport {
|
|
177
|
+
total_users: number;
|
|
178
|
+
keys_active_24h: number;
|
|
179
|
+
keys_created_7d: number;
|
|
180
|
+
signups_7d: number;
|
|
181
|
+
api_calls_24h: number;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface MessageMetadata {
|
|
185
|
+
classification?: string;
|
|
186
|
+
executor?: string;
|
|
187
|
+
procHit?: boolean;
|
|
188
|
+
latencyMs?: number;
|
|
189
|
+
cost?: number;
|
|
190
|
+
confidence?: number;
|
|
191
|
+
reclassified?: boolean;
|
|
192
|
+
probeResult?: string;
|
|
193
|
+
error?: boolean;
|
|
194
|
+
}
|
package/src/ui.ts
ADDED
package/src/version.ts
ADDED