byterover-cli 3.5.0 → 3.6.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/.env.production +4 -6
- package/dist/agent/core/interfaces/i-cipher-agent.d.ts +1 -0
- package/dist/agent/infra/agent/cipher-agent.d.ts +1 -0
- package/dist/agent/infra/agent/cipher-agent.js +1 -0
- package/dist/oclif/commands/curate/view.js +5 -25
- package/dist/oclif/commands/dream.d.ts +18 -0
- package/dist/oclif/commands/dream.js +230 -0
- package/dist/oclif/commands/query-log/summary.d.ts +18 -0
- package/dist/oclif/commands/query-log/summary.js +75 -0
- package/dist/oclif/commands/query-log/view.d.ts +23 -0
- package/dist/oclif/commands/query-log/view.js +95 -0
- package/dist/oclif/lib/time-filter.d.ts +10 -0
- package/dist/oclif/lib/time-filter.js +21 -0
- package/dist/server/config/environment.d.ts +10 -3
- package/dist/server/config/environment.js +34 -15
- package/dist/server/constants.d.ts +5 -0
- package/dist/server/constants.js +7 -0
- package/dist/server/core/domain/entities/query-log-entry.d.ts +61 -0
- package/dist/server/core/domain/entities/query-log-entry.js +40 -0
- package/dist/server/core/domain/transport/schemas.d.ts +108 -7
- package/dist/server/core/domain/transport/schemas.js +34 -2
- package/dist/server/core/interfaces/executor/i-query-executor.d.ts +23 -2
- package/dist/server/core/interfaces/i-terminal.d.ts +3 -0
- package/dist/server/core/interfaces/i-terminal.js +1 -0
- package/dist/server/core/interfaces/storage/i-query-log-store.d.ts +23 -0
- package/dist/server/core/interfaces/storage/i-query-log-store.js +2 -0
- package/dist/server/core/interfaces/usecase/i-query-log-summary-use-case.d.ts +44 -0
- package/dist/server/core/interfaces/usecase/i-query-log-summary-use-case.js +1 -0
- package/dist/server/core/interfaces/usecase/i-query-log-use-case.d.ts +13 -0
- package/dist/server/core/interfaces/usecase/i-query-log-use-case.js +3 -0
- package/dist/server/infra/daemon/agent-process.js +79 -9
- package/dist/server/infra/daemon/brv-server.js +74 -5
- package/dist/server/infra/dream/dream-lock-service.d.ts +37 -0
- package/dist/server/infra/dream/dream-lock-service.js +88 -0
- package/dist/server/infra/dream/dream-log-schema.d.ts +966 -0
- package/dist/server/infra/dream/dream-log-schema.js +57 -0
- package/dist/server/infra/dream/dream-log-store.d.ts +55 -0
- package/dist/server/infra/dream/dream-log-store.js +141 -0
- package/dist/server/infra/dream/dream-response-schemas.d.ts +219 -0
- package/dist/server/infra/dream/dream-response-schemas.js +38 -0
- package/dist/server/infra/dream/dream-state-schema.d.ts +67 -0
- package/dist/server/infra/dream/dream-state-schema.js +23 -0
- package/dist/server/infra/dream/dream-state-service.d.ts +38 -0
- package/dist/server/infra/dream/dream-state-service.js +91 -0
- package/dist/server/infra/dream/dream-trigger.d.ts +46 -0
- package/dist/server/infra/dream/dream-trigger.js +65 -0
- package/dist/server/infra/dream/dream-undo.d.ts +38 -0
- package/dist/server/infra/dream/dream-undo.js +293 -0
- package/dist/server/infra/dream/operations/consolidate.d.ts +52 -0
- package/dist/server/infra/dream/operations/consolidate.js +514 -0
- package/dist/server/infra/dream/operations/prune.d.ts +45 -0
- package/dist/server/infra/dream/operations/prune.js +362 -0
- package/dist/server/infra/dream/operations/synthesize.d.ts +37 -0
- package/dist/server/infra/dream/operations/synthesize.js +278 -0
- package/dist/server/infra/dream/parse-dream-response.d.ts +11 -0
- package/dist/server/infra/dream/parse-dream-response.js +35 -0
- package/dist/server/infra/executor/curate-executor.js +10 -0
- package/dist/server/infra/executor/dream-executor.d.ts +97 -0
- package/dist/server/infra/executor/dream-executor.js +431 -0
- package/dist/server/infra/executor/query-executor.d.ts +2 -2
- package/dist/server/infra/executor/query-executor.js +92 -22
- package/dist/server/infra/mcp/mcp-server.js +3 -0
- package/dist/server/infra/mcp/tools/brv-curate-tool.js +3 -7
- package/dist/server/infra/mcp/tools/brv-query-tool.js +3 -7
- package/dist/server/infra/mcp/tools/index.d.ts +1 -0
- package/dist/server/infra/mcp/tools/index.js +1 -0
- package/dist/server/infra/mcp/tools/shared-schema.d.ts +3 -0
- package/dist/server/infra/mcp/tools/shared-schema.js +17 -0
- package/dist/server/infra/process/feature-handlers.js +10 -6
- package/dist/server/infra/process/query-log-handler.d.ts +42 -0
- package/dist/server/infra/process/query-log-handler.js +150 -0
- package/dist/server/infra/process/task-router.d.ts +40 -0
- package/dist/server/infra/process/task-router.js +67 -9
- package/dist/server/infra/process/transport-handlers.d.ts +4 -0
- package/dist/server/infra/process/transport-handlers.js +1 -0
- package/dist/server/infra/storage/file-curate-log-store.js +1 -1
- package/dist/server/infra/storage/file-query-log-store.d.ts +81 -0
- package/dist/server/infra/storage/file-query-log-store.js +249 -0
- package/dist/server/infra/transport/handlers/config-handler.js +1 -1
- package/dist/server/infra/usecase/curate-log-use-case.js +7 -3
- package/dist/server/infra/usecase/query-log-summary-narrative-formatter.d.ts +15 -0
- package/dist/server/infra/usecase/query-log-summary-narrative-formatter.js +79 -0
- package/dist/server/infra/usecase/query-log-summary-use-case.d.ts +13 -0
- package/dist/server/infra/usecase/query-log-summary-use-case.js +217 -0
- package/dist/server/infra/usecase/query-log-use-case.d.ts +31 -0
- package/dist/server/infra/usecase/query-log-use-case.js +128 -0
- package/dist/server/utils/log-format-utils.d.ts +5 -0
- package/dist/server/utils/log-format-utils.js +23 -0
- package/dist/shared/transport/events/config-events.d.ts +1 -1
- package/oclif.manifest.json +439 -184
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// ── Operation schemas (discriminated on type) ────────────────────────────────
|
|
3
|
+
const ConsolidateOperationSchema = z.object({
|
|
4
|
+
action: z.enum(['MERGE', 'TEMPORAL_UPDATE', 'CROSS_REFERENCE']),
|
|
5
|
+
inputFiles: z.array(z.string()),
|
|
6
|
+
needsReview: z.boolean(),
|
|
7
|
+
outputFile: z.string().optional(),
|
|
8
|
+
previousTexts: z.record(z.string(), z.string()).optional(),
|
|
9
|
+
reason: z.string(),
|
|
10
|
+
type: z.literal('CONSOLIDATE'),
|
|
11
|
+
});
|
|
12
|
+
const SynthesizeOperationSchema = z.object({
|
|
13
|
+
action: z.enum(['CREATE', 'UPDATE']),
|
|
14
|
+
confidence: z.number().min(0).max(1),
|
|
15
|
+
needsReview: z.boolean(),
|
|
16
|
+
outputFile: z.string(),
|
|
17
|
+
sources: z.array(z.string()),
|
|
18
|
+
type: z.literal('SYNTHESIZE'),
|
|
19
|
+
});
|
|
20
|
+
const PruneOperationSchema = z.object({
|
|
21
|
+
action: z.enum(['ARCHIVE', 'KEEP', 'SUGGEST_MERGE']),
|
|
22
|
+
file: z.string(),
|
|
23
|
+
mergeTarget: z.string().optional(),
|
|
24
|
+
needsReview: z.boolean(),
|
|
25
|
+
reason: z.string(),
|
|
26
|
+
stubPath: z.string().optional(),
|
|
27
|
+
type: z.literal('PRUNE'),
|
|
28
|
+
});
|
|
29
|
+
export const DreamOperationSchema = z.discriminatedUnion('type', [
|
|
30
|
+
ConsolidateOperationSchema,
|
|
31
|
+
SynthesizeOperationSchema,
|
|
32
|
+
PruneOperationSchema,
|
|
33
|
+
]);
|
|
34
|
+
// ── Summary schema ───────────────────────────────────────────────────────────
|
|
35
|
+
export const DreamLogSummarySchema = z.object({
|
|
36
|
+
consolidated: z.number().int().min(0),
|
|
37
|
+
errors: z.number().int().min(0),
|
|
38
|
+
flaggedForReview: z.number().int().min(0),
|
|
39
|
+
pruned: z.number().int().min(0),
|
|
40
|
+
synthesized: z.number().int().min(0),
|
|
41
|
+
});
|
|
42
|
+
// ── Entry schema (discriminated on status) ───────────────────────────────────
|
|
43
|
+
const DreamLogEntryBaseSchema = z.object({
|
|
44
|
+
id: z.string().regex(/^drm-\d+$/),
|
|
45
|
+
operations: z.array(DreamOperationSchema),
|
|
46
|
+
startedAt: z.number(),
|
|
47
|
+
summary: DreamLogSummarySchema,
|
|
48
|
+
taskId: z.string().optional(),
|
|
49
|
+
trigger: z.enum(['agent-idle', 'manual', 'cli']),
|
|
50
|
+
});
|
|
51
|
+
export const DreamLogEntrySchema = z.discriminatedUnion('status', [
|
|
52
|
+
DreamLogEntryBaseSchema.extend({ completedAt: z.number(), status: z.literal('completed') }),
|
|
53
|
+
DreamLogEntryBaseSchema.extend({ abortReason: z.string(), completedAt: z.number(), status: z.literal('partial') }),
|
|
54
|
+
DreamLogEntryBaseSchema.extend({ completedAt: z.number(), error: z.string(), status: z.literal('error') }),
|
|
55
|
+
DreamLogEntryBaseSchema.extend({ status: z.literal('processing') }),
|
|
56
|
+
DreamLogEntryBaseSchema.extend({ completedAt: z.number(), status: z.literal('undone'), undoneAt: z.number() }),
|
|
57
|
+
]);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type DreamLogEntry, type DreamLogStatus } from './dream-log-schema.js';
|
|
2
|
+
type DreamLogStoreOptions = {
|
|
3
|
+
baseDir: string;
|
|
4
|
+
maxEntries?: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* File-based store for dream log entries.
|
|
8
|
+
*
|
|
9
|
+
* Each entry is stored as a JSON file:
|
|
10
|
+
* {baseDir}/dream-log/drm-{timestamp_ms}.json
|
|
11
|
+
*
|
|
12
|
+
* Writes are atomic (tmp → rename). Reads validate with Zod and return null
|
|
13
|
+
* for corrupt/missing files. Prunes oldest entries when maxEntries is exceeded.
|
|
14
|
+
*/
|
|
15
|
+
export declare class DreamLogStore {
|
|
16
|
+
private lastTimestamp;
|
|
17
|
+
private readonly logDir;
|
|
18
|
+
private readonly maxEntries;
|
|
19
|
+
constructor(opts: DreamLogStoreOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Retrieve an entry by ID. Returns null if:
|
|
22
|
+
* - ID format is invalid (security: prevents path traversal)
|
|
23
|
+
* - File does not exist
|
|
24
|
+
* - File content fails Zod validation (corrupted)
|
|
25
|
+
*/
|
|
26
|
+
getById(id: string): Promise<DreamLogEntry | null>;
|
|
27
|
+
/**
|
|
28
|
+
* Generate the next monotonic log entry ID in the format `drm-{timestamp_ms}`.
|
|
29
|
+
* Guaranteed to increase even if called multiple times in the same millisecond.
|
|
30
|
+
*/
|
|
31
|
+
getNextId(): Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* List entries sorted newest-first (by timestamp embedded in filename).
|
|
34
|
+
* Filters (status, after, before) are applied before limit. Skips corrupt entries silently.
|
|
35
|
+
*/
|
|
36
|
+
list({ after, before, limit, status, }?: {
|
|
37
|
+
after?: number;
|
|
38
|
+
before?: number;
|
|
39
|
+
limit?: number;
|
|
40
|
+
status?: DreamLogStatus[];
|
|
41
|
+
}): Promise<DreamLogEntry[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Persist a log entry atomically (write to tmp, then rename).
|
|
44
|
+
* After saving, prunes oldest entries if count exceeds maxEntries (best-effort).
|
|
45
|
+
*/
|
|
46
|
+
save(entry: DreamLogEntry): Promise<void>;
|
|
47
|
+
private entryPath;
|
|
48
|
+
private pruneOldest;
|
|
49
|
+
/**
|
|
50
|
+
* If a "processing" entry is older than STALE_PROCESSING_THRESHOLD_MS, the daemon
|
|
51
|
+
* was killed before it could finalize it. Rewrite as "error" on disk (best-effort).
|
|
52
|
+
*/
|
|
53
|
+
private resolveStale;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, readdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { DREAM_LOG_DIR, DREAM_LOG_ID_PREFIX } from '../../constants.js';
|
|
5
|
+
import { DreamLogEntrySchema } from './dream-log-schema.js';
|
|
6
|
+
const ID_PATTERN = new RegExp(`^${DREAM_LOG_ID_PREFIX}-\\d+$`);
|
|
7
|
+
const DEFAULT_MAX_ENTRIES = 50;
|
|
8
|
+
/** Entries stuck in "processing" longer than this are considered interrupted. */
|
|
9
|
+
const STALE_PROCESSING_THRESHOLD_MS = 10 * 60 * 1000; // 10 minutes
|
|
10
|
+
/**
|
|
11
|
+
* File-based store for dream log entries.
|
|
12
|
+
*
|
|
13
|
+
* Each entry is stored as a JSON file:
|
|
14
|
+
* {baseDir}/dream-log/drm-{timestamp_ms}.json
|
|
15
|
+
*
|
|
16
|
+
* Writes are atomic (tmp → rename). Reads validate with Zod and return null
|
|
17
|
+
* for corrupt/missing files. Prunes oldest entries when maxEntries is exceeded.
|
|
18
|
+
*/
|
|
19
|
+
export class DreamLogStore {
|
|
20
|
+
lastTimestamp = 0;
|
|
21
|
+
logDir;
|
|
22
|
+
maxEntries;
|
|
23
|
+
constructor(opts) {
|
|
24
|
+
this.logDir = join(opts.baseDir, DREAM_LOG_DIR);
|
|
25
|
+
this.maxEntries = opts.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Retrieve an entry by ID. Returns null if:
|
|
29
|
+
* - ID format is invalid (security: prevents path traversal)
|
|
30
|
+
* - File does not exist
|
|
31
|
+
* - File content fails Zod validation (corrupted)
|
|
32
|
+
*/
|
|
33
|
+
async getById(id) {
|
|
34
|
+
if (!ID_PATTERN.test(id))
|
|
35
|
+
return null;
|
|
36
|
+
try {
|
|
37
|
+
const raw = await readFile(this.entryPath(id), 'utf8');
|
|
38
|
+
const parsed = DreamLogEntrySchema.safeParse(JSON.parse(raw));
|
|
39
|
+
if (!parsed.success)
|
|
40
|
+
return null;
|
|
41
|
+
return await this.resolveStale(parsed.data);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Generate the next monotonic log entry ID in the format `drm-{timestamp_ms}`.
|
|
49
|
+
* Guaranteed to increase even if called multiple times in the same millisecond.
|
|
50
|
+
*/
|
|
51
|
+
async getNextId() {
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
this.lastTimestamp = now <= this.lastTimestamp ? this.lastTimestamp + 1 : now;
|
|
54
|
+
return `${DREAM_LOG_ID_PREFIX}-${this.lastTimestamp}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* List entries sorted newest-first (by timestamp embedded in filename).
|
|
58
|
+
* Filters (status, after, before) are applied before limit. Skips corrupt entries silently.
|
|
59
|
+
*/
|
|
60
|
+
async list({ after, before, limit, status, } = {}) {
|
|
61
|
+
let files;
|
|
62
|
+
try {
|
|
63
|
+
const entries = await readdir(this.logDir, { withFileTypes: true });
|
|
64
|
+
files = entries
|
|
65
|
+
.filter((e) => e.isFile() && e.name.endsWith('.json') && ID_PATTERN.test(e.name.slice(0, -5)))
|
|
66
|
+
.map((e) => e.name)
|
|
67
|
+
.sort()
|
|
68
|
+
.reverse();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const hasFilters = Boolean(status?.length || after !== undefined || before !== undefined);
|
|
74
|
+
const filesToRead = hasFilters ? files : files.slice(0, limit ?? files.length);
|
|
75
|
+
const allEntries = [];
|
|
76
|
+
await Promise.all(filesToRead.map(async (filename) => {
|
|
77
|
+
const id = filename.slice(0, -5);
|
|
78
|
+
const entry = await this.getById(id);
|
|
79
|
+
if (entry)
|
|
80
|
+
allEntries.push(entry);
|
|
81
|
+
}));
|
|
82
|
+
allEntries.sort((a, b) => b.startedAt - a.startedAt);
|
|
83
|
+
let results = allEntries;
|
|
84
|
+
if (status?.length) {
|
|
85
|
+
results = results.filter((e) => status.includes(e.status));
|
|
86
|
+
}
|
|
87
|
+
if (after !== undefined) {
|
|
88
|
+
results = results.filter((e) => e.startedAt >= after);
|
|
89
|
+
}
|
|
90
|
+
if (before !== undefined) {
|
|
91
|
+
results = results.filter((e) => e.startedAt <= before);
|
|
92
|
+
}
|
|
93
|
+
return limit === undefined ? results : results.slice(0, limit);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Persist a log entry atomically (write to tmp, then rename).
|
|
97
|
+
* After saving, prunes oldest entries if count exceeds maxEntries (best-effort).
|
|
98
|
+
*/
|
|
99
|
+
async save(entry) {
|
|
100
|
+
DreamLogEntrySchema.parse(entry);
|
|
101
|
+
await mkdir(this.logDir, { recursive: true });
|
|
102
|
+
const filePath = this.entryPath(entry.id);
|
|
103
|
+
const tmpPath = `${filePath}.${randomUUID()}.tmp`;
|
|
104
|
+
await writeFile(tmpPath, JSON.stringify(entry, null, 2), 'utf8');
|
|
105
|
+
await rename(tmpPath, filePath);
|
|
106
|
+
this.pruneOldest().catch(() => { });
|
|
107
|
+
}
|
|
108
|
+
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
109
|
+
entryPath(id) {
|
|
110
|
+
return join(this.logDir, `${id}.json`);
|
|
111
|
+
}
|
|
112
|
+
async pruneOldest() {
|
|
113
|
+
const entries = await readdir(this.logDir, { withFileTypes: true });
|
|
114
|
+
const files = entries
|
|
115
|
+
.filter((e) => e.isFile() && e.name.endsWith('.json') && ID_PATTERN.test(e.name.slice(0, -5)))
|
|
116
|
+
.map((e) => e.name)
|
|
117
|
+
.sort();
|
|
118
|
+
if (files.length <= this.maxEntries)
|
|
119
|
+
return;
|
|
120
|
+
const toDelete = files.slice(0, files.length - this.maxEntries);
|
|
121
|
+
await Promise.all(toDelete.map((f) => rm(join(this.logDir, f), { force: true }).catch(() => { })));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* If a "processing" entry is older than STALE_PROCESSING_THRESHOLD_MS, the daemon
|
|
125
|
+
* was killed before it could finalize it. Rewrite as "error" on disk (best-effort).
|
|
126
|
+
*/
|
|
127
|
+
async resolveStale(entry) {
|
|
128
|
+
if (entry.status !== 'processing')
|
|
129
|
+
return entry;
|
|
130
|
+
if (Date.now() - entry.startedAt <= STALE_PROCESSING_THRESHOLD_MS)
|
|
131
|
+
return entry;
|
|
132
|
+
const recovered = {
|
|
133
|
+
...entry,
|
|
134
|
+
completedAt: Date.now(),
|
|
135
|
+
error: 'Interrupted (daemon terminated)',
|
|
136
|
+
status: 'error',
|
|
137
|
+
};
|
|
138
|
+
this.save(recovered).catch(() => { });
|
|
139
|
+
return recovered;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ConsolidationActionSchema: z.ZodObject<{
|
|
3
|
+
confidence: z.ZodOptional<z.ZodNumber>;
|
|
4
|
+
files: z.ZodArray<z.ZodString, "many">;
|
|
5
|
+
mergedContent: z.ZodOptional<z.ZodString>;
|
|
6
|
+
outputFile: z.ZodOptional<z.ZodString>;
|
|
7
|
+
reason: z.ZodString;
|
|
8
|
+
type: z.ZodEnum<["MERGE", "TEMPORAL_UPDATE", "CROSS_REFERENCE", "SKIP"]>;
|
|
9
|
+
updatedContent: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
type: "MERGE" | "SKIP" | "TEMPORAL_UPDATE" | "CROSS_REFERENCE";
|
|
12
|
+
reason: string;
|
|
13
|
+
files: string[];
|
|
14
|
+
confidence?: number | undefined;
|
|
15
|
+
mergedContent?: string | undefined;
|
|
16
|
+
outputFile?: string | undefined;
|
|
17
|
+
updatedContent?: string | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
type: "MERGE" | "SKIP" | "TEMPORAL_UPDATE" | "CROSS_REFERENCE";
|
|
20
|
+
reason: string;
|
|
21
|
+
files: string[];
|
|
22
|
+
confidence?: number | undefined;
|
|
23
|
+
mergedContent?: string | undefined;
|
|
24
|
+
outputFile?: string | undefined;
|
|
25
|
+
updatedContent?: string | undefined;
|
|
26
|
+
}>;
|
|
27
|
+
export declare const ConsolidateResponseSchema: z.ZodObject<{
|
|
28
|
+
actions: z.ZodArray<z.ZodObject<{
|
|
29
|
+
confidence: z.ZodOptional<z.ZodNumber>;
|
|
30
|
+
files: z.ZodArray<z.ZodString, "many">;
|
|
31
|
+
mergedContent: z.ZodOptional<z.ZodString>;
|
|
32
|
+
outputFile: z.ZodOptional<z.ZodString>;
|
|
33
|
+
reason: z.ZodString;
|
|
34
|
+
type: z.ZodEnum<["MERGE", "TEMPORAL_UPDATE", "CROSS_REFERENCE", "SKIP"]>;
|
|
35
|
+
updatedContent: z.ZodOptional<z.ZodString>;
|
|
36
|
+
}, "strip", z.ZodTypeAny, {
|
|
37
|
+
type: "MERGE" | "SKIP" | "TEMPORAL_UPDATE" | "CROSS_REFERENCE";
|
|
38
|
+
reason: string;
|
|
39
|
+
files: string[];
|
|
40
|
+
confidence?: number | undefined;
|
|
41
|
+
mergedContent?: string | undefined;
|
|
42
|
+
outputFile?: string | undefined;
|
|
43
|
+
updatedContent?: string | undefined;
|
|
44
|
+
}, {
|
|
45
|
+
type: "MERGE" | "SKIP" | "TEMPORAL_UPDATE" | "CROSS_REFERENCE";
|
|
46
|
+
reason: string;
|
|
47
|
+
files: string[];
|
|
48
|
+
confidence?: number | undefined;
|
|
49
|
+
mergedContent?: string | undefined;
|
|
50
|
+
outputFile?: string | undefined;
|
|
51
|
+
updatedContent?: string | undefined;
|
|
52
|
+
}>, "many">;
|
|
53
|
+
}, "strip", z.ZodTypeAny, {
|
|
54
|
+
actions: {
|
|
55
|
+
type: "MERGE" | "SKIP" | "TEMPORAL_UPDATE" | "CROSS_REFERENCE";
|
|
56
|
+
reason: string;
|
|
57
|
+
files: string[];
|
|
58
|
+
confidence?: number | undefined;
|
|
59
|
+
mergedContent?: string | undefined;
|
|
60
|
+
outputFile?: string | undefined;
|
|
61
|
+
updatedContent?: string | undefined;
|
|
62
|
+
}[];
|
|
63
|
+
}, {
|
|
64
|
+
actions: {
|
|
65
|
+
type: "MERGE" | "SKIP" | "TEMPORAL_UPDATE" | "CROSS_REFERENCE";
|
|
66
|
+
reason: string;
|
|
67
|
+
files: string[];
|
|
68
|
+
confidence?: number | undefined;
|
|
69
|
+
mergedContent?: string | undefined;
|
|
70
|
+
outputFile?: string | undefined;
|
|
71
|
+
updatedContent?: string | undefined;
|
|
72
|
+
}[];
|
|
73
|
+
}>;
|
|
74
|
+
export type ConsolidationAction = z.infer<typeof ConsolidationActionSchema>;
|
|
75
|
+
export type ConsolidateResponse = z.infer<typeof ConsolidateResponseSchema>;
|
|
76
|
+
export declare const SynthesisCandidateSchema: z.ZodObject<{
|
|
77
|
+
claim: z.ZodString;
|
|
78
|
+
confidence: z.ZodNumber;
|
|
79
|
+
evidence: z.ZodArray<z.ZodObject<{
|
|
80
|
+
domain: z.ZodString;
|
|
81
|
+
fact: z.ZodString;
|
|
82
|
+
}, "strip", z.ZodTypeAny, {
|
|
83
|
+
domain: string;
|
|
84
|
+
fact: string;
|
|
85
|
+
}, {
|
|
86
|
+
domain: string;
|
|
87
|
+
fact: string;
|
|
88
|
+
}>, "many">;
|
|
89
|
+
placement: z.ZodString;
|
|
90
|
+
title: z.ZodString;
|
|
91
|
+
}, "strip", z.ZodTypeAny, {
|
|
92
|
+
title: string;
|
|
93
|
+
confidence: number;
|
|
94
|
+
claim: string;
|
|
95
|
+
evidence: {
|
|
96
|
+
domain: string;
|
|
97
|
+
fact: string;
|
|
98
|
+
}[];
|
|
99
|
+
placement: string;
|
|
100
|
+
}, {
|
|
101
|
+
title: string;
|
|
102
|
+
confidence: number;
|
|
103
|
+
claim: string;
|
|
104
|
+
evidence: {
|
|
105
|
+
domain: string;
|
|
106
|
+
fact: string;
|
|
107
|
+
}[];
|
|
108
|
+
placement: string;
|
|
109
|
+
}>;
|
|
110
|
+
export declare const SynthesizeResponseSchema: z.ZodObject<{
|
|
111
|
+
syntheses: z.ZodArray<z.ZodObject<{
|
|
112
|
+
claim: z.ZodString;
|
|
113
|
+
confidence: z.ZodNumber;
|
|
114
|
+
evidence: z.ZodArray<z.ZodObject<{
|
|
115
|
+
domain: z.ZodString;
|
|
116
|
+
fact: z.ZodString;
|
|
117
|
+
}, "strip", z.ZodTypeAny, {
|
|
118
|
+
domain: string;
|
|
119
|
+
fact: string;
|
|
120
|
+
}, {
|
|
121
|
+
domain: string;
|
|
122
|
+
fact: string;
|
|
123
|
+
}>, "many">;
|
|
124
|
+
placement: z.ZodString;
|
|
125
|
+
title: z.ZodString;
|
|
126
|
+
}, "strip", z.ZodTypeAny, {
|
|
127
|
+
title: string;
|
|
128
|
+
confidence: number;
|
|
129
|
+
claim: string;
|
|
130
|
+
evidence: {
|
|
131
|
+
domain: string;
|
|
132
|
+
fact: string;
|
|
133
|
+
}[];
|
|
134
|
+
placement: string;
|
|
135
|
+
}, {
|
|
136
|
+
title: string;
|
|
137
|
+
confidence: number;
|
|
138
|
+
claim: string;
|
|
139
|
+
evidence: {
|
|
140
|
+
domain: string;
|
|
141
|
+
fact: string;
|
|
142
|
+
}[];
|
|
143
|
+
placement: string;
|
|
144
|
+
}>, "many">;
|
|
145
|
+
}, "strip", z.ZodTypeAny, {
|
|
146
|
+
syntheses: {
|
|
147
|
+
title: string;
|
|
148
|
+
confidence: number;
|
|
149
|
+
claim: string;
|
|
150
|
+
evidence: {
|
|
151
|
+
domain: string;
|
|
152
|
+
fact: string;
|
|
153
|
+
}[];
|
|
154
|
+
placement: string;
|
|
155
|
+
}[];
|
|
156
|
+
}, {
|
|
157
|
+
syntheses: {
|
|
158
|
+
title: string;
|
|
159
|
+
confidence: number;
|
|
160
|
+
claim: string;
|
|
161
|
+
evidence: {
|
|
162
|
+
domain: string;
|
|
163
|
+
fact: string;
|
|
164
|
+
}[];
|
|
165
|
+
placement: string;
|
|
166
|
+
}[];
|
|
167
|
+
}>;
|
|
168
|
+
export type SynthesisCandidate = z.infer<typeof SynthesisCandidateSchema>;
|
|
169
|
+
export type SynthesizeResponse = z.infer<typeof SynthesizeResponseSchema>;
|
|
170
|
+
export declare const PruneDecisionSchema: z.ZodObject<{
|
|
171
|
+
decision: z.ZodEnum<["ARCHIVE", "KEEP", "MERGE_INTO"]>;
|
|
172
|
+
file: z.ZodString;
|
|
173
|
+
mergeTarget: z.ZodOptional<z.ZodString>;
|
|
174
|
+
reason: z.ZodString;
|
|
175
|
+
}, "strip", z.ZodTypeAny, {
|
|
176
|
+
reason: string;
|
|
177
|
+
file: string;
|
|
178
|
+
decision: "ARCHIVE" | "KEEP" | "MERGE_INTO";
|
|
179
|
+
mergeTarget?: string | undefined;
|
|
180
|
+
}, {
|
|
181
|
+
reason: string;
|
|
182
|
+
file: string;
|
|
183
|
+
decision: "ARCHIVE" | "KEEP" | "MERGE_INTO";
|
|
184
|
+
mergeTarget?: string | undefined;
|
|
185
|
+
}>;
|
|
186
|
+
export declare const PruneResponseSchema: z.ZodObject<{
|
|
187
|
+
decisions: z.ZodArray<z.ZodObject<{
|
|
188
|
+
decision: z.ZodEnum<["ARCHIVE", "KEEP", "MERGE_INTO"]>;
|
|
189
|
+
file: z.ZodString;
|
|
190
|
+
mergeTarget: z.ZodOptional<z.ZodString>;
|
|
191
|
+
reason: z.ZodString;
|
|
192
|
+
}, "strip", z.ZodTypeAny, {
|
|
193
|
+
reason: string;
|
|
194
|
+
file: string;
|
|
195
|
+
decision: "ARCHIVE" | "KEEP" | "MERGE_INTO";
|
|
196
|
+
mergeTarget?: string | undefined;
|
|
197
|
+
}, {
|
|
198
|
+
reason: string;
|
|
199
|
+
file: string;
|
|
200
|
+
decision: "ARCHIVE" | "KEEP" | "MERGE_INTO";
|
|
201
|
+
mergeTarget?: string | undefined;
|
|
202
|
+
}>, "many">;
|
|
203
|
+
}, "strip", z.ZodTypeAny, {
|
|
204
|
+
decisions: {
|
|
205
|
+
reason: string;
|
|
206
|
+
file: string;
|
|
207
|
+
decision: "ARCHIVE" | "KEEP" | "MERGE_INTO";
|
|
208
|
+
mergeTarget?: string | undefined;
|
|
209
|
+
}[];
|
|
210
|
+
}, {
|
|
211
|
+
decisions: {
|
|
212
|
+
reason: string;
|
|
213
|
+
file: string;
|
|
214
|
+
decision: "ARCHIVE" | "KEEP" | "MERGE_INTO";
|
|
215
|
+
mergeTarget?: string | undefined;
|
|
216
|
+
}[];
|
|
217
|
+
}>;
|
|
218
|
+
export type PruneDecision = z.infer<typeof PruneDecisionSchema>;
|
|
219
|
+
export type PruneResponse = z.infer<typeof PruneResponseSchema>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// ── Consolidate ──────────────────────────────────────────────────────────────
|
|
3
|
+
export const ConsolidationActionSchema = z.object({
|
|
4
|
+
confidence: z.number().min(0).max(1).optional(),
|
|
5
|
+
files: z.array(z.string()).min(1),
|
|
6
|
+
mergedContent: z.string().optional(),
|
|
7
|
+
outputFile: z.string().optional(),
|
|
8
|
+
reason: z.string(),
|
|
9
|
+
type: z.enum(['MERGE', 'TEMPORAL_UPDATE', 'CROSS_REFERENCE', 'SKIP']),
|
|
10
|
+
updatedContent: z.string().optional(),
|
|
11
|
+
});
|
|
12
|
+
export const ConsolidateResponseSchema = z.object({
|
|
13
|
+
actions: z.array(ConsolidationActionSchema),
|
|
14
|
+
});
|
|
15
|
+
// ── Synthesize ───────────────────────────────────────────────────────────────
|
|
16
|
+
export const SynthesisCandidateSchema = z.object({
|
|
17
|
+
claim: z.string(),
|
|
18
|
+
confidence: z.number().min(0).max(1),
|
|
19
|
+
evidence: z.array(z.object({
|
|
20
|
+
domain: z.string(),
|
|
21
|
+
fact: z.string(),
|
|
22
|
+
})),
|
|
23
|
+
placement: z.string(),
|
|
24
|
+
title: z.string(),
|
|
25
|
+
});
|
|
26
|
+
export const SynthesizeResponseSchema = z.object({
|
|
27
|
+
syntheses: z.array(SynthesisCandidateSchema),
|
|
28
|
+
});
|
|
29
|
+
// ── Prune ────────────────────────────────────────────────────────────────────
|
|
30
|
+
export const PruneDecisionSchema = z.object({
|
|
31
|
+
decision: z.enum(['ARCHIVE', 'KEEP', 'MERGE_INTO']),
|
|
32
|
+
file: z.string(),
|
|
33
|
+
mergeTarget: z.string().optional(),
|
|
34
|
+
reason: z.string(),
|
|
35
|
+
});
|
|
36
|
+
export const PruneResponseSchema = z.object({
|
|
37
|
+
decisions: z.array(PruneDecisionSchema),
|
|
38
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const PendingMergeSchema: z.ZodObject<{
|
|
3
|
+
mergeTarget: z.ZodString;
|
|
4
|
+
reason: z.ZodString;
|
|
5
|
+
sourceFile: z.ZodString;
|
|
6
|
+
suggestedByDreamId: z.ZodString;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
reason: string;
|
|
9
|
+
mergeTarget: string;
|
|
10
|
+
sourceFile: string;
|
|
11
|
+
suggestedByDreamId: string;
|
|
12
|
+
}, {
|
|
13
|
+
reason: string;
|
|
14
|
+
mergeTarget: string;
|
|
15
|
+
sourceFile: string;
|
|
16
|
+
suggestedByDreamId: string;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const DreamStateSchema: z.ZodObject<{
|
|
19
|
+
curationsSinceDream: z.ZodNumber;
|
|
20
|
+
lastDreamAt: z.ZodNullable<z.ZodString>;
|
|
21
|
+
lastDreamLogId: z.ZodNullable<z.ZodString>;
|
|
22
|
+
pendingMerges: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
23
|
+
mergeTarget: z.ZodString;
|
|
24
|
+
reason: z.ZodString;
|
|
25
|
+
sourceFile: z.ZodString;
|
|
26
|
+
suggestedByDreamId: z.ZodString;
|
|
27
|
+
}, "strip", z.ZodTypeAny, {
|
|
28
|
+
reason: string;
|
|
29
|
+
mergeTarget: string;
|
|
30
|
+
sourceFile: string;
|
|
31
|
+
suggestedByDreamId: string;
|
|
32
|
+
}, {
|
|
33
|
+
reason: string;
|
|
34
|
+
mergeTarget: string;
|
|
35
|
+
sourceFile: string;
|
|
36
|
+
suggestedByDreamId: string;
|
|
37
|
+
}>, "many">>>;
|
|
38
|
+
totalDreams: z.ZodNumber;
|
|
39
|
+
version: z.ZodLiteral<1>;
|
|
40
|
+
}, "strip", z.ZodTypeAny, {
|
|
41
|
+
version: 1;
|
|
42
|
+
curationsSinceDream: number;
|
|
43
|
+
lastDreamAt: string | null;
|
|
44
|
+
lastDreamLogId: string | null;
|
|
45
|
+
pendingMerges: {
|
|
46
|
+
reason: string;
|
|
47
|
+
mergeTarget: string;
|
|
48
|
+
sourceFile: string;
|
|
49
|
+
suggestedByDreamId: string;
|
|
50
|
+
}[];
|
|
51
|
+
totalDreams: number;
|
|
52
|
+
}, {
|
|
53
|
+
version: 1;
|
|
54
|
+
curationsSinceDream: number;
|
|
55
|
+
lastDreamAt: string | null;
|
|
56
|
+
lastDreamLogId: string | null;
|
|
57
|
+
totalDreams: number;
|
|
58
|
+
pendingMerges?: {
|
|
59
|
+
reason: string;
|
|
60
|
+
mergeTarget: string;
|
|
61
|
+
sourceFile: string;
|
|
62
|
+
suggestedByDreamId: string;
|
|
63
|
+
}[] | undefined;
|
|
64
|
+
}>;
|
|
65
|
+
export type DreamState = z.infer<typeof DreamStateSchema>;
|
|
66
|
+
export type PendingMerge = z.infer<typeof PendingMergeSchema>;
|
|
67
|
+
export declare const EMPTY_DREAM_STATE: DreamState;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const PendingMergeSchema = z.object({
|
|
3
|
+
mergeTarget: z.string(),
|
|
4
|
+
reason: z.string(),
|
|
5
|
+
sourceFile: z.string(),
|
|
6
|
+
suggestedByDreamId: z.string(),
|
|
7
|
+
});
|
|
8
|
+
export const DreamStateSchema = z.object({
|
|
9
|
+
curationsSinceDream: z.number().int().min(0),
|
|
10
|
+
lastDreamAt: z.string().datetime().nullable(),
|
|
11
|
+
lastDreamLogId: z.string().nullable(),
|
|
12
|
+
pendingMerges: z.array(PendingMergeSchema).optional().default([]),
|
|
13
|
+
totalDreams: z.number().int().min(0),
|
|
14
|
+
version: z.literal(1),
|
|
15
|
+
});
|
|
16
|
+
export const EMPTY_DREAM_STATE = {
|
|
17
|
+
curationsSinceDream: 0,
|
|
18
|
+
lastDreamAt: null,
|
|
19
|
+
lastDreamLogId: null,
|
|
20
|
+
pendingMerges: [],
|
|
21
|
+
totalDreams: 0,
|
|
22
|
+
version: 1,
|
|
23
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type DreamState } from './dream-state-schema.js';
|
|
2
|
+
type DreamStateServiceOptions = {
|
|
3
|
+
baseDir: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* File-based persistence for dream state.
|
|
7
|
+
*
|
|
8
|
+
* Reads return EMPTY_DREAM_STATE on missing/corrupt files (fail-open).
|
|
9
|
+
* Writes are atomic (tmp → rename) and validate with Zod before persisting.
|
|
10
|
+
*/
|
|
11
|
+
export declare class DreamStateService {
|
|
12
|
+
private readonly stateFilePath;
|
|
13
|
+
constructor(opts: DreamStateServiceOptions);
|
|
14
|
+
/**
|
|
15
|
+
* Read-modify-write under a per-file mutex. Serializes concurrent increments
|
|
16
|
+
* from parallel curate tasks within the same agent process so no updates are lost.
|
|
17
|
+
*/
|
|
18
|
+
incrementCurationCount(): Promise<void>;
|
|
19
|
+
read(): Promise<DreamState>;
|
|
20
|
+
/**
|
|
21
|
+
* Generic read-modify-write under the same per-file mutex used by
|
|
22
|
+
* incrementCurationCount. All writers that mutate dream-state.json based on
|
|
23
|
+
* its current contents (e.g. dream-executor step 7's reset, consolidate's
|
|
24
|
+
* pendingMerges clear) MUST go through this method, otherwise concurrent
|
|
25
|
+
* increments can be silently overwritten.
|
|
26
|
+
*/
|
|
27
|
+
update(updater: (state: DreamState) => DreamState): Promise<DreamState>;
|
|
28
|
+
/**
|
|
29
|
+
* Atomic write (tmp file → rename). Does NOT acquire the per-file mutex.
|
|
30
|
+
*
|
|
31
|
+
* Direct callers that perform a logical read-modify-write by pairing
|
|
32
|
+
* {@link read} + write bypass serialization and may lose updates from
|
|
33
|
+
* concurrent writers. Use {@link update} for any RMW that depends on the
|
|
34
|
+
* current state.
|
|
35
|
+
*/
|
|
36
|
+
write(state: DreamState): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export {};
|