@karmaniverous/jeeves-meta 0.8.0 → 0.9.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/README.md +2 -1
- package/dist/cli/jeeves-meta/index.js +74 -14
- package/dist/index.d.ts +3 -0
- package/dist/index.js +74 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ HTTP service for the Jeeves knowledge synthesis engine. Provides a Fastify API,
|
|
|
10
10
|
- **Three-step orchestration** — architect, builder, critic with conditional re-architecture
|
|
11
11
|
- **Discovery via watcher** — filesystem-based meta discovery via `/walk` endpoint (no Qdrant dependency)
|
|
12
12
|
- **Ownership tree** — hierarchical scoping with child meta rollup
|
|
13
|
+
- **Cross-meta references** — `_crossRefs` declares relationships to other metas; referenced `_content` included as architect/builder context
|
|
13
14
|
- **Archive management** — timestamped snapshots with configurable pruning
|
|
14
15
|
- **Lock staging** — write to `.lock` → copy to `meta.json` → archive (crash-safe)
|
|
15
16
|
- **Virtual rule registration** — registers 3 watcher inference rules at startup with retry
|
|
@@ -54,7 +55,7 @@ jeeves-meta service install --config /path/to/jeeves-meta.config.json
|
|
|
54
55
|
| GET | `/metas/:path` | Single meta detail with optional archive |
|
|
55
56
|
| GET | `/preview` | Dry-run: preview inputs for next synthesis |
|
|
56
57
|
| POST | `/synthesize` | Enqueue synthesis (stalest or specific path) |
|
|
57
|
-
| POST | `/seed` | Create `.meta/` directory + meta.json |
|
|
58
|
+
| POST | `/seed` | Create `.meta/` directory + meta.json (optional `crossRefs`) |
|
|
58
59
|
| POST | `/unlock` | Remove `.lock` file from a meta entity |
|
|
59
60
|
| GET | `/config` | Query sanitized config with optional JSONPath (`?path=$.schedule`) |
|
|
60
61
|
|
|
@@ -1230,6 +1230,22 @@ function condenseScopeFiles(files, maxIndividual = 30) {
|
|
|
1230
1230
|
.map(([pattern, count]) => pattern + ' (' + count.toString() + ' files)')
|
|
1231
1231
|
.join('\n');
|
|
1232
1232
|
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Read a meta.json file and extract its `_content` field.
|
|
1235
|
+
*
|
|
1236
|
+
* @param metaJsonPath - Absolute path to a meta.json file.
|
|
1237
|
+
* @returns The `_content` string, or null if missing/unreadable.
|
|
1238
|
+
*/
|
|
1239
|
+
function readMetaContent(metaJsonPath) {
|
|
1240
|
+
try {
|
|
1241
|
+
const raw = readFileSync(metaJsonPath, 'utf8');
|
|
1242
|
+
const meta = JSON.parse(raw);
|
|
1243
|
+
return meta._content ?? null;
|
|
1244
|
+
}
|
|
1245
|
+
catch {
|
|
1246
|
+
return null;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1233
1249
|
/**
|
|
1234
1250
|
* Build the context package for a synthesis cycle.
|
|
1235
1251
|
*
|
|
@@ -1245,15 +1261,18 @@ async function buildContextPackage(node, meta, watcher) {
|
|
|
1245
1261
|
// Child meta outputs
|
|
1246
1262
|
const childMetas = {};
|
|
1247
1263
|
for (const child of node.children) {
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1264
|
+
childMetas[child.ownerPath] = readMetaContent(join(child.metaPath, 'meta.json'));
|
|
1265
|
+
}
|
|
1266
|
+
// Cross-referenced meta outputs
|
|
1267
|
+
const crossRefMetas = {};
|
|
1268
|
+
const seen = new Set();
|
|
1269
|
+
for (const refPath of meta._crossRefs ?? []) {
|
|
1270
|
+
if (refPath === node.ownerPath || refPath === node.metaPath)
|
|
1271
|
+
continue;
|
|
1272
|
+
if (seen.has(refPath))
|
|
1273
|
+
continue;
|
|
1274
|
+
seen.add(refPath);
|
|
1275
|
+
crossRefMetas[refPath] = readMetaContent(join(refPath, '.meta', 'meta.json'));
|
|
1257
1276
|
}
|
|
1258
1277
|
// Archive paths
|
|
1259
1278
|
const archives = listArchiveFiles(node.metaPath);
|
|
@@ -1262,6 +1281,7 @@ async function buildContextPackage(node, meta, watcher) {
|
|
|
1262
1281
|
scopeFiles,
|
|
1263
1282
|
deltaFiles,
|
|
1264
1283
|
childMetas,
|
|
1284
|
+
crossRefMetas,
|
|
1265
1285
|
previousContent: meta._content ?? null,
|
|
1266
1286
|
previousFeedback: meta._feedback ?? null,
|
|
1267
1287
|
steer: meta._steer ?? null,
|
|
@@ -1275,6 +1295,15 @@ async function buildContextPackage(node, meta, watcher) {
|
|
|
1275
1295
|
*
|
|
1276
1296
|
* @module orchestrator/buildTask
|
|
1277
1297
|
*/
|
|
1298
|
+
/** Append a keyed record of meta outputs as subsections, if non-empty. */
|
|
1299
|
+
function appendMetaSections(sections, heading, metas) {
|
|
1300
|
+
if (Object.keys(metas).length === 0)
|
|
1301
|
+
return;
|
|
1302
|
+
sections.push('', heading);
|
|
1303
|
+
for (const [path, content] of Object.entries(metas)) {
|
|
1304
|
+
sections.push(`### ${path}`, typeof content === 'string' ? content : '(not yet synthesized)');
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1278
1307
|
/** Append optional context sections shared across all step prompts. */
|
|
1279
1308
|
function appendSharedSections(sections, ctx, options) {
|
|
1280
1309
|
const opts = {
|
|
@@ -1283,6 +1312,7 @@ function appendSharedSections(sections, ctx, options) {
|
|
|
1283
1312
|
includePreviousFeedback: true,
|
|
1284
1313
|
feedbackHeading: '## PREVIOUS FEEDBACK',
|
|
1285
1314
|
includeChildMetas: true,
|
|
1315
|
+
includeCrossRefs: true,
|
|
1286
1316
|
...options,
|
|
1287
1317
|
};
|
|
1288
1318
|
if (opts.includeSteer && ctx.steer) {
|
|
@@ -1294,11 +1324,11 @@ function appendSharedSections(sections, ctx, options) {
|
|
|
1294
1324
|
if (opts.includePreviousFeedback && ctx.previousFeedback) {
|
|
1295
1325
|
sections.push('', opts.feedbackHeading, ctx.previousFeedback);
|
|
1296
1326
|
}
|
|
1297
|
-
if (opts.includeChildMetas
|
|
1298
|
-
sections
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1327
|
+
if (opts.includeChildMetas) {
|
|
1328
|
+
appendMetaSections(sections, '## CHILD META OUTPUTS', ctx.childMetas);
|
|
1329
|
+
}
|
|
1330
|
+
if (opts.includeCrossRefs) {
|
|
1331
|
+
appendMetaSections(sections, '## CROSS-REFERENCED METAS', ctx.crossRefMetas);
|
|
1302
1332
|
}
|
|
1303
1333
|
}
|
|
1304
1334
|
/**
|
|
@@ -1382,6 +1412,7 @@ function buildCriticTask(ctx, meta, config) {
|
|
|
1382
1412
|
includePreviousContent: false,
|
|
1383
1413
|
feedbackHeading: '## YOUR PREVIOUS FEEDBACK',
|
|
1384
1414
|
includeChildMetas: false,
|
|
1415
|
+
includeCrossRefs: false,
|
|
1385
1416
|
});
|
|
1386
1417
|
sections.push('', '## OUTPUT FORMAT', 'Return your evaluation as Markdown text. Be specific and actionable.');
|
|
1387
1418
|
return sections.join('\n');
|
|
@@ -1417,6 +1448,11 @@ const metaJsonSchema = z
|
|
|
1417
1448
|
_id: z.uuid(),
|
|
1418
1449
|
/** Human-provided steering prompt. Optional. */
|
|
1419
1450
|
_steer: z.string().optional(),
|
|
1451
|
+
/**
|
|
1452
|
+
* Explicit cross-references to other meta owner paths.
|
|
1453
|
+
* Referenced metas' _content is included as architect/builder context.
|
|
1454
|
+
*/
|
|
1455
|
+
_crossRefs: z.array(z.string()).optional(),
|
|
1420
1456
|
/** Architect system prompt used this turn. Defaults from config. */
|
|
1421
1457
|
_architect: z.string().optional(),
|
|
1422
1458
|
/**
|
|
@@ -9917,6 +9953,27 @@ function registerMetasRoutes(app, deps) {
|
|
|
9917
9953
|
score: Math.round(score * 100) / 100,
|
|
9918
9954
|
},
|
|
9919
9955
|
};
|
|
9956
|
+
// Cross-refs status
|
|
9957
|
+
const crossRefsRaw = meta._crossRefs;
|
|
9958
|
+
if (Array.isArray(crossRefsRaw) && crossRefsRaw.length > 0) {
|
|
9959
|
+
response.crossRefs = crossRefsRaw.map((refPath) => {
|
|
9960
|
+
const rp = String(refPath);
|
|
9961
|
+
const refMetaFile = join(rp, '.meta', 'meta.json');
|
|
9962
|
+
if (!existsSync(refMetaFile))
|
|
9963
|
+
return { path: rp, status: 'missing' };
|
|
9964
|
+
try {
|
|
9965
|
+
const refMeta = JSON.parse(readFileSync(refMetaFile, 'utf8'));
|
|
9966
|
+
return {
|
|
9967
|
+
path: rp,
|
|
9968
|
+
status: 'resolved',
|
|
9969
|
+
hasContent: Boolean(refMeta._content),
|
|
9970
|
+
};
|
|
9971
|
+
}
|
|
9972
|
+
catch {
|
|
9973
|
+
return { path: rp, status: 'missing' };
|
|
9974
|
+
}
|
|
9975
|
+
});
|
|
9976
|
+
}
|
|
9920
9977
|
// Archive
|
|
9921
9978
|
if (query.includeArchive) {
|
|
9922
9979
|
const archiveFiles = listArchiveFiles(targetNode.metaPath);
|
|
@@ -10034,6 +10091,7 @@ function registerPreviewRoute(app, deps) {
|
|
|
10034
10091
|
*/
|
|
10035
10092
|
const seedBodySchema = z.object({
|
|
10036
10093
|
path: z.string().min(1),
|
|
10094
|
+
crossRefs: z.array(z.string()).optional(),
|
|
10037
10095
|
});
|
|
10038
10096
|
function registerSeedRoute(app, deps) {
|
|
10039
10097
|
app.post('/seed', (request, reply) => {
|
|
@@ -10048,6 +10106,8 @@ function registerSeedRoute(app, deps) {
|
|
|
10048
10106
|
deps.logger.info({ metaDir }, 'creating .meta directory');
|
|
10049
10107
|
mkdirSync(metaDir, { recursive: true });
|
|
10050
10108
|
const metaJson = { _id: randomUUID() };
|
|
10109
|
+
if (body.crossRefs !== undefined)
|
|
10110
|
+
metaJson._crossRefs = body.crossRefs;
|
|
10051
10111
|
const metaJsonPath = join(metaDir, 'meta.json');
|
|
10052
10112
|
deps.logger.info({ metaJsonPath }, 'writing meta.json');
|
|
10053
10113
|
writeFileSync(metaJsonPath, JSON.stringify(metaJson, null, 2) + '\n');
|
package/dist/index.d.ts
CHANGED
|
@@ -125,6 +125,7 @@ type MetaError = z.infer<typeof metaErrorSchema>;
|
|
|
125
125
|
declare const metaJsonSchema: z.ZodObject<{
|
|
126
126
|
_id: z.ZodUUID;
|
|
127
127
|
_steer: z.ZodOptional<z.ZodString>;
|
|
128
|
+
_crossRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
128
129
|
_architect: z.ZodOptional<z.ZodString>;
|
|
129
130
|
_builder: z.ZodOptional<z.ZodString>;
|
|
130
131
|
_critic: z.ZodOptional<z.ZodString>;
|
|
@@ -253,6 +254,8 @@ interface MetaContext {
|
|
|
253
254
|
deltaFiles: string[];
|
|
254
255
|
/** Child _content outputs, keyed by relative path. */
|
|
255
256
|
childMetas: Record<string, unknown>;
|
|
257
|
+
/** Cross-referenced meta _content outputs, keyed by owner path. */
|
|
258
|
+
crossRefMetas: Record<string, unknown>;
|
|
256
259
|
/** _content from the last cycle, or null on first run. */
|
|
257
260
|
previousContent: string | null;
|
|
258
261
|
/** _feedback from the last cycle, or null on first run. */
|
package/dist/index.js
CHANGED
|
@@ -1222,6 +1222,22 @@ function condenseScopeFiles(files, maxIndividual = 30) {
|
|
|
1222
1222
|
.map(([pattern, count]) => pattern + ' (' + count.toString() + ' files)')
|
|
1223
1223
|
.join('\n');
|
|
1224
1224
|
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Read a meta.json file and extract its `_content` field.
|
|
1227
|
+
*
|
|
1228
|
+
* @param metaJsonPath - Absolute path to a meta.json file.
|
|
1229
|
+
* @returns The `_content` string, or null if missing/unreadable.
|
|
1230
|
+
*/
|
|
1231
|
+
function readMetaContent(metaJsonPath) {
|
|
1232
|
+
try {
|
|
1233
|
+
const raw = readFileSync(metaJsonPath, 'utf8');
|
|
1234
|
+
const meta = JSON.parse(raw);
|
|
1235
|
+
return meta._content ?? null;
|
|
1236
|
+
}
|
|
1237
|
+
catch {
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1225
1241
|
/**
|
|
1226
1242
|
* Build the context package for a synthesis cycle.
|
|
1227
1243
|
*
|
|
@@ -1237,15 +1253,18 @@ async function buildContextPackage(node, meta, watcher) {
|
|
|
1237
1253
|
// Child meta outputs
|
|
1238
1254
|
const childMetas = {};
|
|
1239
1255
|
for (const child of node.children) {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1256
|
+
childMetas[child.ownerPath] = readMetaContent(join(child.metaPath, 'meta.json'));
|
|
1257
|
+
}
|
|
1258
|
+
// Cross-referenced meta outputs
|
|
1259
|
+
const crossRefMetas = {};
|
|
1260
|
+
const seen = new Set();
|
|
1261
|
+
for (const refPath of meta._crossRefs ?? []) {
|
|
1262
|
+
if (refPath === node.ownerPath || refPath === node.metaPath)
|
|
1263
|
+
continue;
|
|
1264
|
+
if (seen.has(refPath))
|
|
1265
|
+
continue;
|
|
1266
|
+
seen.add(refPath);
|
|
1267
|
+
crossRefMetas[refPath] = readMetaContent(join(refPath, '.meta', 'meta.json'));
|
|
1249
1268
|
}
|
|
1250
1269
|
// Archive paths
|
|
1251
1270
|
const archives = listArchiveFiles(node.metaPath);
|
|
@@ -1254,6 +1273,7 @@ async function buildContextPackage(node, meta, watcher) {
|
|
|
1254
1273
|
scopeFiles,
|
|
1255
1274
|
deltaFiles,
|
|
1256
1275
|
childMetas,
|
|
1276
|
+
crossRefMetas,
|
|
1257
1277
|
previousContent: meta._content ?? null,
|
|
1258
1278
|
previousFeedback: meta._feedback ?? null,
|
|
1259
1279
|
steer: meta._steer ?? null,
|
|
@@ -1267,6 +1287,15 @@ async function buildContextPackage(node, meta, watcher) {
|
|
|
1267
1287
|
*
|
|
1268
1288
|
* @module orchestrator/buildTask
|
|
1269
1289
|
*/
|
|
1290
|
+
/** Append a keyed record of meta outputs as subsections, if non-empty. */
|
|
1291
|
+
function appendMetaSections(sections, heading, metas) {
|
|
1292
|
+
if (Object.keys(metas).length === 0)
|
|
1293
|
+
return;
|
|
1294
|
+
sections.push('', heading);
|
|
1295
|
+
for (const [path, content] of Object.entries(metas)) {
|
|
1296
|
+
sections.push(`### ${path}`, typeof content === 'string' ? content : '(not yet synthesized)');
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1270
1299
|
/** Append optional context sections shared across all step prompts. */
|
|
1271
1300
|
function appendSharedSections(sections, ctx, options) {
|
|
1272
1301
|
const opts = {
|
|
@@ -1275,6 +1304,7 @@ function appendSharedSections(sections, ctx, options) {
|
|
|
1275
1304
|
includePreviousFeedback: true,
|
|
1276
1305
|
feedbackHeading: '## PREVIOUS FEEDBACK',
|
|
1277
1306
|
includeChildMetas: true,
|
|
1307
|
+
includeCrossRefs: true,
|
|
1278
1308
|
...options,
|
|
1279
1309
|
};
|
|
1280
1310
|
if (opts.includeSteer && ctx.steer) {
|
|
@@ -1286,11 +1316,11 @@ function appendSharedSections(sections, ctx, options) {
|
|
|
1286
1316
|
if (opts.includePreviousFeedback && ctx.previousFeedback) {
|
|
1287
1317
|
sections.push('', opts.feedbackHeading, ctx.previousFeedback);
|
|
1288
1318
|
}
|
|
1289
|
-
if (opts.includeChildMetas
|
|
1290
|
-
sections
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1319
|
+
if (opts.includeChildMetas) {
|
|
1320
|
+
appendMetaSections(sections, '## CHILD META OUTPUTS', ctx.childMetas);
|
|
1321
|
+
}
|
|
1322
|
+
if (opts.includeCrossRefs) {
|
|
1323
|
+
appendMetaSections(sections, '## CROSS-REFERENCED METAS', ctx.crossRefMetas);
|
|
1294
1324
|
}
|
|
1295
1325
|
}
|
|
1296
1326
|
/**
|
|
@@ -1374,6 +1404,7 @@ function buildCriticTask(ctx, meta, config) {
|
|
|
1374
1404
|
includePreviousContent: false,
|
|
1375
1405
|
feedbackHeading: '## YOUR PREVIOUS FEEDBACK',
|
|
1376
1406
|
includeChildMetas: false,
|
|
1407
|
+
includeCrossRefs: false,
|
|
1377
1408
|
});
|
|
1378
1409
|
sections.push('', '## OUTPUT FORMAT', 'Return your evaluation as Markdown text. Be specific and actionable.');
|
|
1379
1410
|
return sections.join('\n');
|
|
@@ -1409,6 +1440,11 @@ const metaJsonSchema = z
|
|
|
1409
1440
|
_id: z.uuid(),
|
|
1410
1441
|
/** Human-provided steering prompt. Optional. */
|
|
1411
1442
|
_steer: z.string().optional(),
|
|
1443
|
+
/**
|
|
1444
|
+
* Explicit cross-references to other meta owner paths.
|
|
1445
|
+
* Referenced metas' _content is included as architect/builder context.
|
|
1446
|
+
*/
|
|
1447
|
+
_crossRefs: z.array(z.string()).optional(),
|
|
1412
1448
|
/** Architect system prompt used this turn. Defaults from config. */
|
|
1413
1449
|
_architect: z.string().optional(),
|
|
1414
1450
|
/**
|
|
@@ -9913,6 +9949,27 @@ function registerMetasRoutes(app, deps) {
|
|
|
9913
9949
|
score: Math.round(score * 100) / 100,
|
|
9914
9950
|
},
|
|
9915
9951
|
};
|
|
9952
|
+
// Cross-refs status
|
|
9953
|
+
const crossRefsRaw = meta._crossRefs;
|
|
9954
|
+
if (Array.isArray(crossRefsRaw) && crossRefsRaw.length > 0) {
|
|
9955
|
+
response.crossRefs = crossRefsRaw.map((refPath) => {
|
|
9956
|
+
const rp = String(refPath);
|
|
9957
|
+
const refMetaFile = join(rp, '.meta', 'meta.json');
|
|
9958
|
+
if (!existsSync(refMetaFile))
|
|
9959
|
+
return { path: rp, status: 'missing' };
|
|
9960
|
+
try {
|
|
9961
|
+
const refMeta = JSON.parse(readFileSync(refMetaFile, 'utf8'));
|
|
9962
|
+
return {
|
|
9963
|
+
path: rp,
|
|
9964
|
+
status: 'resolved',
|
|
9965
|
+
hasContent: Boolean(refMeta._content),
|
|
9966
|
+
};
|
|
9967
|
+
}
|
|
9968
|
+
catch {
|
|
9969
|
+
return { path: rp, status: 'missing' };
|
|
9970
|
+
}
|
|
9971
|
+
});
|
|
9972
|
+
}
|
|
9916
9973
|
// Archive
|
|
9917
9974
|
if (query.includeArchive) {
|
|
9918
9975
|
const archiveFiles = listArchiveFiles(targetNode.metaPath);
|
|
@@ -10030,6 +10087,7 @@ function registerPreviewRoute(app, deps) {
|
|
|
10030
10087
|
*/
|
|
10031
10088
|
const seedBodySchema = z.object({
|
|
10032
10089
|
path: z.string().min(1),
|
|
10090
|
+
crossRefs: z.array(z.string()).optional(),
|
|
10033
10091
|
});
|
|
10034
10092
|
function registerSeedRoute(app, deps) {
|
|
10035
10093
|
app.post('/seed', (request, reply) => {
|
|
@@ -10044,6 +10102,8 @@ function registerSeedRoute(app, deps) {
|
|
|
10044
10102
|
deps.logger.info({ metaDir }, 'creating .meta directory');
|
|
10045
10103
|
mkdirSync(metaDir, { recursive: true });
|
|
10046
10104
|
const metaJson = { _id: randomUUID() };
|
|
10105
|
+
if (body.crossRefs !== undefined)
|
|
10106
|
+
metaJson._crossRefs = body.crossRefs;
|
|
10047
10107
|
const metaJsonPath = join(metaDir, 'meta.json');
|
|
10048
10108
|
deps.logger.info({ metaJsonPath }, 'writing meta.json');
|
|
10049
10109
|
writeFileSync(metaJsonPath, JSON.stringify(metaJson, null, 2) + '\n');
|