@stackbilt/aegis-core 0.1.0 → 0.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbilt/aegis-core",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Persistent AI agent framework for Cloudflare Workers. Multi-tier memory, autonomous goals, dreaming cycles, MCP native.",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
@@ -54,7 +54,12 @@
54
54
  "./routes/cc-tasks": "./src/routes/cc-tasks.ts",
55
55
  "./routes/pages": "./src/routes/pages.ts",
56
56
  "./routes/dynamic-tools": "./src/routes/dynamic-tools.ts",
57
- "./adapters/voice": "./src/adapters/voice/cloudflare-agent.ts"
57
+ "./adapters/voice": "./src/adapters/voice/cloudflare-agent.ts",
58
+ "./schema-enums": "./src/schema-enums.ts",
59
+ "./contracts/goal": "./src/contracts/goal.contract.ts",
60
+ "./contracts/agenda-item": "./src/contracts/agenda-item.contract.ts",
61
+ "./contracts/cc-task": "./src/contracts/cc-task.contract.ts",
62
+ "./contracts/memory-entry": "./src/contracts/memory-entry.contract.ts"
58
63
  },
59
64
  "scripts": {
60
65
  "dev": "wrangler dev",
@@ -69,9 +74,11 @@
69
74
  "dependencies": {
70
75
  "@cloudflare/voice": "^0.1.3",
71
76
  "@cloudflare/workers-oauth-provider": "^0.2.4",
77
+ "@stackbilt/contracts": "^0.2.1",
72
78
  "@stackbilt/llm-providers": "^1.6.0",
73
79
  "agents": "^0.12.3",
74
- "hono": "^4.12.12"
80
+ "hono": "^4.12.12",
81
+ "zod": "^4.4.3"
75
82
  },
76
83
  "devDependencies": {
77
84
  "@cloudflare/workers-types": "^4.20241218.0",
@@ -0,0 +1,113 @@
1
+ import { z } from 'zod';
2
+ import { defineContract } from '@stackbilt/contracts';
3
+
4
+ const AgendaPriority = z.enum(['low', 'medium', 'high']);
5
+ const AgendaStatus = z.enum(['active', 'done', 'dismissed']);
6
+
7
+ export const AgendaItemContract = defineContract({
8
+ name: 'AgendaItem',
9
+ version: '1.0.0',
10
+ description: 'Operator agenda item — work scratchpad with priority and lifecycle tracking',
11
+
12
+ schema: z.object({
13
+ id: z.number().int().positive(),
14
+ item: z.string().min(1),
15
+ context: z.string().nullable(),
16
+ priority: AgendaPriority.default('medium'),
17
+ status: AgendaStatus.default('active'),
18
+ createdAt: z.string().datetime(),
19
+ resolvedAt: z.string().datetime().nullable(),
20
+ businessUnit: z.string().min(1).default('stackbilt'),
21
+ }),
22
+
23
+ operations: {
24
+ add: {
25
+ input: z.object({
26
+ item: z.string().min(1),
27
+ context: z.string().optional(),
28
+ priority: AgendaPriority.optional(),
29
+ businessUnit: z.string().min(1).optional(),
30
+ }),
31
+ output: 'self' as const,
32
+ emits: ['agenda_item.added'],
33
+ },
34
+
35
+ resolve: {
36
+ input: z.object({ id: z.number().int().positive() }),
37
+ output: 'self' as const,
38
+ transition: { from: 'active', to: 'done' },
39
+ emits: ['agenda_item.resolved'],
40
+ },
41
+
42
+ dismiss: {
43
+ input: z.object({ id: z.number().int().positive() }),
44
+ output: 'self' as const,
45
+ transition: { from: 'active', to: 'dismissed' },
46
+ emits: ['agenda_item.dismissed'],
47
+ },
48
+
49
+ escalate: {
50
+ input: z.object({ id: z.number().int().positive() }),
51
+ output: 'self' as const,
52
+ emits: ['agenda_item.escalated'],
53
+ },
54
+ },
55
+
56
+ states: {
57
+ field: 'status',
58
+ initial: 'active',
59
+ transitions: {
60
+ active: {
61
+ resolve: 'done',
62
+ dismiss: 'dismissed',
63
+ },
64
+ done: {},
65
+ dismissed: {},
66
+ },
67
+ },
68
+
69
+ surfaces: {
70
+ api: {
71
+ basePath: '/api/agenda',
72
+ routes: {
73
+ add: { method: 'POST', path: '/' },
74
+ resolve: { method: 'POST', path: '/:id/resolve' },
75
+ dismiss: { method: 'POST', path: '/:id/dismiss' },
76
+ escalate: { method: 'POST', path: '/:id/escalate' },
77
+ },
78
+ },
79
+ db: {
80
+ table: 'agent_agenda',
81
+ indexes: [
82
+ 'idx_agenda_status(status)',
83
+ 'idx_agenda_priority(priority)',
84
+ 'idx_agenda_bu(business_unit, status)',
85
+ ],
86
+ columnOverrides: {
87
+ createdAt: { default: "datetime('now')" },
88
+ },
89
+ },
90
+ },
91
+
92
+ authority: {
93
+ add: { requires: 'role', roles: ['operator', 'system'] },
94
+ resolve: { requires: 'role', roles: ['operator', 'system'] },
95
+ dismiss: { requires: 'role', roles: ['operator', 'system'] },
96
+ escalate: { requires: 'role', roles: ['system'] },
97
+ },
98
+
99
+ invariants: [
100
+ {
101
+ name: 'resolved_has_timestamp',
102
+ description: 'Done items must have a resolvedAt timestamp',
103
+ check: (entity: unknown) => {
104
+ const a = entity as { status?: string; resolvedAt?: string | null };
105
+ if (a.status === 'done' && !a.resolvedAt) {
106
+ return 'Done agenda item requires resolvedAt';
107
+ }
108
+ return true;
109
+ },
110
+ appliesTo: ['resolve'],
111
+ },
112
+ ],
113
+ });
@@ -0,0 +1,204 @@
1
+ import { z } from 'zod';
2
+ import { defineContract } from '@stackbilt/contracts';
3
+
4
+ const TaskStatus = z.enum(['pending', 'running', 'completed', 'failed', 'cancelled']);
5
+ const TaskAuthority = z.enum(['proposed', 'auto_safe', 'operator']);
6
+ const TaskCategory = z.enum(['docs', 'tests', 'research', 'bugfix', 'feature', 'refactor', 'deploy']);
7
+
8
+ export const CCTaskContract = defineContract({
9
+ name: 'CCTask',
10
+ version: '1.0.0',
11
+ description: 'Claude Code autonomous task — queued, governed, and executed by the task runner',
12
+
13
+ schema: z.object({
14
+ id: z.string().min(1),
15
+ title: z.string().min(1),
16
+ repo: z.string().min(1),
17
+ prompt: z.string().min(1),
18
+ completionSignal: z.string().nullable(),
19
+ status: TaskStatus.default('pending'),
20
+ priority: z.number().int().min(0).max(100).default(50),
21
+ dependsOn: z.string().nullable(),
22
+ blockedBy: z.string().nullable(),
23
+ maxTurns: z.number().int().positive().default(25),
24
+ allowedTools: z.string().nullable(),
25
+ sessionId: z.string().nullable(),
26
+ result: z.string().nullable(),
27
+ error: z.string().nullable(),
28
+ exitCode: z.number().int().nullable(),
29
+ preflightJson: z.string().nullable(),
30
+ failureKind: z.string().nullable(),
31
+ retryable: z.boolean().default(false),
32
+ autopsyJson: z.string().nullable(),
33
+ createdAt: z.string().datetime(),
34
+ startedAt: z.string().datetime().nullable(),
35
+ completedAt: z.string().datetime().nullable(),
36
+ createdBy: z.string().min(1).default('operator'),
37
+ authority: TaskAuthority.default('operator'),
38
+ category: TaskCategory.default('feature'),
39
+ branch: z.string().nullable(),
40
+ prUrl: z.string().nullable(),
41
+ utilityJson: z.string().nullable(),
42
+ githubIssueRepo: z.string().nullable(),
43
+ githubIssueNumber: z.number().int().positive().nullable(),
44
+ businessUnit: z.string().min(1).default('stackbilt'),
45
+ }),
46
+
47
+ operations: {
48
+ create: {
49
+ input: z.object({
50
+ id: z.string().min(1),
51
+ title: z.string().min(1),
52
+ repo: z.string().min(1),
53
+ prompt: z.string().min(1),
54
+ completionSignal: z.string().optional(),
55
+ priority: z.number().int().min(0).max(100).optional(),
56
+ dependsOn: z.string().optional(),
57
+ blockedBy: z.string().optional(),
58
+ maxTurns: z.number().int().positive().optional(),
59
+ allowedTools: z.string().optional(),
60
+ authority: TaskAuthority.optional(),
61
+ category: TaskCategory.optional(),
62
+ githubIssueRepo: z.string().optional(),
63
+ githubIssueNumber: z.number().int().positive().optional(),
64
+ businessUnit: z.string().min(1).optional(),
65
+ }),
66
+ output: 'self' as const,
67
+ emits: ['cc_task.created'],
68
+ },
69
+
70
+ start: {
71
+ input: z.object({
72
+ id: z.string().min(1),
73
+ sessionId: z.string().min(1),
74
+ }),
75
+ output: 'self' as const,
76
+ transition: { from: 'pending', to: 'running' },
77
+ emits: ['cc_task.started'],
78
+ },
79
+
80
+ complete: {
81
+ input: z.object({
82
+ id: z.string().min(1),
83
+ result: z.string().optional(),
84
+ exitCode: z.number().int().optional(),
85
+ prUrl: z.string().optional(),
86
+ utilityJson: z.string().optional(),
87
+ }),
88
+ output: 'self' as const,
89
+ transition: { from: 'running', to: 'completed' },
90
+ emits: ['cc_task.completed'],
91
+ },
92
+
93
+ fail: {
94
+ input: z.object({
95
+ id: z.string().min(1),
96
+ error: z.string().min(1),
97
+ exitCode: z.number().int().optional(),
98
+ failureKind: z.string().optional(),
99
+ retryable: z.boolean().optional(),
100
+ autopsyJson: z.string().optional(),
101
+ }),
102
+ output: 'self' as const,
103
+ transition: { from: 'running', to: 'failed' },
104
+ emits: ['cc_task.failed'],
105
+ },
106
+
107
+ cancel: {
108
+ input: z.object({ id: z.string().min(1) }),
109
+ output: 'self' as const,
110
+ transition: { from: ['pending', 'running'], to: 'cancelled' },
111
+ emits: ['cc_task.cancelled'],
112
+ },
113
+
114
+ approve: {
115
+ input: z.object({ id: z.string().min(1) }),
116
+ output: 'self' as const,
117
+ emits: ['cc_task.approved'],
118
+ },
119
+ },
120
+
121
+ states: {
122
+ field: 'status',
123
+ initial: 'pending',
124
+ transitions: {
125
+ pending: {
126
+ start: 'running',
127
+ cancel: 'cancelled',
128
+ },
129
+ running: {
130
+ complete: 'completed',
131
+ fail: 'failed',
132
+ cancel: 'cancelled',
133
+ },
134
+ completed: {},
135
+ failed: {},
136
+ cancelled: {},
137
+ },
138
+ },
139
+
140
+ surfaces: {
141
+ api: {
142
+ basePath: '/api/cc-tasks',
143
+ routes: {
144
+ create: { method: 'POST', path: '/' },
145
+ start: { method: 'POST', path: '/:id/start' },
146
+ complete: { method: 'POST', path: '/:id/complete' },
147
+ fail: { method: 'POST', path: '/:id/fail' },
148
+ cancel: { method: 'POST', path: '/:id/cancel' },
149
+ approve: { method: 'POST', path: '/:id/approve' },
150
+ },
151
+ },
152
+ db: {
153
+ table: 'cc_tasks',
154
+ indexes: [
155
+ 'idx_cc_tasks_status(status, priority)',
156
+ 'idx_cc_tasks_depends(depends_on)',
157
+ 'idx_cc_tasks_created(created_at)',
158
+ 'idx_cc_tasks_bu(business_unit, status)',
159
+ 'idx_cc_tasks_authority(authority)',
160
+ 'idx_cc_tasks_gh_issue(github_issue_repo, github_issue_number)',
161
+ 'idx_cc_tasks_failure_kind(failure_kind, completed_at)',
162
+ ],
163
+ columnOverrides: {
164
+ createdAt: { default: "datetime('now')" },
165
+ },
166
+ },
167
+ },
168
+
169
+ authority: {
170
+ create: { requires: 'role', roles: ['operator', 'system'] },
171
+ start: { requires: 'role', roles: ['system'] },
172
+ complete: { requires: 'role', roles: ['system'] },
173
+ fail: { requires: 'role', roles: ['system'] },
174
+ cancel: { requires: 'role', roles: ['operator', 'system'] },
175
+ approve: { requires: 'role', roles: ['operator'] },
176
+ },
177
+
178
+ invariants: [
179
+ {
180
+ name: 'proposed_task_needs_approval',
181
+ description: 'Proposed tasks must be approved before they can run',
182
+ check: (entity: unknown) => {
183
+ const t = entity as { authority?: string; status?: string };
184
+ if (t.authority === 'proposed' && t.status === 'running') {
185
+ return 'Proposed tasks require approval before execution';
186
+ }
187
+ return true;
188
+ },
189
+ appliesTo: ['start'],
190
+ },
191
+ {
192
+ name: 'completed_has_timestamp',
193
+ description: 'Completed or failed tasks must have a completedAt timestamp',
194
+ check: (entity: unknown) => {
195
+ const t = entity as { status?: string; completedAt?: string | null };
196
+ if ((t.status === 'completed' || t.status === 'failed') && !t.completedAt) {
197
+ return 'Terminal tasks require completedAt';
198
+ }
199
+ return true;
200
+ },
201
+ appliesTo: ['complete', 'fail'],
202
+ },
203
+ ],
204
+ });
@@ -0,0 +1,147 @@
1
+ import { z } from 'zod';
2
+ import { defineContract } from '@stackbilt/contracts';
3
+
4
+ const GoalStatus = z.enum(['active', 'paused', 'completed', 'failed']);
5
+ const AuthorityLevel = z.enum(['propose', 'auto_low', 'auto_high']);
6
+
7
+ export const GoalContract = defineContract({
8
+ name: 'Goal',
9
+ version: '1.0.0',
10
+ description: 'Autonomous agent goal with scheduled execution and authority-gated autonomy',
11
+
12
+ schema: z.object({
13
+ id: z.string().min(1),
14
+ title: z.string().min(1),
15
+ description: z.string().nullable(),
16
+ status: GoalStatus.default('active'),
17
+ authorityLevel: AuthorityLevel.default('propose'),
18
+ scheduleHours: z.number().int().positive().default(6),
19
+ createdAt: z.string().datetime(),
20
+ lastRunAt: z.string().datetime().nullable(),
21
+ nextRunAt: z.string().datetime().nullable(),
22
+ completedAt: z.string().datetime().nullable(),
23
+ runCount: z.number().int().nonnegative().default(0),
24
+ contextJson: z.string().nullable(),
25
+ businessUnit: z.string().min(1).default('stackbilt'),
26
+ }),
27
+
28
+ operations: {
29
+ create: {
30
+ input: z.object({
31
+ id: z.string().min(1),
32
+ title: z.string().min(1),
33
+ description: z.string().optional(),
34
+ authorityLevel: AuthorityLevel.optional(),
35
+ scheduleHours: z.number().int().positive().optional(),
36
+ contextJson: z.string().optional(),
37
+ businessUnit: z.string().min(1).optional(),
38
+ }),
39
+ output: 'self' as const,
40
+ emits: ['goal.created'],
41
+ },
42
+
43
+ pause: {
44
+ input: z.object({ id: z.string().min(1) }),
45
+ output: 'self' as const,
46
+ transition: { from: 'active', to: 'paused' },
47
+ emits: ['goal.paused'],
48
+ },
49
+
50
+ resume: {
51
+ input: z.object({ id: z.string().min(1) }),
52
+ output: 'self' as const,
53
+ transition: { from: 'paused', to: 'active' },
54
+ emits: ['goal.resumed'],
55
+ },
56
+
57
+ complete: {
58
+ input: z.object({ id: z.string().min(1) }),
59
+ output: 'self' as const,
60
+ transition: { from: ['active', 'paused'], to: 'completed' },
61
+ emits: ['goal.completed'],
62
+ },
63
+
64
+ fail: {
65
+ input: z.object({ id: z.string().min(1) }),
66
+ output: 'self' as const,
67
+ transition: { from: ['active', 'paused'], to: 'failed' },
68
+ emits: ['goal.failed'],
69
+ },
70
+
71
+ recordRun: {
72
+ input: z.object({
73
+ id: z.string().min(1),
74
+ nextRunAt: z.string().datetime().optional(),
75
+ }),
76
+ output: 'self' as const,
77
+ emits: ['goal.ran'],
78
+ },
79
+ },
80
+
81
+ states: {
82
+ field: 'status',
83
+ initial: 'active',
84
+ transitions: {
85
+ active: {
86
+ pause: 'paused',
87
+ complete: 'completed',
88
+ fail: 'failed',
89
+ },
90
+ paused: {
91
+ resume: 'active',
92
+ complete: 'completed',
93
+ fail: 'failed',
94
+ },
95
+ completed: {},
96
+ failed: {},
97
+ },
98
+ },
99
+
100
+ surfaces: {
101
+ api: {
102
+ basePath: '/api/goals',
103
+ routes: {
104
+ create: { method: 'POST', path: '/' },
105
+ pause: { method: 'POST', path: '/:id/pause' },
106
+ resume: { method: 'POST', path: '/:id/resume' },
107
+ complete: { method: 'POST', path: '/:id/complete' },
108
+ fail: { method: 'POST', path: '/:id/fail' },
109
+ },
110
+ },
111
+ db: {
112
+ table: 'agent_goals',
113
+ indexes: [
114
+ 'idx_goals_status(status)',
115
+ 'idx_goals_next_run(next_run_at)',
116
+ 'idx_goals_bu(business_unit, status)',
117
+ ],
118
+ columnOverrides: {
119
+ createdAt: { default: "datetime('now')" },
120
+ },
121
+ },
122
+ },
123
+
124
+ authority: {
125
+ create: { requires: 'role', roles: ['operator', 'system'] },
126
+ pause: { requires: 'role', roles: ['operator', 'system'] },
127
+ resume: { requires: 'role', roles: ['operator', 'system'] },
128
+ complete: { requires: 'role', roles: ['system'] },
129
+ fail: { requires: 'role', roles: ['system'] },
130
+ recordRun: { requires: 'role', roles: ['system'] },
131
+ },
132
+
133
+ invariants: [
134
+ {
135
+ name: 'completed_has_timestamp',
136
+ description: 'Completed goals must have a completedAt timestamp',
137
+ check: (entity: unknown) => {
138
+ const g = entity as { status?: string; completedAt?: string | null };
139
+ if (g.status === 'completed' && !g.completedAt) {
140
+ return 'Completed goal requires completedAt';
141
+ }
142
+ return true;
143
+ },
144
+ appliesTo: ['complete'],
145
+ },
146
+ ],
147
+ });
@@ -0,0 +1,177 @@
1
+ import { z } from 'zod';
2
+ import { defineContract } from '@stackbilt/contracts';
3
+
4
+ const ValidationStage = z.enum(['candidate', 'validated', 'expert', 'canonical', 'refuted']);
5
+
6
+ export const MemoryEntryContract = defineContract({
7
+ name: 'MemoryEntry',
8
+ version: '1.0.0',
9
+ description: 'Semantic memory entry with topic-scoped facts, CRIX validation pipeline, and decay tracking',
10
+
11
+ schema: z.object({
12
+ id: z.number().int().positive(),
13
+ topic: z.string().min(1),
14
+ fact: z.string().min(1),
15
+ factHash: z.string().default(''),
16
+ confidence: z.number().min(0).max(1).default(0.8),
17
+ source: z.string().min(1),
18
+ createdAt: z.string().datetime(),
19
+ updatedAt: z.string().datetime(),
20
+ expiresAt: z.string().datetime().nullable(),
21
+ validUntil: z.string().datetime().nullable(),
22
+ supersededBy: z.number().int().positive().nullable(),
23
+ strength: z.number().int().nonnegative().default(1),
24
+ lastRecalledAt: z.string().datetime().nullable(),
25
+ validationStage: ValidationStage.default('candidate'),
26
+ validators: z.string().nullable(),
27
+ }),
28
+
29
+ operations: {
30
+ record: {
31
+ input: z.object({
32
+ topic: z.string().min(1),
33
+ fact: z.string().min(1),
34
+ source: z.string().min(1),
35
+ confidence: z.number().min(0).max(1).optional(),
36
+ expiresAt: z.string().datetime().optional(),
37
+ }),
38
+ output: 'self' as const,
39
+ emits: ['memory_entry.recorded'],
40
+ },
41
+
42
+ validate: {
43
+ input: z.object({
44
+ id: z.number().int().positive(),
45
+ repo: z.string().min(1),
46
+ confirmed: z.boolean(),
47
+ date: z.string().datetime(),
48
+ }),
49
+ output: 'self' as const,
50
+ transition: { from: 'candidate', to: 'validated' },
51
+ emits: ['memory_entry.validated'],
52
+ },
53
+
54
+ promoteToExpert: {
55
+ input: z.object({ id: z.number().int().positive() }),
56
+ output: 'self' as const,
57
+ transition: { from: 'validated', to: 'expert' },
58
+ emits: ['memory_entry.promoted_expert'],
59
+ },
60
+
61
+ promoteToCanonical: {
62
+ input: z.object({ id: z.number().int().positive() }),
63
+ output: 'self' as const,
64
+ transition: { from: 'expert', to: 'canonical' },
65
+ emits: ['memory_entry.promoted_canonical'],
66
+ },
67
+
68
+ refute: {
69
+ input: z.object({
70
+ id: z.number().int().positive(),
71
+ supersededBy: z.number().int().positive().optional(),
72
+ }),
73
+ output: 'self' as const,
74
+ transition: { from: ['candidate', 'validated', 'expert'], to: 'refuted' },
75
+ emits: ['memory_entry.refuted'],
76
+ },
77
+
78
+ recall: {
79
+ input: z.object({ id: z.number().int().positive() }),
80
+ output: 'self' as const,
81
+ emits: ['memory_entry.recalled'],
82
+ },
83
+
84
+ expire: {
85
+ input: z.object({ id: z.number().int().positive() }),
86
+ output: 'self' as const,
87
+ emits: ['memory_entry.expired'],
88
+ },
89
+ },
90
+
91
+ states: {
92
+ field: 'validationStage',
93
+ initial: 'candidate',
94
+ transitions: {
95
+ candidate: {
96
+ validate: 'validated',
97
+ refute: 'refuted',
98
+ },
99
+ validated: {
100
+ promoteToExpert: 'expert',
101
+ refute: 'refuted',
102
+ },
103
+ expert: {
104
+ promoteToCanonical: 'canonical',
105
+ refute: 'refuted',
106
+ },
107
+ canonical: {},
108
+ refuted: {},
109
+ },
110
+ },
111
+
112
+ surfaces: {
113
+ api: {
114
+ basePath: '/api/memory',
115
+ routes: {
116
+ record: { method: 'POST', path: '/' },
117
+ validate: { method: 'POST', path: '/:id/validate' },
118
+ promoteToExpert: { method: 'POST', path: '/:id/promote-expert' },
119
+ promoteToCanonical: { method: 'POST', path: '/:id/promote-canonical' },
120
+ refute: { method: 'POST', path: '/:id/refute' },
121
+ recall: { method: 'POST', path: '/:id/recall' },
122
+ expire: { method: 'POST', path: '/:id/expire' },
123
+ },
124
+ },
125
+ db: {
126
+ table: 'memory_entries',
127
+ indexes: [
128
+ 'idx_memory_topic(topic)',
129
+ 'idx_memory_dedup(topic, fact_hash)',
130
+ 'idx_memory_expires(expires_at)',
131
+ 'idx_memory_valid(valid_until)',
132
+ 'idx_memory_validation_stage(validation_stage)',
133
+ ],
134
+ columnOverrides: {
135
+ createdAt: { default: "datetime('now')" },
136
+ updatedAt: { default: "datetime('now')" },
137
+ },
138
+ },
139
+ },
140
+
141
+ authority: {
142
+ record: { requires: 'role', roles: ['operator', 'system'] },
143
+ validate: { requires: 'role', roles: ['system'] },
144
+ promoteToExpert: { requires: 'role', roles: ['system'] },
145
+ promoteToCanonical: { requires: 'role', roles: ['system'] },
146
+ refute: { requires: 'role', roles: ['operator', 'system'] },
147
+ recall: { requires: 'role', roles: ['system'] },
148
+ expire: { requires: 'role', roles: ['system'] },
149
+ },
150
+
151
+ invariants: [
152
+ {
153
+ name: 'refuted_entry_not_canonical',
154
+ description: 'Canonical entries cannot be refuted — they must be superseded instead',
155
+ check: (entity: unknown) => {
156
+ const m = entity as { validationStage?: string };
157
+ if (m.validationStage === 'canonical') {
158
+ return 'Canonical entries cannot transition to refuted';
159
+ }
160
+ return true;
161
+ },
162
+ appliesTo: ['refute'],
163
+ },
164
+ {
165
+ name: 'high_confidence_for_canonical',
166
+ description: 'Canonical entries should have confidence >= 0.9',
167
+ check: (entity: unknown) => {
168
+ const m = entity as { validationStage?: string; confidence?: number };
169
+ if (m.validationStage === 'canonical' && (m.confidence ?? 0) < 0.9) {
170
+ return 'Canonical memory entries require confidence >= 0.9';
171
+ }
172
+ return true;
173
+ },
174
+ appliesTo: ['promoteToCanonical'],
175
+ },
176
+ ],
177
+ });
package/src/core.ts CHANGED
@@ -34,13 +34,14 @@ import { dynamicToolsRoutes } from './routes/dynamic-tools.js';
34
34
 
35
35
  export type TaskPhase = 'heartbeat' | 'cron';
36
36
 
37
- export interface ScheduledTaskPlugin {
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ export interface ScheduledTaskPlugin<TEnv extends EdgeEnv = any> {
38
39
  /** Unique task name (used in task_runs table and audit chain) */
39
40
  name: string;
40
41
  /** heartbeat = runs every hour, cron = time-gated */
41
42
  phase: TaskPhase;
42
43
  /** The task implementation */
43
- run: (env: EdgeEnv) => Promise<void>;
44
+ run: (env: TEnv) => Promise<void>;
44
45
  /**
45
46
  * Optional time gate for cron-phase tasks.
46
47
  * If provided, the task only runs when `new Date().getUTCHours() % frequency === 0`.
@@ -115,7 +116,8 @@ export interface AegisAppConfig {
115
116
  operator: OperatorConfig;
116
117
 
117
118
  /** Additional scheduled tasks to register alongside core tasks */
118
- scheduledTasks?: ScheduledTaskPlugin[];
119
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
120
+ scheduledTasks?: ScheduledTaskPlugin<any>[];
119
121
 
120
122
  /** Additional executors to register */
121
123
  executors?: ExecutorPlugin[];
@@ -56,6 +56,13 @@ export type ActionOutcome = EnumValues<typeof ACTION_OUTCOMES>;
56
56
  export const HEARTBEAT_STATUSES = ['ok', 'error', 'skipped'] as const;
57
57
  export type HeartbeatStatus = EnumValues<typeof HEARTBEAT_STATUSES>;
58
58
 
59
+ // ─── CC Jobs ─────────────────────────────────────────────────
60
+
61
+ export const JOB_STATUSES = ['active', 'paused', 'completed', 'failed', 'cancelled'] as const;
62
+ export type JobStatus = EnumValues<typeof JOB_STATUSES>;
63
+
64
+ export const TERMINAL_JOB_STATUSES = new Set<JobStatus>(['completed', 'failed', 'cancelled']);
65
+
59
66
  // ─── CC Tasks ────────────────────────────────────────────────
60
67
 
61
68
  export const TASK_STATUSES = ['pending', 'running', 'completed', 'failed', 'cancelled'] as const;
@@ -124,6 +131,19 @@ export type ToolExecutor = EnumValues<typeof TOOL_EXECUTORS>;
124
131
  export const TOOL_STATUSES = ['active', 'promoted', 'retired', 'draft'] as const;
125
132
  export type ToolStatus = EnumValues<typeof TOOL_STATUSES>;
126
133
 
134
+ // ─── Sprint Backlog ──────────────────────────────────────────
135
+
136
+ export const SPRINT_STAGES = ['plan', 'implement', 'review', 'test', 'ship', 'monitor', 'done', 'failed'] as const;
137
+ export type SprintStage = EnumValues<typeof SPRINT_STAGES>;
138
+
139
+ export const RISK_LEVELS = ['low', 'medium', 'high', 'critical'] as const;
140
+ export type RiskLevel = EnumValues<typeof RISK_LEVELS>;
141
+
142
+ // ─── Alerts ──────────────────────────────────────────────────
143
+
144
+ export const ALERT_SEVERITIES = ['critical', 'high', 'medium', 'low'] as const;
145
+ export type AlertSeverity = EnumValues<typeof ALERT_SEVERITIES>;
146
+
127
147
  // ─── CodeBeast Findings ─────────────────────────────────────
128
148
 
129
149
  export const FINDING_SEVERITIES = ['HIGH', 'MID', 'LOW', 'INFO'] as const;