opencode-autognosis 2.2.0 → 2.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/dist/database.d.ts +9 -1
- package/dist/database.js +57 -4
- package/dist/unified-api.js +74 -39
- package/package.json +1 -1
package/dist/database.d.ts
CHANGED
|
@@ -13,8 +13,16 @@ export declare class CodeGraphDB {
|
|
|
13
13
|
}): void;
|
|
14
14
|
getJob(id: string): any;
|
|
15
15
|
listJobs(type?: string, limit?: number): any;
|
|
16
|
-
postToBlackboard(author: string, message: string, topic?: string): void;
|
|
16
|
+
postToBlackboard(author: string, message: string, topic?: string, symbolId?: string): void;
|
|
17
17
|
readBlackboard(topic?: string, limit?: number): any;
|
|
18
|
+
getGraffiti(symbolId: string): any;
|
|
19
|
+
acquireLock(resourceId: string, agentName: string, ttlSeconds?: number): void;
|
|
20
|
+
releaseLock(resourceId: string, agentName: string): void;
|
|
21
|
+
isLocked(resourceId: string): {
|
|
22
|
+
owner_agent: string;
|
|
23
|
+
expires_at: string;
|
|
24
|
+
} | undefined;
|
|
25
|
+
listLocks(): any;
|
|
18
26
|
storeIntent(patchId: string, reasoning: string, planId: string): void;
|
|
19
27
|
getIntent(patchId: string): any;
|
|
20
28
|
addArchRule(source: string, target: string): void;
|
package/dist/database.js
CHANGED
|
@@ -146,9 +146,18 @@ export class CodeGraphDB {
|
|
|
146
146
|
author TEXT,
|
|
147
147
|
message TEXT,
|
|
148
148
|
topic TEXT,
|
|
149
|
+
symbol_id TEXT, -- Optional link to a code symbol
|
|
150
|
+
embedding BLOB, -- For semantic search on the blackboard
|
|
149
151
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
150
152
|
);
|
|
151
153
|
|
|
154
|
+
CREATE TABLE IF NOT EXISTS locks (
|
|
155
|
+
resource_id TEXT PRIMARY KEY, -- file path or symbol name
|
|
156
|
+
owner_agent TEXT,
|
|
157
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
158
|
+
expires_at DATETIME
|
|
159
|
+
);
|
|
160
|
+
|
|
152
161
|
CREATE TABLE IF NOT EXISTS intents (
|
|
153
162
|
patch_id TEXT PRIMARY KEY,
|
|
154
163
|
reasoning TEXT,
|
|
@@ -222,11 +231,18 @@ export class CodeGraphDB {
|
|
|
222
231
|
}
|
|
223
232
|
return this.db.prepare("SELECT * FROM background_jobs ORDER BY created_at DESC LIMIT ?").all(limit);
|
|
224
233
|
}
|
|
225
|
-
postToBlackboard(author, message, topic = 'general') {
|
|
234
|
+
postToBlackboard(author, message, topic = 'general', symbolId) {
|
|
235
|
+
const insert = this.db.prepare(`
|
|
236
|
+
INSERT INTO blackboard (author, message, topic, symbol_id)
|
|
237
|
+
VALUES (?, ?, ?, ?)
|
|
238
|
+
RETURNING id
|
|
239
|
+
`);
|
|
240
|
+
const res = insert.get(author, message, topic, symbolId || null);
|
|
241
|
+
// Queue for embedding (blackboard search)
|
|
226
242
|
this.db.prepare(`
|
|
227
|
-
INSERT INTO
|
|
228
|
-
VALUES (?,
|
|
229
|
-
`).run(
|
|
243
|
+
INSERT INTO embedding_queue (chunk_id, text_to_embed)
|
|
244
|
+
VALUES (?, ?)
|
|
245
|
+
`).run(`blackboard-${res.id}`, `${topic.toUpperCase()}: ${message}`);
|
|
230
246
|
}
|
|
231
247
|
readBlackboard(topic, limit = 10) {
|
|
232
248
|
if (topic) {
|
|
@@ -238,6 +254,43 @@ export class CodeGraphDB {
|
|
|
238
254
|
SELECT * FROM blackboard ORDER BY timestamp DESC LIMIT ?
|
|
239
255
|
`).all(limit);
|
|
240
256
|
}
|
|
257
|
+
getGraffiti(symbolId) {
|
|
258
|
+
return this.db.prepare(`
|
|
259
|
+
SELECT author, message, timestamp
|
|
260
|
+
FROM blackboard
|
|
261
|
+
WHERE symbol_id = ?
|
|
262
|
+
ORDER BY timestamp DESC
|
|
263
|
+
`).all(symbolId);
|
|
264
|
+
}
|
|
265
|
+
acquireLock(resourceId, agentName, ttlSeconds = 300) {
|
|
266
|
+
// Check if already locked by someone else
|
|
267
|
+
const current = this.isLocked(resourceId);
|
|
268
|
+
if (current && current.owner_agent !== agentName) {
|
|
269
|
+
throw new Error(`Resource ${resourceId} is already locked by ${current.owner_agent}`);
|
|
270
|
+
}
|
|
271
|
+
const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();
|
|
272
|
+
this.db.prepare(`
|
|
273
|
+
INSERT INTO locks (resource_id, owner_agent, expires_at)
|
|
274
|
+
VALUES (?, ?, ?)
|
|
275
|
+
ON CONFLICT(resource_id) DO UPDATE SET
|
|
276
|
+
owner_agent = excluded.owner_agent,
|
|
277
|
+
expires_at = excluded.expires_at
|
|
278
|
+
`).run(resourceId, agentName, expiresAt);
|
|
279
|
+
}
|
|
280
|
+
releaseLock(resourceId, agentName) {
|
|
281
|
+
this.db.prepare(`
|
|
282
|
+
DELETE FROM locks
|
|
283
|
+
WHERE resource_id = ? AND owner_agent = ?
|
|
284
|
+
`).run(resourceId, agentName);
|
|
285
|
+
}
|
|
286
|
+
isLocked(resourceId) {
|
|
287
|
+
// Automatically prune expired locks
|
|
288
|
+
this.db.prepare("DELETE FROM locks WHERE expires_at < CURRENT_TIMESTAMP").run();
|
|
289
|
+
return this.db.prepare("SELECT * FROM locks WHERE resource_id = ?").get(resourceId);
|
|
290
|
+
}
|
|
291
|
+
listLocks() {
|
|
292
|
+
return this.db.prepare("SELECT * FROM locks").all();
|
|
293
|
+
}
|
|
241
294
|
storeIntent(patchId, reasoning, planId) {
|
|
242
295
|
this.db.prepare(`
|
|
243
296
|
INSERT INTO intents (patch_id, reasoning, plan_id)
|
package/dist/unified-api.js
CHANGED
|
@@ -40,13 +40,13 @@ async function updateBridgePrompt(plugins) {
|
|
|
40
40
|
if (!fsSync.existsSync(bridgePath))
|
|
41
41
|
return "bridge.md not found at " + bridgePath;
|
|
42
42
|
const toolsSection = `
|
|
43
|
-
## Current Consolidated Tools (Autognosis v2.
|
|
43
|
+
## Current Consolidated Tools (Autognosis v2.3)
|
|
44
44
|
- code_search: Universal search (semantic, symbol, filename, content).
|
|
45
45
|
- code_analyze: Deep structural analysis and impact reports.
|
|
46
|
-
- code_context: Working memory (ActiveSet) management and
|
|
47
|
-
- code_read: Precise symbol jumping and file slicing.
|
|
46
|
+
- code_context: Working memory (ActiveSet) management, LRU eviction, and Symbol Graffiti.
|
|
47
|
+
- code_read: Precise symbol jumping and file slicing with Mutex Lock checks.
|
|
48
48
|
- code_propose: Planning, patch generation, PR promotion, and Intent indexing.
|
|
49
|
-
- code_status: System health, background jobs,
|
|
49
|
+
- code_status: System health, background jobs, Multi-Agent Blackboard, and Resource Locks.
|
|
50
50
|
- code_setup: Environment initialization, AI setup, and Architectural Boundaries.
|
|
51
51
|
|
|
52
52
|
## Other Detected Plugins
|
|
@@ -63,14 +63,15 @@ ${plugins.filter(p => p !== "opencode-autognosis").map(p => `- ${p}`).join('\n')
|
|
|
63
63
|
return "Updated bridge.md with consolidated tools and detected plugins.";
|
|
64
64
|
}
|
|
65
65
|
export function unifiedTools() {
|
|
66
|
+
const agentName = process.env.AGENT_NAME || `agent-${process.pid}`;
|
|
66
67
|
return {
|
|
67
68
|
code_search: tool({
|
|
68
69
|
description: "Search the codebase using various engines (filename, content, symbol, or semantic/vector).",
|
|
69
70
|
args: {
|
|
70
71
|
query: tool.schema.string().describe("Search query"),
|
|
71
|
-
mode: tool.schema.enum(["filename", "content", "symbol", "semantic"]).optional().default("filename")
|
|
72
|
-
path: tool.schema.string().optional().default(".")
|
|
73
|
-
limit: tool.schema.number().optional().default(10)
|
|
72
|
+
mode: tool.schema.enum(["filename", "content", "symbol", "semantic"]).optional().default("filename"),
|
|
73
|
+
path: tool.schema.string().optional().default("."),
|
|
74
|
+
limit: tool.schema.number().optional().default(10),
|
|
74
75
|
plan_id: tool.schema.string().optional()
|
|
75
76
|
},
|
|
76
77
|
async execute(args) {
|
|
@@ -106,7 +107,7 @@ export function unifiedTools() {
|
|
|
106
107
|
action: tool.schema.enum(["create", "load", "add", "remove", "status", "list", "close", "evict"]),
|
|
107
108
|
target: tool.schema.string().optional().describe("ActiveSet ID or Chunk IDs"),
|
|
108
109
|
name: tool.schema.string().optional(),
|
|
109
|
-
limit: tool.schema.number().optional().default(5)
|
|
110
|
+
limit: tool.schema.number().optional().default(5),
|
|
110
111
|
plan_id: tool.schema.string().optional()
|
|
111
112
|
},
|
|
112
113
|
async execute(args) {
|
|
@@ -126,7 +127,7 @@ export function unifiedTools() {
|
|
|
126
127
|
}
|
|
127
128
|
}),
|
|
128
129
|
code_read: tool({
|
|
129
|
-
description: "Precise reading of symbols or file slices. Follows
|
|
130
|
+
description: "Precise reading of symbols or file slices. Follows current plan. Checks for locks and returns graffiti.",
|
|
130
131
|
args: {
|
|
131
132
|
symbol: tool.schema.string().optional().describe("Symbol to jump to"),
|
|
132
133
|
file: tool.schema.string().optional().describe("File path to read"),
|
|
@@ -135,24 +136,37 @@ export function unifiedTools() {
|
|
|
135
136
|
plan_id: tool.schema.string().optional()
|
|
136
137
|
},
|
|
137
138
|
async execute(args) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
const resourceId = args.symbol || args.file;
|
|
140
|
+
if (resourceId) {
|
|
141
|
+
getDb().logAccess(resourceId, args.plan_id);
|
|
142
|
+
const lock = getDb().isLocked(resourceId);
|
|
143
|
+
const graffiti = getDb().getGraffiti(resourceId);
|
|
144
|
+
let result;
|
|
145
|
+
if (args.symbol) {
|
|
146
|
+
result = await internal.jump_to_symbol.execute({ symbol: args.symbol, plan_id: args.plan_id });
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
result = await internal.read_slice.execute({ file: args.file, start_line: args.start_line, end_line: args.end_line, plan_id: args.plan_id });
|
|
150
|
+
}
|
|
151
|
+
const parsed = JSON.parse(result);
|
|
152
|
+
return JSON.stringify({
|
|
153
|
+
...parsed,
|
|
154
|
+
coordination: {
|
|
155
|
+
lock_status: lock ? `LOCKED by ${lock.owner_agent}` : "FREE",
|
|
156
|
+
graffiti: graffiti.length > 0 ? graffiti : undefined
|
|
157
|
+
}
|
|
158
|
+
}, null, 2);
|
|
145
159
|
}
|
|
146
|
-
throw new Error("Either 'symbol' or 'file'
|
|
160
|
+
throw new Error("Either 'symbol' or 'file' must be provided.");
|
|
147
161
|
}
|
|
148
162
|
}),
|
|
149
163
|
code_propose: tool({
|
|
150
|
-
description: "Plan, propose, and promote changes.
|
|
164
|
+
description: "Plan, propose, and promote changes. Automatically handles coordination pulse and lock checks.",
|
|
151
165
|
args: {
|
|
152
166
|
action: tool.schema.enum(["plan", "patch", "validate", "finalize", "promote"]),
|
|
153
167
|
symbol: tool.schema.string().optional(),
|
|
154
168
|
intent: tool.schema.string().optional(),
|
|
155
|
-
reasoning: tool.schema.string().optional()
|
|
169
|
+
reasoning: tool.schema.string().optional(),
|
|
156
170
|
message: tool.schema.string().optional(),
|
|
157
171
|
patch_path: tool.schema.string().optional(),
|
|
158
172
|
branch: tool.schema.string().optional(),
|
|
@@ -161,33 +175,38 @@ export function unifiedTools() {
|
|
|
161
175
|
},
|
|
162
176
|
async execute(args) {
|
|
163
177
|
switch (args.action) {
|
|
164
|
-
case "plan":
|
|
178
|
+
case "plan": {
|
|
179
|
+
getDb().postToBlackboard(agentName, `Planning ${args.intent} for ${args.symbol}`, "pulse");
|
|
180
|
+
return internal.brief_fix_loop.execute({ symbol: args.symbol, intent: args.intent });
|
|
181
|
+
}
|
|
165
182
|
case "patch": {
|
|
183
|
+
// 1. Check for locks on all changed files
|
|
166
184
|
const { stdout: diff } = await internal.runCmd("git diff");
|
|
185
|
+
const { stdout: files } = await internal.runCmd("git diff --name-only");
|
|
186
|
+
const changedFiles = files.split('\n').filter(Boolean);
|
|
187
|
+
for (const file of changedFiles) {
|
|
188
|
+
const lock = getDb().isLocked(file);
|
|
189
|
+
if (lock && lock.owner_agent !== agentName) {
|
|
190
|
+
return JSON.stringify({ status: "COLLISION_PREVENTED", message: `File ${file} is locked by ${lock.owner_agent}. Use 'code_status' to investigate.` });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// 2. Run Policy Engine
|
|
167
194
|
const violations = policyEngine.checkDiff(diff);
|
|
168
195
|
if (violations.some(v => v.severity === "error")) {
|
|
169
196
|
return JSON.stringify({ status: "POLICY_VIOLATION", violations, message: "Patch rejected by policy engine." }, null, 2);
|
|
170
197
|
}
|
|
198
|
+
// 3. Prepare patch and record intent
|
|
171
199
|
const res = await internal.prepare_patch.execute({ message: args.message, plan_id: args.plan_id });
|
|
172
200
|
const json = JSON.parse(res);
|
|
173
|
-
if (json.status === "SUCCESS"
|
|
174
|
-
|
|
201
|
+
if (json.status === "SUCCESS") {
|
|
202
|
+
if (args.reasoning)
|
|
203
|
+
getDb().storeIntent(json.patch_id, args.reasoning, args.plan_id || "adhoc");
|
|
204
|
+
getDb().postToBlackboard(agentName, `Proposed patch ${json.patch_id}: ${args.message}`, "pulse");
|
|
175
205
|
}
|
|
176
206
|
return res;
|
|
177
207
|
}
|
|
178
208
|
case "validate": {
|
|
179
|
-
|
|
180
|
-
const { stdout: diff } = await internal.runCmd("git diff --name-only");
|
|
181
|
-
const changedFiles = diff.split('\n').filter(Boolean);
|
|
182
|
-
for (const file of changedFiles) {
|
|
183
|
-
const deps = await internal.extractDependencies.execute({ content: "", ast: null, filePath: file });
|
|
184
|
-
const imports = JSON.parse(deps);
|
|
185
|
-
for (const imp of imports) {
|
|
186
|
-
const violation = getDb().checkArchViolation(file, imp);
|
|
187
|
-
if (violation)
|
|
188
|
-
return JSON.stringify({ status: "ARCH_VIOLATION", file, forbidden_import: imp, rule: violation }, null, 2);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
209
|
+
getDb().postToBlackboard(agentName, `Validating patch ${args.patch_path}`, "pulse");
|
|
191
210
|
return internal.validate_patch.execute({ patch_path: args.patch_path, plan_id: args.plan_id });
|
|
192
211
|
}
|
|
193
212
|
case "promote": {
|
|
@@ -198,22 +217,27 @@ export function unifiedTools() {
|
|
|
198
217
|
execSync(`git apply ${args.patch_path}`);
|
|
199
218
|
execSync(`git add . && git commit -m "${args.message || 'Automated promotion'}"`);
|
|
200
219
|
execSync(`gh pr create --title "${args.message}" --body "Automated promotion from Autognosis v2."`);
|
|
220
|
+
getDb().postToBlackboard(agentName, `Promoted patch to PR on branch ${branch}`, "pulse");
|
|
201
221
|
return JSON.stringify({ status: "SUCCESS", promoted_to: branch, pr: "OPENED" }, null, 2);
|
|
202
222
|
}
|
|
203
223
|
catch (e) {
|
|
204
224
|
return JSON.stringify({ status: "ERROR", message: e.message }, null, 2);
|
|
205
225
|
}
|
|
206
226
|
}
|
|
207
|
-
case "finalize":
|
|
227
|
+
case "finalize": {
|
|
228
|
+
getDb().postToBlackboard(agentName, `Finalized plan ${args.plan_id} with outcome: ${args.outcome}`, "pulse");
|
|
229
|
+
return internal.finalize_plan.execute({ plan_id: args.plan_id, outcome: args.outcome });
|
|
230
|
+
}
|
|
208
231
|
}
|
|
209
232
|
}
|
|
210
233
|
}),
|
|
211
234
|
code_status: tool({
|
|
212
|
-
description: "Monitor system health, background jobs,
|
|
235
|
+
description: "Monitor system health, background jobs, Multi-Agent Blackboard, and Resource Locks.",
|
|
213
236
|
args: {
|
|
214
|
-
mode: tool.schema.enum(["stats", "hot_files", "jobs", "plan", "doctor", "blackboard"]).optional().default("stats"),
|
|
215
|
-
action: tool.schema.enum(["post", "read"]).optional(),
|
|
237
|
+
mode: tool.schema.enum(["stats", "hot_files", "jobs", "plan", "doctor", "blackboard", "locks"]).optional().default("stats"),
|
|
238
|
+
action: tool.schema.enum(["post", "read", "lock", "unlock"]).optional(),
|
|
216
239
|
topic: tool.schema.string().optional().default("general"),
|
|
240
|
+
target: tool.schema.string().optional().describe("Resource ID (file/symbol) for locks or Symbol ID for graffiti"),
|
|
217
241
|
message: tool.schema.string().optional(),
|
|
218
242
|
job_id: tool.schema.string().optional(),
|
|
219
243
|
plan_id: tool.schema.string().optional(),
|
|
@@ -221,9 +245,20 @@ export function unifiedTools() {
|
|
|
221
245
|
},
|
|
222
246
|
async execute(args) {
|
|
223
247
|
switch (args.mode) {
|
|
248
|
+
case "locks": {
|
|
249
|
+
if (args.action === "lock") {
|
|
250
|
+
getDb().acquireLock(args.target, agentName);
|
|
251
|
+
return JSON.stringify({ status: "SUCCESS", message: `Locked ${args.target}` });
|
|
252
|
+
}
|
|
253
|
+
else if (args.action === "unlock") {
|
|
254
|
+
getDb().releaseLock(args.target, agentName);
|
|
255
|
+
return JSON.stringify({ status: "SUCCESS", message: `Unlocked ${args.target}` });
|
|
256
|
+
}
|
|
257
|
+
return JSON.stringify({ status: "SUCCESS", active_locks: getDb().listLocks() });
|
|
258
|
+
}
|
|
224
259
|
case "blackboard": {
|
|
225
260
|
if (args.action === "post") {
|
|
226
|
-
getDb().postToBlackboard(
|
|
261
|
+
getDb().postToBlackboard(agentName, args.message, args.topic, args.target);
|
|
227
262
|
return JSON.stringify({ status: "SUCCESS", message: "Posted to blackboard." });
|
|
228
263
|
}
|
|
229
264
|
return JSON.stringify({ status: "SUCCESS", entries: getDb().readBlackboard(args.topic) });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-autognosis",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Advanced RAG-powered codebase awareness for OpenCode agents. Features Chunk Cards synthesis, hierarchical reasoning, ActiveSet working memory, and performance optimization for enterprise-scale repositories.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|