noormme 1.2.4 → 1.2.5
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/dist/cjs/agentic/CapabilityManager.js +4 -8
- package/dist/cjs/agentic/EpisodicMemory.js +4 -3
- package/dist/cjs/agentic/PersonaManager.js +4 -8
- package/dist/cjs/agentic/PolicyEnforcer.js +4 -7
- package/dist/cjs/agentic/ResourceMonitor.js +2 -1
- package/dist/cjs/agentic/SessionManager.js +7 -6
- package/dist/cjs/agentic/improvement/AblationEngine.d.ts +0 -4
- package/dist/cjs/agentic/improvement/AblationEngine.js +4 -14
- package/dist/cjs/agentic/improvement/ActionRefiner.js +3 -7
- package/dist/cjs/agentic/improvement/ConflictResolver.js +4 -3
- package/dist/cjs/agentic/improvement/GoalArchitect.js +10 -9
- package/dist/cjs/agentic/improvement/KnowledgeDistiller.js +4 -3
- package/dist/cjs/agentic/improvement/SelfTestRegistry.js +3 -7
- package/dist/cjs/agentic/improvement/governance/PersonaAuditor.js +4 -8
- package/dist/cjs/agentic/improvement/hive/KnowledgePromoter.js +4 -3
- package/dist/cjs/agentic/improvement/hive/SkillPropagator.js +1 -1
- package/dist/cjs/agentic/util/db-utils.d.ts +4 -0
- package/dist/cjs/agentic/util/db-utils.js +27 -0
- package/dist/cjs/helpers/agent-schema.js +1 -0
- package/dist/cjs/testing/test-utils.js +2 -0
- package/dist/esm/agentic/CapabilityManager.js +4 -8
- package/dist/esm/agentic/EpisodicMemory.js +4 -3
- package/dist/esm/agentic/PersonaManager.js +4 -8
- package/dist/esm/agentic/PolicyEnforcer.js +4 -7
- package/dist/esm/agentic/ResourceMonitor.js +2 -1
- package/dist/esm/agentic/SessionManager.js +7 -6
- package/dist/esm/agentic/improvement/AblationEngine.d.ts +0 -4
- package/dist/esm/agentic/improvement/AblationEngine.js +4 -14
- package/dist/esm/agentic/improvement/ActionRefiner.js +3 -7
- package/dist/esm/agentic/improvement/ConflictResolver.js +4 -3
- package/dist/esm/agentic/improvement/GoalArchitect.js +10 -9
- package/dist/esm/agentic/improvement/KnowledgeDistiller.js +4 -3
- package/dist/esm/agentic/improvement/SelfTestRegistry.js +3 -7
- package/dist/esm/agentic/improvement/governance/PersonaAuditor.js +4 -8
- package/dist/esm/agentic/improvement/hive/KnowledgePromoter.js +4 -3
- package/dist/esm/agentic/improvement/hive/SkillPropagator.js +1 -1
- package/dist/esm/agentic/util/db-utils.d.ts +4 -0
- package/dist/esm/agentic/util/db-utils.js +25 -0
- package/dist/esm/helpers/agent-schema.js +1 -0
- package/dist/esm/testing/test-utils.js +2 -0
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CapabilityManager = void 0;
|
|
4
|
+
const db_utils_js_1 = require("./util/db-utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* CapabilityManager tracks the skills (tools) available to an agent
|
|
6
7
|
* and their historical reliability.
|
|
@@ -88,18 +89,13 @@ class CapabilityManager {
|
|
|
88
89
|
*/
|
|
89
90
|
async reportOutcome(name, success, trxOrDb = this.db) {
|
|
90
91
|
const runner = async (trx) => {
|
|
91
|
-
|
|
92
|
+
const baseQuery = trx
|
|
92
93
|
.selectFrom(this.capabilitiesTable)
|
|
93
94
|
.selectAll()
|
|
94
95
|
.where('name', '=', name)
|
|
95
96
|
.orderBy('updated_at', 'desc');
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
99
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
100
|
-
query = query.forUpdate();
|
|
101
|
-
}
|
|
102
|
-
const capability = await query.executeTakeFirst();
|
|
97
|
+
const capability = await (0, db_utils_js_1.withLock)(baseQuery, trx)
|
|
98
|
+
.executeTakeFirst();
|
|
103
99
|
if (capability) {
|
|
104
100
|
const cap = capability;
|
|
105
101
|
const metadata = typeof cap.metadata === 'string'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EpisodicMemory = void 0;
|
|
4
|
+
const db_utils_js_1 = require("./util/db-utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* EpisodicMemory groups interactions into semantic chunks (episodes),
|
|
6
7
|
* allowing agents to recall specific scenarios and their outcomes.
|
|
@@ -39,11 +40,11 @@ class EpisodicMemory {
|
|
|
39
40
|
*/
|
|
40
41
|
async completeEpisode(episodeId, summary, metadata) {
|
|
41
42
|
return await this.db.transaction().execute(async (trx) => {
|
|
42
|
-
const
|
|
43
|
+
const query = trx
|
|
43
44
|
.selectFrom(this.episodesTable)
|
|
44
45
|
.selectAll()
|
|
45
|
-
.where('id', '=', episodeId)
|
|
46
|
-
|
|
46
|
+
.where('id', '=', episodeId);
|
|
47
|
+
const existing = await (0, db_utils_js_1.withLock)(query, trx) // Audit Phase 12: Atomic completion lock
|
|
47
48
|
.executeTakeFirst();
|
|
48
49
|
if (!existing)
|
|
49
50
|
throw new Error(`Episode with ID ${episodeId} not found`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PersonaManager = void 0;
|
|
4
|
+
const db_utils_js_1 = require("./util/db-utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* PersonaManager handles persistent agent identities that bridge multiple sessions.
|
|
6
7
|
*/
|
|
@@ -21,17 +22,12 @@ class PersonaManager {
|
|
|
21
22
|
*/
|
|
22
23
|
async upsertPersona(name, options = {}, trxOrDb = this.db) {
|
|
23
24
|
const runner = async (trx) => {
|
|
24
|
-
|
|
25
|
+
const baseQuery = trx
|
|
25
26
|
.selectFrom(this.personasTable)
|
|
26
27
|
.selectAll()
|
|
27
28
|
.where('name', '=', name);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
31
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
32
|
-
query = query.forUpdate();
|
|
33
|
-
}
|
|
34
|
-
const existing = await query.executeTakeFirst();
|
|
29
|
+
const existing = await (0, db_utils_js_1.withLock)(baseQuery, trx)
|
|
30
|
+
.executeTakeFirst();
|
|
35
31
|
const values = {
|
|
36
32
|
name,
|
|
37
33
|
role: options.role || null,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PolicyEnforcer = void 0;
|
|
4
|
+
const db_utils_js_1 = require("./util/db-utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* PolicyEnforcer stores and validates agent autonomous guardrails,
|
|
6
7
|
* such as budgets, safety constraints, and privacy rules.
|
|
@@ -25,16 +26,12 @@ class PolicyEnforcer {
|
|
|
25
26
|
*/
|
|
26
27
|
async definePolicy(name, type, definition, isEnabled = true, trxOrDb = this.db) {
|
|
27
28
|
const runner = async (trx) => {
|
|
28
|
-
|
|
29
|
+
const query = trx
|
|
29
30
|
.selectFrom(this.policiesTable)
|
|
30
31
|
.select('id')
|
|
31
32
|
.where('name', '=', name);
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
35
|
-
query = query.forUpdate();
|
|
36
|
-
}
|
|
37
|
-
const existing = await query.executeTakeFirst();
|
|
33
|
+
const existing = await (0, db_utils_js_1.withLock)(query, trx)
|
|
34
|
+
.executeTakeFirst();
|
|
38
35
|
if (existing) {
|
|
39
36
|
const updated = await trx
|
|
40
37
|
.updateTable(this.policiesTable)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ResourceMonitor = void 0;
|
|
4
|
+
const sql_js_1 = require("../raw-builder/sql.js");
|
|
4
5
|
/**
|
|
5
6
|
* ResourceMonitor tracks token usage and costs across sessions.
|
|
6
7
|
*/
|
|
@@ -100,7 +101,7 @@ class ResourceMonitor {
|
|
|
100
101
|
.select([
|
|
101
102
|
'model_name',
|
|
102
103
|
(eb) => eb.fn
|
|
103
|
-
.sum(
|
|
104
|
+
.sum((0, sql_js_1.sql) `input_tokens + output_tokens`)
|
|
104
105
|
.as('totalTokens'),
|
|
105
106
|
(eb) => eb.fn.sum('cost').as('totalCost'),
|
|
106
107
|
])
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SessionManager = void 0;
|
|
4
|
+
const db_utils_js_1 = require("./util/db-utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* SessionManager handles the lifecycle of agentic sessions, including
|
|
6
7
|
* message history, goal tracking, and memory persistence.
|
|
@@ -134,12 +135,12 @@ class SessionManager {
|
|
|
134
135
|
async upsertGoal(sessionId, description, options = {}) {
|
|
135
136
|
const { status = 'pending', priority = 0, parentId, metadata } = options;
|
|
136
137
|
return await this.db.transaction().execute(async (trx) => {
|
|
137
|
-
const
|
|
138
|
+
const query = trx
|
|
138
139
|
.selectFrom(this.goalsTable)
|
|
139
140
|
.selectAll()
|
|
140
141
|
.where('session_id', '=', sessionId)
|
|
141
|
-
.where('description', '=', description)
|
|
142
|
-
|
|
142
|
+
.where('description', '=', description);
|
|
143
|
+
const existing = await (0, db_utils_js_1.withLock)(query, trx) // Audit Phase 13: Atomic goal lock
|
|
143
144
|
.executeTakeFirst();
|
|
144
145
|
if (existing) {
|
|
145
146
|
const updated = await trx
|
|
@@ -210,11 +211,11 @@ class SessionManager {
|
|
|
210
211
|
// We avoid the Read-Modify-Write race condition by letting the DB handle the merge
|
|
211
212
|
// or by using a strict transaction if the DB doesn't support JSON patching natively.
|
|
212
213
|
const updated = await this.db.transaction().execute(async (trx) => {
|
|
213
|
-
const
|
|
214
|
+
const query = trx
|
|
214
215
|
.selectFrom(this.messagesTable)
|
|
215
216
|
.select('metadata')
|
|
216
|
-
.where('id', '=', messageId)
|
|
217
|
-
|
|
217
|
+
.where('id', '=', messageId);
|
|
218
|
+
const message = await (0, db_utils_js_1.withLock)(query, trx) // Lock the row for the duration of the transaction
|
|
218
219
|
.executeTakeFirstOrThrow();
|
|
219
220
|
const metadata = typeof message.metadata === 'string'
|
|
220
221
|
? JSON.parse(message.metadata)
|
|
@@ -14,10 +14,6 @@ export declare class AblationEngine {
|
|
|
14
14
|
private linksTable;
|
|
15
15
|
constructor(db: Kysely<any>, cortex: Cortex, config?: AgenticConfig);
|
|
16
16
|
private get typedDb();
|
|
17
|
-
/**
|
|
18
|
-
* Helper to apply forUpdate only where supported (Skip for SQLite)
|
|
19
|
-
*/
|
|
20
|
-
private withLock;
|
|
21
17
|
/**
|
|
22
18
|
* Identify "Zombies": Items that have never been retrieved/hit and are old.
|
|
23
19
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AblationEngine = void 0;
|
|
4
|
+
const db_utils_js_1 = require("../util/db-utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* AblationEngine identifies and removes unused or redundant data
|
|
6
7
|
* to keep the agent's context window and database lean.
|
|
@@ -23,17 +24,6 @@ class AblationEngine {
|
|
|
23
24
|
get typedDb() {
|
|
24
25
|
return this.db;
|
|
25
26
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Helper to apply forUpdate only where supported (Skip for SQLite)
|
|
28
|
-
*/
|
|
29
|
-
withLock(query, trx) {
|
|
30
|
-
const executor = trx.getExecutor();
|
|
31
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
32
|
-
if (adapterName.toLowerCase().includes('sqlite')) {
|
|
33
|
-
return query;
|
|
34
|
-
}
|
|
35
|
-
return query.forUpdate();
|
|
36
|
-
}
|
|
37
27
|
/**
|
|
38
28
|
* Identify "Zombies": Items that have never been retrieved/hit and are old.
|
|
39
29
|
*/
|
|
@@ -55,7 +45,7 @@ class AblationEngine {
|
|
|
55
45
|
.where('id', 'not in', (eb) => eb.selectFrom(this.linksTable).select('source_id'))
|
|
56
46
|
.where('id', 'not in', (eb) => eb.selectFrom(this.linksTable).select('target_id'))
|
|
57
47
|
.limit(500); // Audit Phase 9: Batch limit
|
|
58
|
-
const knowledgeToPrune = await
|
|
48
|
+
const knowledgeToPrune = await (0, db_utils_js_1.withLock)(query, trx).execute();
|
|
59
49
|
if (knowledgeToPrune.length > 0) {
|
|
60
50
|
const candidates = knowledgeToPrune.map((k) => this.cortex.knowledge['parseKnowledge'](k));
|
|
61
51
|
const idsToDelete = [];
|
|
@@ -151,7 +141,7 @@ class AblationEngine {
|
|
|
151
141
|
.selectFrom(this.knowledgeTable)
|
|
152
142
|
.selectAll()
|
|
153
143
|
.where('id', '=', id);
|
|
154
|
-
const item = (await
|
|
144
|
+
const item = (await (0, db_utils_js_1.withLock)(query, trx).executeTakeFirst());
|
|
155
145
|
if (!item)
|
|
156
146
|
return false;
|
|
157
147
|
const metadata = typeof item.metadata === 'string'
|
|
@@ -194,7 +184,7 @@ class AblationEngine {
|
|
|
194
184
|
.selectFrom(this.knowledgeTable)
|
|
195
185
|
.selectAll()
|
|
196
186
|
.where('id', '=', id);
|
|
197
|
-
const item = (await
|
|
187
|
+
const item = (await (0, db_utils_js_1.withLock)(query, t).executeTakeFirst());
|
|
198
188
|
if (!item)
|
|
199
189
|
return false;
|
|
200
190
|
const metadata = typeof item.metadata === 'string'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ActionRefiner = void 0;
|
|
4
|
+
const db_utils_js_1 = require("../util/db-utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* ActionRefiner analyzes the ActionJournal to find patterns in failures
|
|
6
7
|
* and suggests new CognitiveRules to improve agent performance.
|
|
@@ -73,18 +74,13 @@ class ActionRefiner {
|
|
|
73
74
|
// Audit Phase 19: Atomic rule proposal via transaction + existence check
|
|
74
75
|
const runner = async (trx) => {
|
|
75
76
|
const rulesTable = this.cortex.config.rulesTable || 'agent_rules';
|
|
76
|
-
|
|
77
|
+
const query = trx
|
|
77
78
|
.selectFrom(rulesTable)
|
|
78
79
|
.select('id')
|
|
79
80
|
.where('table_name', '=', 'agent_actions')
|
|
80
81
|
.where('operation', '=', 'insert')
|
|
81
82
|
.where('metadata', 'like', `%\"targetTool\":\"${toolName}\"%`);
|
|
82
|
-
const
|
|
83
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
84
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
85
|
-
query = query.forUpdate();
|
|
86
|
-
}
|
|
87
|
-
const existing = await query.executeTakeFirst();
|
|
83
|
+
const existing = await (0, db_utils_js_1.withLock)(query, trx).executeTakeFirst();
|
|
88
84
|
if (!existing) {
|
|
89
85
|
console.log(`[ActionRefiner] Proposing reflection rule for tool: ${toolName}`);
|
|
90
86
|
await this.cortex.rules.defineRule('agent_actions', 'insert', 'audit', {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ConflictResolver = void 0;
|
|
4
4
|
const similarity_js_1 = require("../../util/similarity.js");
|
|
5
|
+
const db_utils_js_1 = require("../util/db-utils.js");
|
|
5
6
|
/**
|
|
6
7
|
* ConflictResolver identifies and resolves logical inconsistencies
|
|
7
8
|
* in the agent's cognitive rules and behavior policies.
|
|
@@ -85,14 +86,14 @@ class ConflictResolver {
|
|
|
85
86
|
async resolveConflict(tableName, operation) {
|
|
86
87
|
console.log(`[ConflictResolver] Resolving conflict for ${tableName}:${operation}`);
|
|
87
88
|
return await this.db.transaction().execute(async (trx) => {
|
|
88
|
-
const
|
|
89
|
+
const query = trx
|
|
89
90
|
.selectFrom(this.rulesTable)
|
|
90
91
|
.selectAll()
|
|
91
92
|
.where('table_name', '=', tableName)
|
|
92
93
|
.where('operation', '=', operation)
|
|
93
94
|
.where('is_enabled', '=', true)
|
|
94
|
-
.orderBy('created_at', 'desc')
|
|
95
|
-
|
|
95
|
+
.orderBy('created_at', 'desc');
|
|
96
|
+
const rules = (await (0, db_utils_js_1.withLock)(query, trx) // Audit Phase 10: Atomic resolution lock
|
|
96
97
|
.execute());
|
|
97
98
|
if (rules.length <= 1)
|
|
98
99
|
return;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GoalArchitect = void 0;
|
|
4
4
|
const similarity_js_1 = require("../../util/similarity.js");
|
|
5
|
+
const db_utils_js_1 = require("../util/db-utils.js");
|
|
5
6
|
/**
|
|
6
7
|
* GoalArchitect enables agents to autonomously deconstruct complex
|
|
7
8
|
* objectives into manageable sub-goals.
|
|
@@ -26,11 +27,11 @@ class GoalArchitect {
|
|
|
26
27
|
return await this.db.transaction().execute(async (trx) => {
|
|
27
28
|
// 1. Audit Phase 9: Circular Dependency Protection inside transaction
|
|
28
29
|
await this.detectCircularDependency(goalId, new Set(), trx);
|
|
29
|
-
const
|
|
30
|
+
const query = trx
|
|
30
31
|
.selectFrom(this.goalsTable)
|
|
31
32
|
.selectAll()
|
|
32
|
-
.where('id', '=', goalId)
|
|
33
|
-
|
|
33
|
+
.where('id', '=', goalId);
|
|
34
|
+
const goal = (await (0, db_utils_js_1.withLock)(query, trx) // Audit Phase 9: Atomic acquisition
|
|
34
35
|
.executeTakeFirst());
|
|
35
36
|
if (!goal)
|
|
36
37
|
throw new Error(`Goal ${goalId} not found`);
|
|
@@ -101,11 +102,11 @@ class GoalArchitect {
|
|
|
101
102
|
const sortedIds = [...goalIds].sort((a, b) => String(a).localeCompare(String(b)));
|
|
102
103
|
await this.db.transaction().execute(async (trx) => {
|
|
103
104
|
// Pre-lock all rows in deterministic order
|
|
104
|
-
|
|
105
|
+
const query = trx
|
|
105
106
|
.selectFrom(this.goalsTable)
|
|
106
107
|
.select('id')
|
|
107
|
-
.where('id', 'in', sortedIds)
|
|
108
|
-
|
|
108
|
+
.where('id', 'in', sortedIds);
|
|
109
|
+
await (0, db_utils_js_1.withLock)(query, trx)
|
|
109
110
|
.execute();
|
|
110
111
|
for (let i = 0; i < goalIds.length; i++) {
|
|
111
112
|
await trx
|
|
@@ -121,11 +122,11 @@ class GoalArchitect {
|
|
|
121
122
|
*/
|
|
122
123
|
async markGoalAs(goalId, status, outcome) {
|
|
123
124
|
return await this.db.transaction().execute(async (trx) => {
|
|
124
|
-
const
|
|
125
|
+
const query = trx
|
|
125
126
|
.selectFrom(this.goalsTable)
|
|
126
127
|
.selectAll()
|
|
127
|
-
.where('id', '=', goalId)
|
|
128
|
-
|
|
128
|
+
.where('id', '=', goalId);
|
|
129
|
+
const goal = (await (0, db_utils_js_1.withLock)(query, trx) // Audit Phase 9: Atomic status/meta update
|
|
129
130
|
.executeTakeFirst());
|
|
130
131
|
if (!goal)
|
|
131
132
|
throw new Error(`Goal ${goalId} not found`);
|
|
@@ -5,6 +5,7 @@ const FactDistiller_js_1 = require("./distillation/FactDistiller.js");
|
|
|
5
5
|
const ConflictChallenger_js_1 = require("./distillation/ConflictChallenger.js");
|
|
6
6
|
const RelationshipArchitect_js_1 = require("./distillation/RelationshipArchitect.js");
|
|
7
7
|
const KnowledgeConsolidator_js_1 = require("./distillation/KnowledgeConsolidator.js");
|
|
8
|
+
const db_utils_js_1 = require("../util/db-utils.js");
|
|
8
9
|
/**
|
|
9
10
|
* KnowledgeDistiller extracts structured "KnowledgeItems" from longtail history,
|
|
10
11
|
* allowing agents to build a permanent, queryable knowledge base.
|
|
@@ -106,11 +107,11 @@ class KnowledgeDistiller {
|
|
|
106
107
|
*/
|
|
107
108
|
async recordHit(id) {
|
|
108
109
|
await this.db.transaction().execute(async (trx) => {
|
|
109
|
-
const
|
|
110
|
+
const query = trx
|
|
110
111
|
.selectFrom(this.knowledgeTable)
|
|
111
112
|
.select(['id', 'entity', 'metadata'])
|
|
112
|
-
.where('id', '=', id)
|
|
113
|
-
|
|
113
|
+
.where('id', '=', id);
|
|
114
|
+
const existing = await (0, db_utils_js_1.withLock)(query, trx)
|
|
114
115
|
.executeTakeFirst();
|
|
115
116
|
if (!existing)
|
|
116
117
|
return;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SelfTestRegistry = void 0;
|
|
4
|
+
const db_utils_js_1 = require("../util/db-utils.js");
|
|
4
5
|
/**
|
|
5
6
|
* SelfTestRegistry allows agents to self-register verification probes
|
|
6
7
|
* that ensure autonomous changes don't violate core logic.
|
|
@@ -57,16 +58,11 @@ class SelfTestRegistry {
|
|
|
57
58
|
results.push({ name: probe.name, success });
|
|
58
59
|
const updateRunner = async (trx) => {
|
|
59
60
|
// Audit Phase 13: Lock row before updating last_status
|
|
60
|
-
|
|
61
|
+
const query = trx
|
|
61
62
|
.selectFrom('agent_logic_probes')
|
|
62
63
|
.select('id')
|
|
63
64
|
.where('id', '=', probe.id);
|
|
64
|
-
|
|
65
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
66
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
67
|
-
query = query.forUpdate();
|
|
68
|
-
}
|
|
69
|
-
await query.executeTakeFirst();
|
|
65
|
+
await (0, db_utils_js_1.withLock)(query, trx).executeTakeFirst();
|
|
70
66
|
await trx
|
|
71
67
|
.updateTable('agent_logic_probes')
|
|
72
68
|
.set({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PersonaAuditor = void 0;
|
|
4
|
+
const db_utils_js_1 = require("../../util/db-utils.js");
|
|
4
5
|
class PersonaAuditor {
|
|
5
6
|
async audit(ctx) {
|
|
6
7
|
const issues = [];
|
|
@@ -43,17 +44,12 @@ class PersonaAuditor {
|
|
|
43
44
|
console.warn(`[PersonaAuditor] QUARANTINING Persona ${id}: ${reason}`);
|
|
44
45
|
// Use the provided transaction or start a new one to ensure atomicity
|
|
45
46
|
const runner = async (trx) => {
|
|
46
|
-
|
|
47
|
+
const query = trx
|
|
47
48
|
.selectFrom(ctx.personasTable)
|
|
48
49
|
.selectAll()
|
|
49
50
|
.where('id', '=', id);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
53
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
54
|
-
query = query.forUpdate();
|
|
55
|
-
}
|
|
56
|
-
const persona = await query.executeTakeFirst();
|
|
51
|
+
const persona = await (0, db_utils_js_1.withLock)(query, trx)
|
|
52
|
+
.executeTakeFirst();
|
|
57
53
|
if (persona) {
|
|
58
54
|
const metadata = typeof persona.metadata === 'string'
|
|
59
55
|
? JSON.parse(persona.metadata)
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.KnowledgePromoter = void 0;
|
|
4
|
+
const db_utils_js_1 = require("../../util/db-utils.js");
|
|
4
5
|
class KnowledgePromoter {
|
|
5
6
|
async promote(db, cortex, config, knowledgeTable, item) {
|
|
6
7
|
return await db.transaction().execute(async (trx) => {
|
|
7
8
|
// Check if a global version already exists
|
|
8
|
-
const
|
|
9
|
+
const query = trx
|
|
9
10
|
.selectFrom(knowledgeTable)
|
|
10
11
|
.selectAll()
|
|
11
12
|
.where('entity', '=', item.entity)
|
|
12
13
|
.where('fact', '=', item.fact)
|
|
13
|
-
.where('source_session_id', 'is', null)
|
|
14
|
-
|
|
14
|
+
.where('source_session_id', 'is', null);
|
|
15
|
+
const existingGlobal = await (0, db_utils_js_1.withLock)(query, trx) // Prevent concurrent promotion duplication
|
|
15
16
|
.executeTakeFirst();
|
|
16
17
|
if (existingGlobal) {
|
|
17
18
|
// Reinforce existing global knowledge
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withLock = withLock;
|
|
4
|
+
/**
|
|
5
|
+
* Utility function to apply a "FOR UPDATE" lock only where supported (Not in SQLite)
|
|
6
|
+
*/
|
|
7
|
+
function withLock(query, trx) {
|
|
8
|
+
if (!trx || typeof trx.getExecutor !== 'function') {
|
|
9
|
+
// If we're using a mock or a version of Kysely without getExecutor
|
|
10
|
+
// we default to no lock to prevent failures in tests
|
|
11
|
+
return query;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const executor = trx.getExecutor();
|
|
15
|
+
const adapterName = executor?.adapter?.constructor?.name ||
|
|
16
|
+
executor?.dialect?.constructor?.name ||
|
|
17
|
+
'';
|
|
18
|
+
if (adapterName.toLowerCase().includes('sqlite')) {
|
|
19
|
+
return query;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
// Fallback if execution fails for some reason
|
|
24
|
+
return query;
|
|
25
|
+
}
|
|
26
|
+
return query.forUpdate();
|
|
27
|
+
}
|
|
@@ -119,6 +119,7 @@ class AgentSchemaHelper {
|
|
|
119
119
|
.addColumn('tool_name', 'text', (col) => col.notNull())
|
|
120
120
|
.addColumn('arguments', 'text', (col) => col.notNull())
|
|
121
121
|
.addColumn('outcome', 'text')
|
|
122
|
+
.addColumn('error', 'text')
|
|
122
123
|
.addColumn('status', 'text', (col) => col.notNull().defaultTo('pending'))
|
|
123
124
|
.addColumn('duration_ms', 'integer')
|
|
124
125
|
.addColumn('metadata', 'text')
|
|
@@ -94,6 +94,8 @@ async function setupTestSchema(db) {
|
|
|
94
94
|
.on('comments')
|
|
95
95
|
.column('user_id')
|
|
96
96
|
.execute();
|
|
97
|
+
// Initialize agent schema
|
|
98
|
+
await db.agent.schema.initializeSchema();
|
|
97
99
|
// Initialize NOORMME to discover the schema
|
|
98
100
|
// Temporarily enable warnings to see discovery errors
|
|
99
101
|
const originalWarn = console.warn;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./CapabilityManager.d.ts" />
|
|
2
|
+
import { withLock } from './util/db-utils.js';
|
|
2
3
|
/**
|
|
3
4
|
* CapabilityManager tracks the skills (tools) available to an agent
|
|
4
5
|
* and their historical reliability.
|
|
@@ -86,18 +87,13 @@ export class CapabilityManager {
|
|
|
86
87
|
*/
|
|
87
88
|
async reportOutcome(name, success, trxOrDb = this.db) {
|
|
88
89
|
const runner = async (trx) => {
|
|
89
|
-
|
|
90
|
+
const baseQuery = trx
|
|
90
91
|
.selectFrom(this.capabilitiesTable)
|
|
91
92
|
.selectAll()
|
|
92
93
|
.where('name', '=', name)
|
|
93
94
|
.orderBy('updated_at', 'desc');
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
97
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
98
|
-
query = query.forUpdate();
|
|
99
|
-
}
|
|
100
|
-
const capability = await query.executeTakeFirst();
|
|
95
|
+
const capability = await withLock(baseQuery, trx)
|
|
96
|
+
.executeTakeFirst();
|
|
101
97
|
if (capability) {
|
|
102
98
|
const cap = capability;
|
|
103
99
|
const metadata = typeof cap.metadata === 'string'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./EpisodicMemory.d.ts" />
|
|
2
|
+
import { withLock } from './util/db-utils.js';
|
|
2
3
|
/**
|
|
3
4
|
* EpisodicMemory groups interactions into semantic chunks (episodes),
|
|
4
5
|
* allowing agents to recall specific scenarios and their outcomes.
|
|
@@ -37,11 +38,11 @@ export class EpisodicMemory {
|
|
|
37
38
|
*/
|
|
38
39
|
async completeEpisode(episodeId, summary, metadata) {
|
|
39
40
|
return await this.db.transaction().execute(async (trx) => {
|
|
40
|
-
const
|
|
41
|
+
const query = trx
|
|
41
42
|
.selectFrom(this.episodesTable)
|
|
42
43
|
.selectAll()
|
|
43
|
-
.where('id', '=', episodeId)
|
|
44
|
-
|
|
44
|
+
.where('id', '=', episodeId);
|
|
45
|
+
const existing = await withLock(query, trx) // Audit Phase 12: Atomic completion lock
|
|
45
46
|
.executeTakeFirst();
|
|
46
47
|
if (!existing)
|
|
47
48
|
throw new Error(`Episode with ID ${episodeId} not found`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./PersonaManager.d.ts" />
|
|
2
|
+
import { withLock } from './util/db-utils.js';
|
|
2
3
|
/**
|
|
3
4
|
* PersonaManager handles persistent agent identities that bridge multiple sessions.
|
|
4
5
|
*/
|
|
@@ -19,17 +20,12 @@ export class PersonaManager {
|
|
|
19
20
|
*/
|
|
20
21
|
async upsertPersona(name, options = {}, trxOrDb = this.db) {
|
|
21
22
|
const runner = async (trx) => {
|
|
22
|
-
|
|
23
|
+
const baseQuery = trx
|
|
23
24
|
.selectFrom(this.personasTable)
|
|
24
25
|
.selectAll()
|
|
25
26
|
.where('name', '=', name);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
29
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
30
|
-
query = query.forUpdate();
|
|
31
|
-
}
|
|
32
|
-
const existing = await query.executeTakeFirst();
|
|
27
|
+
const existing = await withLock(baseQuery, trx)
|
|
28
|
+
.executeTakeFirst();
|
|
33
29
|
const values = {
|
|
34
30
|
name,
|
|
35
31
|
role: options.role || null,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./PolicyEnforcer.d.ts" />
|
|
2
|
+
import { withLock } from './util/db-utils.js';
|
|
2
3
|
/**
|
|
3
4
|
* PolicyEnforcer stores and validates agent autonomous guardrails,
|
|
4
5
|
* such as budgets, safety constraints, and privacy rules.
|
|
@@ -23,16 +24,12 @@ export class PolicyEnforcer {
|
|
|
23
24
|
*/
|
|
24
25
|
async definePolicy(name, type, definition, isEnabled = true, trxOrDb = this.db) {
|
|
25
26
|
const runner = async (trx) => {
|
|
26
|
-
|
|
27
|
+
const query = trx
|
|
27
28
|
.selectFrom(this.policiesTable)
|
|
28
29
|
.select('id')
|
|
29
30
|
.where('name', '=', name);
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
33
|
-
query = query.forUpdate();
|
|
34
|
-
}
|
|
35
|
-
const existing = await query.executeTakeFirst();
|
|
31
|
+
const existing = await withLock(query, trx)
|
|
32
|
+
.executeTakeFirst();
|
|
36
33
|
if (existing) {
|
|
37
34
|
const updated = await trx
|
|
38
35
|
.updateTable(this.policiesTable)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./ResourceMonitor.d.ts" />
|
|
2
|
+
import { sql } from '../raw-builder/sql.js';
|
|
2
3
|
/**
|
|
3
4
|
* ResourceMonitor tracks token usage and costs across sessions.
|
|
4
5
|
*/
|
|
@@ -98,7 +99,7 @@ export class ResourceMonitor {
|
|
|
98
99
|
.select([
|
|
99
100
|
'model_name',
|
|
100
101
|
(eb) => eb.fn
|
|
101
|
-
.sum(
|
|
102
|
+
.sum(sql `input_tokens + output_tokens`)
|
|
102
103
|
.as('totalTokens'),
|
|
103
104
|
(eb) => eb.fn.sum('cost').as('totalCost'),
|
|
104
105
|
])
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./SessionManager.d.ts" />
|
|
2
|
+
import { withLock } from './util/db-utils.js';
|
|
2
3
|
/**
|
|
3
4
|
* SessionManager handles the lifecycle of agentic sessions, including
|
|
4
5
|
* message history, goal tracking, and memory persistence.
|
|
@@ -132,12 +133,12 @@ export class SessionManager {
|
|
|
132
133
|
async upsertGoal(sessionId, description, options = {}) {
|
|
133
134
|
const { status = 'pending', priority = 0, parentId, metadata } = options;
|
|
134
135
|
return await this.db.transaction().execute(async (trx) => {
|
|
135
|
-
const
|
|
136
|
+
const query = trx
|
|
136
137
|
.selectFrom(this.goalsTable)
|
|
137
138
|
.selectAll()
|
|
138
139
|
.where('session_id', '=', sessionId)
|
|
139
|
-
.where('description', '=', description)
|
|
140
|
-
|
|
140
|
+
.where('description', '=', description);
|
|
141
|
+
const existing = await withLock(query, trx) // Audit Phase 13: Atomic goal lock
|
|
141
142
|
.executeTakeFirst();
|
|
142
143
|
if (existing) {
|
|
143
144
|
const updated = await trx
|
|
@@ -208,11 +209,11 @@ export class SessionManager {
|
|
|
208
209
|
// We avoid the Read-Modify-Write race condition by letting the DB handle the merge
|
|
209
210
|
// or by using a strict transaction if the DB doesn't support JSON patching natively.
|
|
210
211
|
const updated = await this.db.transaction().execute(async (trx) => {
|
|
211
|
-
const
|
|
212
|
+
const query = trx
|
|
212
213
|
.selectFrom(this.messagesTable)
|
|
213
214
|
.select('metadata')
|
|
214
|
-
.where('id', '=', messageId)
|
|
215
|
-
|
|
215
|
+
.where('id', '=', messageId);
|
|
216
|
+
const message = await withLock(query, trx) // Lock the row for the duration of the transaction
|
|
216
217
|
.executeTakeFirstOrThrow();
|
|
217
218
|
const metadata = typeof message.metadata === 'string'
|
|
218
219
|
? JSON.parse(message.metadata)
|
|
@@ -14,10 +14,6 @@ export declare class AblationEngine {
|
|
|
14
14
|
private linksTable;
|
|
15
15
|
constructor(db: Kysely<any>, cortex: Cortex, config?: AgenticConfig);
|
|
16
16
|
private get typedDb();
|
|
17
|
-
/**
|
|
18
|
-
* Helper to apply forUpdate only where supported (Skip for SQLite)
|
|
19
|
-
*/
|
|
20
|
-
private withLock;
|
|
21
17
|
/**
|
|
22
18
|
* Identify "Zombies": Items that have never been retrieved/hit and are old.
|
|
23
19
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./AblationEngine.d.ts" />
|
|
2
|
+
import { withLock } from '../util/db-utils.js';
|
|
2
3
|
/**
|
|
3
4
|
* AblationEngine identifies and removes unused or redundant data
|
|
4
5
|
* to keep the agent's context window and database lean.
|
|
@@ -21,17 +22,6 @@ export class AblationEngine {
|
|
|
21
22
|
get typedDb() {
|
|
22
23
|
return this.db;
|
|
23
24
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Helper to apply forUpdate only where supported (Skip for SQLite)
|
|
26
|
-
*/
|
|
27
|
-
withLock(query, trx) {
|
|
28
|
-
const executor = trx.getExecutor();
|
|
29
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
30
|
-
if (adapterName.toLowerCase().includes('sqlite')) {
|
|
31
|
-
return query;
|
|
32
|
-
}
|
|
33
|
-
return query.forUpdate();
|
|
34
|
-
}
|
|
35
25
|
/**
|
|
36
26
|
* Identify "Zombies": Items that have never been retrieved/hit and are old.
|
|
37
27
|
*/
|
|
@@ -53,7 +43,7 @@ export class AblationEngine {
|
|
|
53
43
|
.where('id', 'not in', (eb) => eb.selectFrom(this.linksTable).select('source_id'))
|
|
54
44
|
.where('id', 'not in', (eb) => eb.selectFrom(this.linksTable).select('target_id'))
|
|
55
45
|
.limit(500); // Audit Phase 9: Batch limit
|
|
56
|
-
const knowledgeToPrune = await
|
|
46
|
+
const knowledgeToPrune = await withLock(query, trx).execute();
|
|
57
47
|
if (knowledgeToPrune.length > 0) {
|
|
58
48
|
const candidates = knowledgeToPrune.map((k) => this.cortex.knowledge['parseKnowledge'](k));
|
|
59
49
|
const idsToDelete = [];
|
|
@@ -149,7 +139,7 @@ export class AblationEngine {
|
|
|
149
139
|
.selectFrom(this.knowledgeTable)
|
|
150
140
|
.selectAll()
|
|
151
141
|
.where('id', '=', id);
|
|
152
|
-
const item = (await
|
|
142
|
+
const item = (await withLock(query, trx).executeTakeFirst());
|
|
153
143
|
if (!item)
|
|
154
144
|
return false;
|
|
155
145
|
const metadata = typeof item.metadata === 'string'
|
|
@@ -192,7 +182,7 @@ export class AblationEngine {
|
|
|
192
182
|
.selectFrom(this.knowledgeTable)
|
|
193
183
|
.selectAll()
|
|
194
184
|
.where('id', '=', id);
|
|
195
|
-
const item = (await
|
|
185
|
+
const item = (await withLock(query, t).executeTakeFirst());
|
|
196
186
|
if (!item)
|
|
197
187
|
return false;
|
|
198
188
|
const metadata = typeof item.metadata === 'string'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./ActionRefiner.d.ts" />
|
|
2
|
+
import { withLock } from '../util/db-utils.js';
|
|
2
3
|
/**
|
|
3
4
|
* ActionRefiner analyzes the ActionJournal to find patterns in failures
|
|
4
5
|
* and suggests new CognitiveRules to improve agent performance.
|
|
@@ -71,18 +72,13 @@ export class ActionRefiner {
|
|
|
71
72
|
// Audit Phase 19: Atomic rule proposal via transaction + existence check
|
|
72
73
|
const runner = async (trx) => {
|
|
73
74
|
const rulesTable = this.cortex.config.rulesTable || 'agent_rules';
|
|
74
|
-
|
|
75
|
+
const query = trx
|
|
75
76
|
.selectFrom(rulesTable)
|
|
76
77
|
.select('id')
|
|
77
78
|
.where('table_name', '=', 'agent_actions')
|
|
78
79
|
.where('operation', '=', 'insert')
|
|
79
80
|
.where('metadata', 'like', `%\"targetTool\":\"${toolName}\"%`);
|
|
80
|
-
const
|
|
81
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
82
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
83
|
-
query = query.forUpdate();
|
|
84
|
-
}
|
|
85
|
-
const existing = await query.executeTakeFirst();
|
|
81
|
+
const existing = await withLock(query, trx).executeTakeFirst();
|
|
86
82
|
if (!existing) {
|
|
87
83
|
console.log(`[ActionRefiner] Proposing reflection rule for tool: ${toolName}`);
|
|
88
84
|
await this.cortex.rules.defineRule('agent_actions', 'insert', 'audit', {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference types="./ConflictResolver.d.ts" />
|
|
2
2
|
import { calculateSimilarity } from '../../util/similarity.js';
|
|
3
|
+
import { withLock } from '../util/db-utils.js';
|
|
3
4
|
/**
|
|
4
5
|
* ConflictResolver identifies and resolves logical inconsistencies
|
|
5
6
|
* in the agent's cognitive rules and behavior policies.
|
|
@@ -83,14 +84,14 @@ export class ConflictResolver {
|
|
|
83
84
|
async resolveConflict(tableName, operation) {
|
|
84
85
|
console.log(`[ConflictResolver] Resolving conflict for ${tableName}:${operation}`);
|
|
85
86
|
return await this.db.transaction().execute(async (trx) => {
|
|
86
|
-
const
|
|
87
|
+
const query = trx
|
|
87
88
|
.selectFrom(this.rulesTable)
|
|
88
89
|
.selectAll()
|
|
89
90
|
.where('table_name', '=', tableName)
|
|
90
91
|
.where('operation', '=', operation)
|
|
91
92
|
.where('is_enabled', '=', true)
|
|
92
|
-
.orderBy('created_at', 'desc')
|
|
93
|
-
|
|
93
|
+
.orderBy('created_at', 'desc');
|
|
94
|
+
const rules = (await withLock(query, trx) // Audit Phase 10: Atomic resolution lock
|
|
94
95
|
.execute());
|
|
95
96
|
if (rules.length <= 1)
|
|
96
97
|
return;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/// <reference types="./GoalArchitect.d.ts" />
|
|
2
2
|
import { calculateSimilarity } from '../../util/similarity.js';
|
|
3
|
+
import { withLock } from '../util/db-utils.js';
|
|
3
4
|
/**
|
|
4
5
|
* GoalArchitect enables agents to autonomously deconstruct complex
|
|
5
6
|
* objectives into manageable sub-goals.
|
|
@@ -24,11 +25,11 @@ export class GoalArchitect {
|
|
|
24
25
|
return await this.db.transaction().execute(async (trx) => {
|
|
25
26
|
// 1. Audit Phase 9: Circular Dependency Protection inside transaction
|
|
26
27
|
await this.detectCircularDependency(goalId, new Set(), trx);
|
|
27
|
-
const
|
|
28
|
+
const query = trx
|
|
28
29
|
.selectFrom(this.goalsTable)
|
|
29
30
|
.selectAll()
|
|
30
|
-
.where('id', '=', goalId)
|
|
31
|
-
|
|
31
|
+
.where('id', '=', goalId);
|
|
32
|
+
const goal = (await withLock(query, trx) // Audit Phase 9: Atomic acquisition
|
|
32
33
|
.executeTakeFirst());
|
|
33
34
|
if (!goal)
|
|
34
35
|
throw new Error(`Goal ${goalId} not found`);
|
|
@@ -99,11 +100,11 @@ export class GoalArchitect {
|
|
|
99
100
|
const sortedIds = [...goalIds].sort((a, b) => String(a).localeCompare(String(b)));
|
|
100
101
|
await this.db.transaction().execute(async (trx) => {
|
|
101
102
|
// Pre-lock all rows in deterministic order
|
|
102
|
-
|
|
103
|
+
const query = trx
|
|
103
104
|
.selectFrom(this.goalsTable)
|
|
104
105
|
.select('id')
|
|
105
|
-
.where('id', 'in', sortedIds)
|
|
106
|
-
|
|
106
|
+
.where('id', 'in', sortedIds);
|
|
107
|
+
await withLock(query, trx)
|
|
107
108
|
.execute();
|
|
108
109
|
for (let i = 0; i < goalIds.length; i++) {
|
|
109
110
|
await trx
|
|
@@ -119,11 +120,11 @@ export class GoalArchitect {
|
|
|
119
120
|
*/
|
|
120
121
|
async markGoalAs(goalId, status, outcome) {
|
|
121
122
|
return await this.db.transaction().execute(async (trx) => {
|
|
122
|
-
const
|
|
123
|
+
const query = trx
|
|
123
124
|
.selectFrom(this.goalsTable)
|
|
124
125
|
.selectAll()
|
|
125
|
-
.where('id', '=', goalId)
|
|
126
|
-
|
|
126
|
+
.where('id', '=', goalId);
|
|
127
|
+
const goal = (await withLock(query, trx) // Audit Phase 9: Atomic status/meta update
|
|
127
128
|
.executeTakeFirst());
|
|
128
129
|
if (!goal)
|
|
129
130
|
throw new Error(`Goal ${goalId} not found`);
|
|
@@ -3,6 +3,7 @@ import { FactDistiller } from './distillation/FactDistiller.js';
|
|
|
3
3
|
import { ConflictChallenger } from './distillation/ConflictChallenger.js';
|
|
4
4
|
import { RelationshipArchitect } from './distillation/RelationshipArchitect.js';
|
|
5
5
|
import { KnowledgeConsolidator } from './distillation/KnowledgeConsolidator.js';
|
|
6
|
+
import { withLock } from '../util/db-utils.js';
|
|
6
7
|
/**
|
|
7
8
|
* KnowledgeDistiller extracts structured "KnowledgeItems" from longtail history,
|
|
8
9
|
* allowing agents to build a permanent, queryable knowledge base.
|
|
@@ -104,11 +105,11 @@ export class KnowledgeDistiller {
|
|
|
104
105
|
*/
|
|
105
106
|
async recordHit(id) {
|
|
106
107
|
await this.db.transaction().execute(async (trx) => {
|
|
107
|
-
const
|
|
108
|
+
const query = trx
|
|
108
109
|
.selectFrom(this.knowledgeTable)
|
|
109
110
|
.select(['id', 'entity', 'metadata'])
|
|
110
|
-
.where('id', '=', id)
|
|
111
|
-
|
|
111
|
+
.where('id', '=', id);
|
|
112
|
+
const existing = await withLock(query, trx)
|
|
112
113
|
.executeTakeFirst();
|
|
113
114
|
if (!existing)
|
|
114
115
|
return;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./SelfTestRegistry.d.ts" />
|
|
2
|
+
import { withLock } from '../util/db-utils.js';
|
|
2
3
|
/**
|
|
3
4
|
* SelfTestRegistry allows agents to self-register verification probes
|
|
4
5
|
* that ensure autonomous changes don't violate core logic.
|
|
@@ -55,16 +56,11 @@ export class SelfTestRegistry {
|
|
|
55
56
|
results.push({ name: probe.name, success });
|
|
56
57
|
const updateRunner = async (trx) => {
|
|
57
58
|
// Audit Phase 13: Lock row before updating last_status
|
|
58
|
-
|
|
59
|
+
const query = trx
|
|
59
60
|
.selectFrom('agent_logic_probes')
|
|
60
61
|
.select('id')
|
|
61
62
|
.where('id', '=', probe.id);
|
|
62
|
-
|
|
63
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
64
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
65
|
-
query = query.forUpdate();
|
|
66
|
-
}
|
|
67
|
-
await query.executeTakeFirst();
|
|
63
|
+
await withLock(query, trx).executeTakeFirst();
|
|
68
64
|
await trx
|
|
69
65
|
.updateTable('agent_logic_probes')
|
|
70
66
|
.set({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="./PersonaAuditor.d.ts" />
|
|
2
|
+
import { withLock } from '../../util/db-utils.js';
|
|
2
3
|
export class PersonaAuditor {
|
|
3
4
|
async audit(ctx) {
|
|
4
5
|
const issues = [];
|
|
@@ -41,17 +42,12 @@ export class PersonaAuditor {
|
|
|
41
42
|
console.warn(`[PersonaAuditor] QUARANTINING Persona ${id}: ${reason}`);
|
|
42
43
|
// Use the provided transaction or start a new one to ensure atomicity
|
|
43
44
|
const runner = async (trx) => {
|
|
44
|
-
|
|
45
|
+
const query = trx
|
|
45
46
|
.selectFrom(ctx.personasTable)
|
|
46
47
|
.selectAll()
|
|
47
48
|
.where('id', '=', id);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const adapterName = executor?.adapter?.constructor?.name || executor?.dialect?.constructor?.name || '';
|
|
51
|
-
if (!adapterName.toLowerCase().includes('sqlite')) {
|
|
52
|
-
query = query.forUpdate();
|
|
53
|
-
}
|
|
54
|
-
const persona = await query.executeTakeFirst();
|
|
49
|
+
const persona = await withLock(query, trx)
|
|
50
|
+
.executeTakeFirst();
|
|
55
51
|
if (persona) {
|
|
56
52
|
const metadata = typeof persona.metadata === 'string'
|
|
57
53
|
? JSON.parse(persona.metadata)
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/// <reference types="./KnowledgePromoter.d.ts" />
|
|
2
|
+
import { withLock } from '../../util/db-utils.js';
|
|
2
3
|
export class KnowledgePromoter {
|
|
3
4
|
async promote(db, cortex, config, knowledgeTable, item) {
|
|
4
5
|
return await db.transaction().execute(async (trx) => {
|
|
5
6
|
// Check if a global version already exists
|
|
6
|
-
const
|
|
7
|
+
const query = trx
|
|
7
8
|
.selectFrom(knowledgeTable)
|
|
8
9
|
.selectAll()
|
|
9
10
|
.where('entity', '=', item.entity)
|
|
10
11
|
.where('fact', '=', item.fact)
|
|
11
|
-
.where('source_session_id', 'is', null)
|
|
12
|
-
|
|
12
|
+
.where('source_session_id', 'is', null);
|
|
13
|
+
const existingGlobal = await withLock(query, trx) // Prevent concurrent promotion duplication
|
|
13
14
|
.executeTakeFirst();
|
|
14
15
|
if (existingGlobal) {
|
|
15
16
|
// Reinforce existing global knowledge
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/// <reference types="./db-utils.d.ts" />
|
|
2
|
+
/**
|
|
3
|
+
* Utility function to apply a "FOR UPDATE" lock only where supported (Not in SQLite)
|
|
4
|
+
*/
|
|
5
|
+
export function withLock(query, trx) {
|
|
6
|
+
if (!trx || typeof trx.getExecutor !== 'function') {
|
|
7
|
+
// If we're using a mock or a version of Kysely without getExecutor
|
|
8
|
+
// we default to no lock to prevent failures in tests
|
|
9
|
+
return query;
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const executor = trx.getExecutor();
|
|
13
|
+
const adapterName = executor?.adapter?.constructor?.name ||
|
|
14
|
+
executor?.dialect?.constructor?.name ||
|
|
15
|
+
'';
|
|
16
|
+
if (adapterName.toLowerCase().includes('sqlite')) {
|
|
17
|
+
return query;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
// Fallback if execution fails for some reason
|
|
22
|
+
return query;
|
|
23
|
+
}
|
|
24
|
+
return query.forUpdate();
|
|
25
|
+
}
|
|
@@ -117,6 +117,7 @@ export class AgentSchemaHelper {
|
|
|
117
117
|
.addColumn('tool_name', 'text', (col) => col.notNull())
|
|
118
118
|
.addColumn('arguments', 'text', (col) => col.notNull())
|
|
119
119
|
.addColumn('outcome', 'text')
|
|
120
|
+
.addColumn('error', 'text')
|
|
120
121
|
.addColumn('status', 'text', (col) => col.notNull().defaultTo('pending'))
|
|
121
122
|
.addColumn('duration_ms', 'integer')
|
|
122
123
|
.addColumn('metadata', 'text')
|
|
@@ -89,6 +89,8 @@ export async function setupTestSchema(db) {
|
|
|
89
89
|
.on('comments')
|
|
90
90
|
.column('user_id')
|
|
91
91
|
.execute();
|
|
92
|
+
// Initialize agent schema
|
|
93
|
+
await db.agent.schema.initializeSchema();
|
|
92
94
|
// Initialize NOORMME to discover the schema
|
|
93
95
|
// Temporarily enable warnings to see discovery errors
|
|
94
96
|
const originalWarn = console.warn;
|