@kodrunhq/opencode-autopilot 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/configure-tui.ts +1 -1
- package/package.json +1 -1
- package/src/config.ts +76 -14
- package/src/index.ts +39 -2
- package/src/memory/capture.ts +205 -0
- package/src/memory/constants.ts +26 -0
- package/src/memory/database.ts +103 -0
- package/src/memory/decay.ts +94 -0
- package/src/memory/index.ts +24 -0
- package/src/memory/injector.ts +85 -0
- package/src/memory/project-key.ts +5 -0
- package/src/memory/repository.ts +217 -0
- package/src/memory/retrieval.ts +260 -0
- package/src/memory/schemas.ts +34 -0
- package/src/memory/types.ts +12 -0
- package/src/orchestrator/arena.ts +31 -0
- package/src/orchestrator/handlers/architect.ts +2 -2
- package/src/orchestrator/skill-injection.ts +3 -3
- package/src/tools/configure.ts +1 -1
- package/src/tools/memory-status.ts +164 -0
- package/src/tools/orchestrate.ts +8 -9
|
@@ -82,9 +82,9 @@ export async function loadAdaptiveSkillContext(
|
|
|
82
82
|
|
|
83
83
|
const matchingSkills = filterSkillsByStack(allSkills, projectTags);
|
|
84
84
|
return buildMultiSkillContext(matchingSkills, tokenBudget);
|
|
85
|
-
} catch
|
|
86
|
-
// Best-effort
|
|
87
|
-
|
|
85
|
+
} catch {
|
|
86
|
+
// Best-effort: all errors return empty string. Caller (injectSkillContext)
|
|
87
|
+
// logs the error — no need to re-throw since the call site is also best-effort.
|
|
88
88
|
return "";
|
|
89
89
|
}
|
|
90
90
|
}
|
package/src/tools/configure.ts
CHANGED
|
@@ -314,7 +314,7 @@ async function handleCommit(configPath?: string): Promise<string> {
|
|
|
314
314
|
}
|
|
315
315
|
const newConfig = {
|
|
316
316
|
...currentConfig,
|
|
317
|
-
version:
|
|
317
|
+
version: 5 as const,
|
|
318
318
|
configured: true,
|
|
319
319
|
groups: groupsRecord,
|
|
320
320
|
overrides: currentConfig.overrides ?? {},
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* oc_memory_status tool — inspect memory system state.
|
|
3
|
+
*
|
|
4
|
+
* Shows observation counts, storage size, recent observations,
|
|
5
|
+
* preferences, and per-type breakdowns. Follows the *Core + tool()
|
|
6
|
+
* pattern from create-agent.ts.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Database } from "bun:sqlite";
|
|
12
|
+
import { statSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { tool } from "@opencode-ai/plugin";
|
|
15
|
+
import { DB_FILE, MEMORY_DIR, OBSERVATION_TYPES } from "../memory/constants";
|
|
16
|
+
import { getMemoryDb } from "../memory/database";
|
|
17
|
+
import { getAllPreferences } from "../memory/repository";
|
|
18
|
+
import { getGlobalConfigDir } from "../utils/paths";
|
|
19
|
+
|
|
20
|
+
interface MemoryStatusResult {
|
|
21
|
+
readonly stats: {
|
|
22
|
+
readonly totalObservations: number;
|
|
23
|
+
readonly totalProjects: number;
|
|
24
|
+
readonly totalPreferences: number;
|
|
25
|
+
readonly storageSizeKb: number;
|
|
26
|
+
readonly observationsByType: Record<string, number>;
|
|
27
|
+
} | null;
|
|
28
|
+
readonly recentObservations: readonly {
|
|
29
|
+
readonly type: string;
|
|
30
|
+
readonly summary: string;
|
|
31
|
+
readonly createdAt: string;
|
|
32
|
+
readonly confidence: number;
|
|
33
|
+
}[];
|
|
34
|
+
readonly preferences: readonly {
|
|
35
|
+
readonly key: string;
|
|
36
|
+
readonly value: string;
|
|
37
|
+
readonly confidence: number;
|
|
38
|
+
}[];
|
|
39
|
+
readonly error?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Core function for memory status inspection.
|
|
44
|
+
* Accepts a Database instance for testability (or uses the singleton).
|
|
45
|
+
*/
|
|
46
|
+
export function memoryStatusCore(
|
|
47
|
+
_args: { readonly detail?: "summary" | "full" },
|
|
48
|
+
dbOrPath?: Database | string,
|
|
49
|
+
): MemoryStatusResult {
|
|
50
|
+
let ownedDb: Database | null = null;
|
|
51
|
+
try {
|
|
52
|
+
if (typeof dbOrPath === "string") {
|
|
53
|
+
ownedDb = new Database(dbOrPath);
|
|
54
|
+
}
|
|
55
|
+
const db = dbOrPath instanceof Database ? dbOrPath : (ownedDb ?? getMemoryDb());
|
|
56
|
+
|
|
57
|
+
// Count observations
|
|
58
|
+
const obsCountRow = db.query("SELECT COUNT(*) as cnt FROM observations").get() as {
|
|
59
|
+
cnt: number;
|
|
60
|
+
};
|
|
61
|
+
const totalObservations = obsCountRow.cnt;
|
|
62
|
+
|
|
63
|
+
// Count by type
|
|
64
|
+
const typeRows = db
|
|
65
|
+
.query("SELECT type, COUNT(*) as cnt FROM observations GROUP BY type")
|
|
66
|
+
.all() as Array<{ type: string; cnt: number }>;
|
|
67
|
+
|
|
68
|
+
const observationsByType: Record<string, number> = {};
|
|
69
|
+
for (const t of OBSERVATION_TYPES) {
|
|
70
|
+
observationsByType[t] = 0;
|
|
71
|
+
}
|
|
72
|
+
for (const row of typeRows) {
|
|
73
|
+
observationsByType[row.type] = row.cnt;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Count projects
|
|
77
|
+
const projCountRow = db.query("SELECT COUNT(*) as cnt FROM projects").get() as {
|
|
78
|
+
cnt: number;
|
|
79
|
+
};
|
|
80
|
+
const totalProjects = projCountRow.cnt;
|
|
81
|
+
|
|
82
|
+
// Count preferences
|
|
83
|
+
const prefCountRow = db.query("SELECT COUNT(*) as cnt FROM preferences").get() as {
|
|
84
|
+
cnt: number;
|
|
85
|
+
};
|
|
86
|
+
const totalPreferences = prefCountRow.cnt;
|
|
87
|
+
|
|
88
|
+
// Storage size — derive from actual DB path, not always the global default
|
|
89
|
+
let storageSizeKb = 0;
|
|
90
|
+
try {
|
|
91
|
+
const statPath =
|
|
92
|
+
typeof dbOrPath === "string" && dbOrPath !== ":memory:"
|
|
93
|
+
? dbOrPath
|
|
94
|
+
: join(getGlobalConfigDir(), MEMORY_DIR, DB_FILE);
|
|
95
|
+
const stat = statSync(statPath);
|
|
96
|
+
storageSizeKb = Math.round(stat.size / 1024);
|
|
97
|
+
} catch {
|
|
98
|
+
// DB might be in-memory or path doesn't exist
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Recent observations (last 10)
|
|
102
|
+
const recentRows = db
|
|
103
|
+
.query(
|
|
104
|
+
"SELECT type, summary, created_at, confidence FROM observations ORDER BY created_at DESC LIMIT 10",
|
|
105
|
+
)
|
|
106
|
+
.all() as Array<{
|
|
107
|
+
type: string;
|
|
108
|
+
summary: string;
|
|
109
|
+
created_at: string;
|
|
110
|
+
confidence: number;
|
|
111
|
+
}>;
|
|
112
|
+
|
|
113
|
+
const recentObservations = recentRows.map((row) => ({
|
|
114
|
+
type: row.type,
|
|
115
|
+
summary: row.summary,
|
|
116
|
+
createdAt: row.created_at,
|
|
117
|
+
confidence: row.confidence,
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
// All preferences
|
|
121
|
+
const allPrefs = getAllPreferences(db);
|
|
122
|
+
const preferences = allPrefs.map((p) => ({
|
|
123
|
+
key: p.key,
|
|
124
|
+
value: p.value,
|
|
125
|
+
confidence: p.confidence,
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
stats: {
|
|
130
|
+
totalObservations,
|
|
131
|
+
totalProjects,
|
|
132
|
+
totalPreferences,
|
|
133
|
+
storageSizeKb,
|
|
134
|
+
observationsByType,
|
|
135
|
+
},
|
|
136
|
+
recentObservations,
|
|
137
|
+
preferences,
|
|
138
|
+
};
|
|
139
|
+
} catch (err) {
|
|
140
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
141
|
+
return {
|
|
142
|
+
stats: null,
|
|
143
|
+
recentObservations: [],
|
|
144
|
+
preferences: [],
|
|
145
|
+
error: `Memory system error: ${detail}`,
|
|
146
|
+
};
|
|
147
|
+
} finally {
|
|
148
|
+
ownedDb?.close();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const ocMemoryStatus = tool({
|
|
153
|
+
description:
|
|
154
|
+
"Show memory system status: observation counts, recent memories, preferences, and storage size.",
|
|
155
|
+
args: {
|
|
156
|
+
detail: tool.schema
|
|
157
|
+
.enum(["summary", "full"])
|
|
158
|
+
.default("summary")
|
|
159
|
+
.describe("Level of detail to show"),
|
|
160
|
+
},
|
|
161
|
+
async execute(args) {
|
|
162
|
+
return JSON.stringify(memoryStatusCore(args), null, 2);
|
|
163
|
+
},
|
|
164
|
+
});
|
package/src/tools/orchestrate.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { DispatchResult } from "../orchestrator/handlers/types";
|
|
|
5
5
|
import { buildLessonContext } from "../orchestrator/lesson-injection";
|
|
6
6
|
import { loadLessonMemory } from "../orchestrator/lesson-memory";
|
|
7
7
|
import { completePhase, getNextPhase } from "../orchestrator/phase";
|
|
8
|
-
import {
|
|
8
|
+
import { loadAdaptiveSkillContext } from "../orchestrator/skill-injection";
|
|
9
9
|
import { createInitialState, loadState, patchState, saveState } from "../orchestrator/state";
|
|
10
10
|
import type { Phase } from "../orchestrator/types";
|
|
11
11
|
import { isEnoentError } from "../utils/fs-helpers";
|
|
@@ -96,17 +96,16 @@ async function injectLessonContext(
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
* Attempt to inject
|
|
99
|
+
* Attempt to inject stack-filtered adaptive skill context into a dispatch prompt.
|
|
100
100
|
* Best-effort: failures are silently swallowed to avoid breaking dispatch.
|
|
101
101
|
*/
|
|
102
|
-
async function injectSkillContext(prompt: string): Promise<string> {
|
|
102
|
+
async function injectSkillContext(prompt: string, projectRoot?: string): Promise<string> {
|
|
103
103
|
try {
|
|
104
104
|
const baseDir = getGlobalConfigDir();
|
|
105
|
-
const
|
|
106
|
-
const ctx = buildSkillContext(content);
|
|
105
|
+
const ctx = await loadAdaptiveSkillContext(baseDir, projectRoot ?? process.cwd());
|
|
107
106
|
if (ctx) return prompt + ctx;
|
|
108
|
-
} catch {
|
|
109
|
-
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.warn("[opencode-autopilot] skill injection failed:", err);
|
|
110
109
|
}
|
|
111
110
|
return prompt;
|
|
112
111
|
}
|
|
@@ -146,7 +145,7 @@ async function processHandlerResult(
|
|
|
146
145
|
handlerResult.phase,
|
|
147
146
|
artifactDir,
|
|
148
147
|
);
|
|
149
|
-
const withSkills = await injectSkillContext(enrichedPrompt);
|
|
148
|
+
const withSkills = await injectSkillContext(enrichedPrompt, join(artifactDir, ".."));
|
|
150
149
|
if (withSkills !== handlerResult.prompt) {
|
|
151
150
|
return JSON.stringify({ ...handlerResult, prompt: withSkills });
|
|
152
151
|
}
|
|
@@ -163,7 +162,7 @@ async function processHandlerResult(
|
|
|
163
162
|
handlerResult.phase as string,
|
|
164
163
|
artifactDir,
|
|
165
164
|
);
|
|
166
|
-
const skillSuffix = await injectSkillContext("");
|
|
165
|
+
const skillSuffix = await injectSkillContext("", join(artifactDir, ".."));
|
|
167
166
|
const combinedSuffix = lessonSuffix + (skillSuffix || "");
|
|
168
167
|
if (combinedSuffix) {
|
|
169
168
|
const enrichedAgents = handlerResult.agents.map((entry) => ({
|