@kodrunhq/opencode-autopilot 1.15.2 → 1.17.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/cli.ts +5 -0
- package/bin/inspect.ts +337 -0
- package/package.json +1 -1
- package/src/agents/autopilot.ts +7 -15
- package/src/config/index.ts +29 -0
- package/src/config/migrations.ts +196 -0
- package/src/config/v7.ts +45 -0
- package/src/config.ts +3 -3
- package/src/health/checks.ts +126 -4
- package/src/health/types.ts +1 -1
- package/src/index.ts +128 -13
- package/src/inspect/formatters.ts +225 -0
- package/src/inspect/repository.ts +882 -0
- package/src/kernel/database.ts +45 -0
- package/src/kernel/migrations.ts +62 -0
- package/src/kernel/repository.ts +571 -0
- package/src/kernel/schema.ts +122 -0
- package/src/kernel/transaction.ts +48 -0
- package/src/kernel/types.ts +65 -0
- package/src/logging/domains.ts +39 -0
- package/src/logging/forensic-writer.ts +177 -0
- package/src/logging/index.ts +4 -0
- package/src/logging/logger.ts +44 -0
- package/src/logging/performance.ts +59 -0
- package/src/logging/rotation.ts +261 -0
- package/src/logging/types.ts +33 -0
- package/src/memory/capture-utils.ts +149 -0
- package/src/memory/capture.ts +82 -67
- package/src/memory/database.ts +74 -12
- package/src/memory/decay.ts +11 -2
- package/src/memory/index.ts +17 -1
- package/src/memory/injector.ts +4 -1
- package/src/memory/lessons.ts +85 -0
- package/src/memory/observations.ts +177 -0
- package/src/memory/preferences.ts +718 -0
- package/src/memory/project-key.ts +6 -0
- package/src/memory/projects.ts +83 -0
- package/src/memory/repository.ts +52 -216
- package/src/memory/retrieval.ts +88 -170
- package/src/memory/schemas.ts +39 -7
- package/src/memory/types.ts +4 -0
- package/src/observability/context-display.ts +8 -0
- package/src/observability/event-handlers.ts +69 -20
- package/src/observability/event-store.ts +29 -1
- package/src/observability/forensic-log.ts +167 -0
- package/src/observability/forensic-schemas.ts +77 -0
- package/src/observability/forensic-types.ts +10 -0
- package/src/observability/index.ts +21 -27
- package/src/observability/log-reader.ts +161 -111
- package/src/observability/log-writer.ts +41 -83
- package/src/observability/retention.ts +2 -2
- package/src/observability/session-logger.ts +36 -57
- package/src/observability/summary-generator.ts +31 -19
- package/src/observability/types.ts +12 -24
- package/src/orchestrator/contracts/invariants.ts +14 -0
- package/src/orchestrator/contracts/legacy-result-adapter.ts +8 -20
- package/src/orchestrator/error-context.ts +24 -0
- package/src/orchestrator/fallback/event-handler.ts +47 -3
- package/src/orchestrator/handlers/architect.ts +2 -1
- package/src/orchestrator/handlers/build-utils.ts +118 -0
- package/src/orchestrator/handlers/build.ts +42 -219
- package/src/orchestrator/handlers/retrospective.ts +2 -2
- package/src/orchestrator/handlers/types.ts +0 -1
- package/src/orchestrator/lesson-memory.ts +36 -11
- package/src/orchestrator/orchestration-logger.ts +53 -24
- package/src/orchestrator/phase.ts +8 -4
- package/src/orchestrator/progress.ts +63 -0
- package/src/orchestrator/state.ts +79 -17
- package/src/projects/database.ts +47 -0
- package/src/projects/repository.ts +264 -0
- package/src/projects/resolve.ts +301 -0
- package/src/projects/schemas.ts +30 -0
- package/src/projects/types.ts +12 -0
- package/src/review/memory.ts +39 -11
- package/src/review/parse-findings.ts +116 -0
- package/src/review/pipeline.ts +3 -107
- package/src/review/selection.ts +38 -4
- package/src/scoring/time-provider.ts +23 -0
- package/src/tools/doctor.ts +28 -4
- package/src/tools/forensics.ts +7 -12
- package/src/tools/logs.ts +38 -11
- package/src/tools/memory-preferences.ts +157 -0
- package/src/tools/memory-status.ts +17 -96
- package/src/tools/orchestrate.ts +108 -90
- package/src/tools/pipeline-report.ts +3 -2
- package/src/tools/quick.ts +2 -2
- package/src/tools/replay.ts +42 -0
- package/src/tools/review.ts +46 -7
- package/src/tools/session-stats.ts +3 -2
- package/src/tools/summary.ts +43 -0
- package/src/utils/paths.ts +20 -1
- package/src/utils/random.ts +33 -0
- package/src/ux/session-summary.ts +56 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Legacy path-hash project key.
|
|
5
|
+
*
|
|
6
|
+
* Kept only for migration compatibility while the runtime moves to the
|
|
7
|
+
* project registry / stable project identity model.
|
|
8
|
+
*/
|
|
3
9
|
export function computeProjectKey(projectPath: string): string {
|
|
4
10
|
return createHash("sha256").update(projectPath).digest("hex");
|
|
5
11
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project repository operations.
|
|
3
|
+
*
|
|
4
|
+
* Handles project upsert and path resolution.
|
|
5
|
+
* Extracted from repository.ts for better module organization.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Database } from "bun:sqlite";
|
|
11
|
+
import { getMemoryDb } from "./database";
|
|
12
|
+
import { projectSchema } from "./schemas";
|
|
13
|
+
import type { Project } from "./types";
|
|
14
|
+
|
|
15
|
+
/** Resolve optional db parameter to singleton fallback. */
|
|
16
|
+
function resolveDb(db?: Database): Database {
|
|
17
|
+
return db ?? getMemoryDb();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Map a snake_case DB row to camelCase Project. */
|
|
21
|
+
function rowToProject(row: Record<string, unknown>): Project {
|
|
22
|
+
return {
|
|
23
|
+
id: row.id as string,
|
|
24
|
+
path: row.path as string,
|
|
25
|
+
name: row.name as string,
|
|
26
|
+
firstSeenAt: ((row.first_seen_at as string) ?? (row.last_updated as string)) as string,
|
|
27
|
+
lastUpdated: row.last_updated as string,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create or replace a project record.
|
|
33
|
+
*/
|
|
34
|
+
export function upsertProject(project: Project, db?: Database): void {
|
|
35
|
+
const validated = projectSchema.parse(project);
|
|
36
|
+
const d = resolveDb(db);
|
|
37
|
+
const firstSeenAt = validated.firstSeenAt ?? validated.lastUpdated;
|
|
38
|
+
d.run(
|
|
39
|
+
`INSERT INTO projects (id, path, name, first_seen_at, last_updated)
|
|
40
|
+
VALUES (?, ?, ?, ?, ?)
|
|
41
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
42
|
+
path = excluded.path,
|
|
43
|
+
name = excluded.name,
|
|
44
|
+
first_seen_at = COALESCE(projects.first_seen_at, excluded.first_seen_at),
|
|
45
|
+
last_updated = excluded.last_updated`,
|
|
46
|
+
[validated.id, validated.path, validated.name, firstSeenAt, validated.lastUpdated],
|
|
47
|
+
);
|
|
48
|
+
d.run("UPDATE project_paths SET is_current = 0, last_updated = ? WHERE project_id = ?", [
|
|
49
|
+
validated.lastUpdated,
|
|
50
|
+
validated.id,
|
|
51
|
+
]);
|
|
52
|
+
d.run(
|
|
53
|
+
`INSERT INTO project_paths (project_id, path, first_seen_at, last_updated, is_current)
|
|
54
|
+
VALUES (?, ?, ?, ?, 1)
|
|
55
|
+
ON CONFLICT(project_id, path) DO UPDATE SET
|
|
56
|
+
last_updated = excluded.last_updated,
|
|
57
|
+
is_current = 1`,
|
|
58
|
+
[validated.id, validated.path, firstSeenAt, validated.lastUpdated],
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get a project by its filesystem path. Returns null if not found.
|
|
64
|
+
*/
|
|
65
|
+
export function getProjectByPath(path: string, db?: Database): Project | null {
|
|
66
|
+
const d = resolveDb(db);
|
|
67
|
+
const row = d
|
|
68
|
+
.query(
|
|
69
|
+
`SELECT p.*
|
|
70
|
+
FROM projects p
|
|
71
|
+
WHERE p.path = ?
|
|
72
|
+
UNION ALL
|
|
73
|
+
SELECT p.*
|
|
74
|
+
FROM project_paths pp
|
|
75
|
+
JOIN projects p ON p.id = pp.project_id
|
|
76
|
+
WHERE pp.path = ?
|
|
77
|
+
AND NOT EXISTS (SELECT 1 FROM projects p2 WHERE p2.path = ?)
|
|
78
|
+
ORDER BY last_updated DESC
|
|
79
|
+
LIMIT 1`,
|
|
80
|
+
)
|
|
81
|
+
.get(path, path, path) as Record<string, unknown> | null;
|
|
82
|
+
return row ? rowToProject(row) : null;
|
|
83
|
+
}
|
package/src/memory/repository.ts
CHANGED
|
@@ -1,217 +1,53 @@
|
|
|
1
|
-
import type { Database } from "bun:sqlite";
|
|
2
|
-
import { OBSERVATION_TYPES } from "./constants";
|
|
3
|
-
import { getMemoryDb } from "./database";
|
|
4
|
-
import { observationSchema, preferenceSchema, projectSchema } from "./schemas";
|
|
5
|
-
import type { Observation, ObservationType, Preference, Project } from "./types";
|
|
6
|
-
|
|
7
|
-
/** Resolve optional db parameter to singleton fallback. */
|
|
8
|
-
function resolveDb(db?: Database): Database {
|
|
9
|
-
return db ?? getMemoryDb();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** Validate observation type at runtime. */
|
|
13
|
-
function parseObservationType(value: unknown): ObservationType {
|
|
14
|
-
if (typeof value === "string" && (OBSERVATION_TYPES as readonly string[]).includes(value)) {
|
|
15
|
-
return value as ObservationType;
|
|
16
|
-
}
|
|
17
|
-
return "context"; // safe fallback for corrupt/unknown types
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** Map a snake_case DB row to camelCase Observation. */
|
|
21
|
-
function rowToObservation(row: Record<string, unknown>): Observation {
|
|
22
|
-
return {
|
|
23
|
-
id: row.id as number,
|
|
24
|
-
projectId: (row.project_id as string) ?? null,
|
|
25
|
-
sessionId: row.session_id as string,
|
|
26
|
-
type: parseObservationType(row.type),
|
|
27
|
-
content: row.content as string,
|
|
28
|
-
summary: row.summary as string,
|
|
29
|
-
confidence: row.confidence as number,
|
|
30
|
-
accessCount: row.access_count as number,
|
|
31
|
-
createdAt: row.created_at as string,
|
|
32
|
-
lastAccessed: row.last_accessed as string,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Map a snake_case DB row to camelCase Project. */
|
|
37
|
-
function rowToProject(row: Record<string, unknown>): Project {
|
|
38
|
-
return {
|
|
39
|
-
id: row.id as string,
|
|
40
|
-
path: row.path as string,
|
|
41
|
-
name: row.name as string,
|
|
42
|
-
lastUpdated: row.last_updated as string,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Map a snake_case DB row to camelCase Preference. */
|
|
47
|
-
function rowToPreference(row: Record<string, unknown>): Preference {
|
|
48
|
-
return {
|
|
49
|
-
id: row.id as string,
|
|
50
|
-
key: row.key as string,
|
|
51
|
-
value: row.value as string,
|
|
52
|
-
confidence: row.confidence as number,
|
|
53
|
-
sourceSession: (row.source_session as string) ?? null,
|
|
54
|
-
createdAt: row.created_at as string,
|
|
55
|
-
lastUpdated: row.last_updated as string,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Insert an observation. Validates via Zod before writing.
|
|
61
|
-
* Returns the observation with the generated id.
|
|
62
|
-
*/
|
|
63
|
-
export function insertObservation(obs: Omit<Observation, "id">, db?: Database): Observation {
|
|
64
|
-
const validated = observationSchema.omit({ id: true }).parse(obs);
|
|
65
|
-
const d = resolveDb(db);
|
|
66
|
-
|
|
67
|
-
d.run(
|
|
68
|
-
`INSERT INTO observations (project_id, session_id, type, content, summary, confidence, access_count, created_at, last_accessed)
|
|
69
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
70
|
-
[
|
|
71
|
-
validated.projectId,
|
|
72
|
-
validated.sessionId,
|
|
73
|
-
validated.type,
|
|
74
|
-
validated.content,
|
|
75
|
-
validated.summary,
|
|
76
|
-
validated.confidence,
|
|
77
|
-
validated.accessCount,
|
|
78
|
-
validated.createdAt,
|
|
79
|
-
validated.lastAccessed,
|
|
80
|
-
],
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const row = d.query("SELECT last_insert_rowid() as id").get() as { id: number };
|
|
84
|
-
return { ...validated, id: row.id };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Search observations using FTS5 MATCH with BM25 ranking.
|
|
89
|
-
* Filters by projectId (null for user-level observations).
|
|
90
|
-
*/
|
|
91
|
-
export function searchObservations(
|
|
92
|
-
query: string,
|
|
93
|
-
projectId: string | null,
|
|
94
|
-
limit = 20,
|
|
95
|
-
db?: Database,
|
|
96
|
-
): Array<Observation & { ftsRank: number }> {
|
|
97
|
-
const d = resolveDb(db);
|
|
98
|
-
|
|
99
|
-
const projectFilter = projectId === null ? "AND o.project_id IS NULL" : "AND o.project_id = ?";
|
|
100
|
-
|
|
101
|
-
// Sanitize FTS5 query — wrap in double-quotes to prevent operator injection
|
|
102
|
-
const safeFtsQuery = `"${query.replace(/"/g, '""')}"`;
|
|
103
|
-
const params: Array<string | number> =
|
|
104
|
-
projectId === null ? [safeFtsQuery, limit] : [safeFtsQuery, projectId, limit];
|
|
105
|
-
|
|
106
|
-
const rows = d
|
|
107
|
-
.query(
|
|
108
|
-
`SELECT o.*, bm25(observations_fts) as fts_rank
|
|
109
|
-
FROM observations_fts f
|
|
110
|
-
JOIN observations o ON o.id = f.rowid
|
|
111
|
-
WHERE observations_fts MATCH ?
|
|
112
|
-
${projectFilter}
|
|
113
|
-
ORDER BY fts_rank
|
|
114
|
-
LIMIT ?`,
|
|
115
|
-
)
|
|
116
|
-
.all(...params) as Array<Record<string, unknown>>;
|
|
117
|
-
|
|
118
|
-
return rows.map((row) => ({
|
|
119
|
-
...rowToObservation(row),
|
|
120
|
-
ftsRank: row.fts_rank as number,
|
|
121
|
-
}));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Create or replace a project record.
|
|
126
|
-
*/
|
|
127
|
-
export function upsertProject(project: Project, db?: Database): void {
|
|
128
|
-
const validated = projectSchema.parse(project);
|
|
129
|
-
const d = resolveDb(db);
|
|
130
|
-
d.run(`INSERT OR REPLACE INTO projects (id, path, name, last_updated) VALUES (?, ?, ?, ?)`, [
|
|
131
|
-
validated.id,
|
|
132
|
-
validated.path,
|
|
133
|
-
validated.name,
|
|
134
|
-
validated.lastUpdated,
|
|
135
|
-
]);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Get a project by its filesystem path. Returns null if not found.
|
|
140
|
-
*/
|
|
141
|
-
export function getProjectByPath(path: string, db?: Database): Project | null {
|
|
142
|
-
const d = resolveDb(db);
|
|
143
|
-
const row = d.query("SELECT * FROM projects WHERE path = ?").get(path) as Record<
|
|
144
|
-
string,
|
|
145
|
-
unknown
|
|
146
|
-
> | null;
|
|
147
|
-
return row ? rowToProject(row) : null;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Get observations filtered by project_id, ordered by created_at DESC.
|
|
152
|
-
*/
|
|
153
|
-
export function getObservationsByProject(
|
|
154
|
-
projectId: string | null,
|
|
155
|
-
limit = 50,
|
|
156
|
-
db?: Database,
|
|
157
|
-
): readonly Observation[] {
|
|
158
|
-
const d = resolveDb(db);
|
|
159
|
-
|
|
160
|
-
const whereClause = projectId === null ? "WHERE project_id IS NULL" : "WHERE project_id = ?";
|
|
161
|
-
const params: Array<string | number> = projectId === null ? [limit] : [projectId, limit];
|
|
162
|
-
|
|
163
|
-
const rows = d
|
|
164
|
-
.query(`SELECT * FROM observations ${whereClause} ORDER BY created_at DESC LIMIT ?`)
|
|
165
|
-
.all(...params) as Array<Record<string, unknown>>;
|
|
166
|
-
|
|
167
|
-
return rows.map(rowToObservation);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
1
|
/**
|
|
171
|
-
*
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
2
|
+
* Memory repository - unified exports for observations, preferences, projects, and lessons.
|
|
3
|
+
*
|
|
4
|
+
* This module re-exports all public functions from focused sub-modules:
|
|
5
|
+
* - observations.ts: Observation CRUD and search
|
|
6
|
+
* - preferences.ts: Preference records with evidence tracking
|
|
7
|
+
* - projects.ts: Project metadata and path resolution
|
|
8
|
+
* - lessons.ts: Extracted lessons retrieval
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export { listRelevantLessons } from "./lessons";
|
|
14
|
+
// Re-export all public functions from sub-modules
|
|
15
|
+
export {
|
|
16
|
+
deleteObservation,
|
|
17
|
+
getObservationsByProject,
|
|
18
|
+
getRecentFailureObservations,
|
|
19
|
+
insertObservation,
|
|
20
|
+
searchObservations,
|
|
21
|
+
updateAccessCount,
|
|
22
|
+
} from "./observations";
|
|
23
|
+
export {
|
|
24
|
+
deletePreferenceRecord,
|
|
25
|
+
deletePreferencesByKey,
|
|
26
|
+
getAllPreferences,
|
|
27
|
+
getConfirmedPreferencesForProject,
|
|
28
|
+
getPreferenceRecordById,
|
|
29
|
+
type ListPreferenceRecordOptions,
|
|
30
|
+
listPreferenceEvidence,
|
|
31
|
+
listPreferenceRecords,
|
|
32
|
+
type PreferenceEvidencePruneOptions,
|
|
33
|
+
type PreferenceMutationResult,
|
|
34
|
+
type PreferencePruneOptions,
|
|
35
|
+
type PreferencePruneStatus,
|
|
36
|
+
type PreferenceUpsertInput,
|
|
37
|
+
prunePreferenceEvidence,
|
|
38
|
+
prunePreferences,
|
|
39
|
+
type UpsertPreferenceRecordInput,
|
|
40
|
+
upsertPreference,
|
|
41
|
+
upsertPreferenceRecord,
|
|
42
|
+
} from "./preferences";
|
|
43
|
+
export { getProjectByPath, upsertProject } from "./projects";
|
|
44
|
+
|
|
45
|
+
// Re-export types for convenience
|
|
46
|
+
export type {
|
|
47
|
+
Observation,
|
|
48
|
+
ObservationType,
|
|
49
|
+
Preference,
|
|
50
|
+
PreferenceEvidence,
|
|
51
|
+
PreferenceRecord,
|
|
52
|
+
Project,
|
|
53
|
+
} from "./types";
|