@karmaniverous/jeeves-meta 0.13.11 → 0.14.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 CHANGED
@@ -65,6 +65,7 @@ jeeves-meta service install --config /path/to/jeeves-meta/config.json
65
65
  | POST | `/config/apply` | Apply a config patch (merge or replace) |
66
66
  | GET | `/queue` | Current queue state (current, pending, stats) |
67
67
  | POST | `/queue/clear` | Remove all pending queue items |
68
+ | PATCH | `/metas/:path` | Update user-settable reserved properties (`_steer`, `_emphasis`, `_depth`, `_crossRefs`, `_disabled`) |
68
69
 
69
70
  ## Configuration
70
71
 
@@ -8554,6 +8554,7 @@ function computeSummary(entries, depthWeight) {
8554
8554
  let staleCount = 0;
8555
8555
  let errorCount = 0;
8556
8556
  let lockedCount = 0;
8557
+ let disabledCount = 0;
8557
8558
  let neverSynthesizedCount = 0;
8558
8559
  let totalArchitectTokens = 0;
8559
8560
  let totalBuilderTokens = 0;
@@ -8569,6 +8570,8 @@ function computeSummary(entries, depthWeight) {
8569
8570
  errorCount++;
8570
8571
  if (e.locked)
8571
8572
  lockedCount++;
8573
+ if (e.disabled)
8574
+ disabledCount++;
8572
8575
  if (e.lastSynthesized === null)
8573
8576
  neverSynthesizedCount++;
8574
8577
  totalArchitectTokens += e.architectTokens ?? 0;
@@ -8593,6 +8596,7 @@ function computeSummary(entries, depthWeight) {
8593
8596
  stale: staleCount,
8594
8597
  errors: errorCount,
8595
8598
  locked: lockedCount,
8599
+ disabled: disabledCount,
8596
8600
  neverSynthesized: neverSynthesizedCount,
8597
8601
  tokens: {
8598
8602
  architect: totalArchitectTokens,
@@ -9089,6 +9093,7 @@ async function listMetas(config, watcher) {
9089
9093
  const emphasis = meta._emphasis ?? 1;
9090
9094
  const hasError = Boolean(meta._error);
9091
9095
  const locked = isLocked(normalizePath(node.metaPath));
9096
+ const disabled = meta._disabled === true;
9092
9097
  const neverSynth = !meta._generatedAt;
9093
9098
  // Compute staleness
9094
9099
  let stalenessSeconds;
@@ -9111,6 +9116,7 @@ async function listMetas(config, watcher) {
9111
9116
  lastSynthesized: meta._generatedAt ?? null,
9112
9117
  hasError,
9113
9118
  locked,
9119
+ disabled,
9114
9120
  architectTokens: archTokens > 0 ? archTokens : null,
9115
9121
  builderTokens: buildTokens > 0 ? buildTokens : null,
9116
9122
  criticTokens: critTokens > 0 ? critTokens : null,
@@ -10056,6 +10062,8 @@ const metaJsonSchema = z
10056
10062
  * Cleared on successful cycle.
10057
10063
  */
10058
10064
  _error: metaErrorSchema.optional(),
10065
+ /** When true, this meta is skipped during staleness scheduling. Manual trigger still works. */
10066
+ _disabled: z.boolean().optional(),
10059
10067
  })
10060
10068
  .loose();
10061
10069
 
@@ -11762,7 +11770,7 @@ class Scheduler {
11762
11770
  try {
11763
11771
  const result = await listMetas(this.config, this.watcher);
11764
11772
  const stale = result.entries
11765
- .filter((e) => e.stalenessSeconds > 0)
11773
+ .filter((e) => e.stalenessSeconds > 0 && !e.disabled)
11766
11774
  .map((e) => ({
11767
11775
  node: e.node,
11768
11776
  meta: e.meta,
@@ -11921,6 +11929,10 @@ const metasQuerySchema = z.object({
11921
11929
  .enum(['true', 'false'])
11922
11930
  .transform((v) => v === 'true')
11923
11931
  .optional(),
11932
+ disabled: z
11933
+ .enum(['true', 'false'])
11934
+ .transform((v) => v === 'true')
11935
+ .optional(),
11924
11936
  fields: z.string().optional(),
11925
11937
  });
11926
11938
  const metaDetailQuerySchema = z.object({
@@ -11951,6 +11963,9 @@ function registerMetasRoutes(app, deps) {
11951
11963
  if (query.locked !== undefined) {
11952
11964
  entries = entries.filter((e) => e.locked === query.locked);
11953
11965
  }
11966
+ if (query.disabled !== undefined) {
11967
+ entries = entries.filter((e) => e.disabled === query.disabled);
11968
+ }
11954
11969
  if (typeof query.staleHours === 'number') {
11955
11970
  entries = entries.filter((e) => e.stalenessSeconds >= query.staleHours * 3600);
11956
11971
  }
@@ -11966,6 +11981,7 @@ function registerMetasRoutes(app, deps) {
11966
11981
  'lastSynthesized',
11967
11982
  'hasError',
11968
11983
  'locked',
11984
+ 'disabled',
11969
11985
  'architectTokens',
11970
11986
  'builderTokens',
11971
11987
  'criticTokens',
@@ -11982,6 +11998,7 @@ function registerMetasRoutes(app, deps) {
11982
11998
  lastSynthesized: e.lastSynthesized,
11983
11999
  hasError: e.hasError,
11984
12000
  locked: e.locked,
12001
+ disabled: e.disabled,
11985
12002
  architectTokens: e.architectTokens,
11986
12003
  builderTokens: e.builderTokens,
11987
12004
  criticTokens: e.criticTokens,
@@ -12087,6 +12104,91 @@ function registerMetasRoutes(app, deps) {
12087
12104
  });
12088
12105
  }
12089
12106
 
12107
+ /**
12108
+ * PATCH /metas/:path — update user-settable reserved properties on a meta.
12109
+ *
12110
+ * Supported fields: _steer, _emphasis, _depth, _crossRefs, _disabled.
12111
+ * Set a field to null to remove it. Unknown keys are rejected.
12112
+ *
12113
+ * @module routes/metasUpdate
12114
+ */
12115
+ const updateBodySchema = z
12116
+ .object({
12117
+ _steer: z.union([z.string(), z.null()]).optional(),
12118
+ _emphasis: z.union([z.number().min(0), z.null()]).optional(),
12119
+ _depth: z.union([z.number(), z.null()]).optional(),
12120
+ _crossRefs: z.union([z.array(z.string()), z.null()]).optional(),
12121
+ _disabled: z.union([z.boolean(), z.null()]).optional(),
12122
+ })
12123
+ .strict();
12124
+ function registerMetasUpdateRoute(app, deps) {
12125
+ app.patch('/metas/:path', async (request, reply) => {
12126
+ const parseResult = updateBodySchema.safeParse(request.body);
12127
+ if (!parseResult.success) {
12128
+ return reply.status(400).send({
12129
+ error: 'BAD_REQUEST',
12130
+ message: parseResult.error.message,
12131
+ });
12132
+ }
12133
+ const updates = parseResult.data;
12134
+ const targetPath = normalizePath(decodeURIComponent(request.params.path));
12135
+ const metaDir = resolveMetaDir(targetPath);
12136
+ let meta;
12137
+ try {
12138
+ meta = (await readMetaJson(metaDir));
12139
+ }
12140
+ catch {
12141
+ return reply.status(404).send({
12142
+ error: 'NOT_FOUND',
12143
+ message: 'Meta path not found: ' + targetPath,
12144
+ });
12145
+ }
12146
+ const metaJsonPath = join(metaDir, 'meta.json');
12147
+ const KEYS = [
12148
+ '_steer',
12149
+ '_emphasis',
12150
+ '_depth',
12151
+ '_crossRefs',
12152
+ '_disabled',
12153
+ ];
12154
+ const toDelete = new Set();
12155
+ const toSet = {};
12156
+ for (const key of KEYS) {
12157
+ const value = updates[key];
12158
+ if (value === null) {
12159
+ toDelete.add(key);
12160
+ }
12161
+ else if (value !== undefined) {
12162
+ toSet[key] = value;
12163
+ }
12164
+ }
12165
+ const updated = {};
12166
+ for (const [k, v] of Object.entries(meta)) {
12167
+ if (!toDelete.has(k))
12168
+ updated[k] = v;
12169
+ }
12170
+ Object.assign(updated, toSet);
12171
+ await writeFile(metaJsonPath, JSON.stringify(updated, null, 2) + '\n');
12172
+ // Project the response — exclude the same large fields as the detail route.
12173
+ const defaultExclude = new Set([
12174
+ '_architect',
12175
+ '_builder',
12176
+ '_critic',
12177
+ '_content',
12178
+ '_feedback',
12179
+ ]);
12180
+ const projected = {};
12181
+ for (const [k, v] of Object.entries(updated)) {
12182
+ if (!defaultExclude.has(k))
12183
+ projected[k] = v;
12184
+ }
12185
+ return reply.send({
12186
+ path: metaDir,
12187
+ meta: projected,
12188
+ });
12189
+ });
12190
+ }
12191
+
12090
12192
  /**
12091
12193
  * GET /preview — dry-run synthesis preview.
12092
12194
  *
@@ -12408,7 +12510,7 @@ function registerSynthesizeRoute(app, deps) {
12408
12510
  });
12409
12511
  }
12410
12512
  const stale = result.entries
12411
- .filter((e) => e.stalenessSeconds > 0)
12513
+ .filter((e) => e.stalenessSeconds > 0 && !e.disabled)
12412
12514
  .map((e) => ({
12413
12515
  node: e.node,
12414
12516
  meta: e.meta,
@@ -12487,6 +12589,7 @@ function registerRoutes(app, deps) {
12487
12589
  });
12488
12590
  registerStatusRoute(app, deps);
12489
12591
  registerMetasRoutes(app, deps);
12592
+ registerMetasUpdateRoute(app);
12490
12593
  registerSynthesizeRoute(app, deps);
12491
12594
  registerPreviewRoute(app, deps);
12492
12595
  registerSeedRoute(app, deps);
package/dist/index.d.ts CHANGED
@@ -316,6 +316,7 @@ declare const metaJsonSchema: z.ZodObject<{
316
316
  code: z.ZodString;
317
317
  message: z.ZodString;
318
318
  }, z.core.$strip>>;
319
+ _disabled: z.ZodOptional<z.ZodBoolean>;
319
320
  }, z.core.$loose>;
320
321
  /** Inferred type for meta.json. */
321
322
  type MetaJson = z.infer<typeof metaJsonSchema>;
@@ -743,6 +744,8 @@ interface MetaEntry {
743
744
  hasError: boolean;
744
745
  /** Whether this meta is currently locked. */
745
746
  locked: boolean;
747
+ /** Whether this meta is disabled (skipped during staleness scheduling). */
748
+ disabled: boolean;
746
749
  /** Cumulative architect tokens, or null if never run. */
747
750
  architectTokens: number | null;
748
751
  /** Cumulative builder tokens, or null if never run. */
@@ -762,6 +765,7 @@ interface MetaListSummary {
762
765
  stale: number;
763
766
  errors: number;
764
767
  locked: number;
768
+ disabled: number;
765
769
  neverSynthesized: number;
766
770
  tokens: {
767
771
  architect: number;
package/dist/index.js CHANGED
@@ -8292,6 +8292,7 @@ function computeSummary(entries, depthWeight) {
8292
8292
  let staleCount = 0;
8293
8293
  let errorCount = 0;
8294
8294
  let lockedCount = 0;
8295
+ let disabledCount = 0;
8295
8296
  let neverSynthesizedCount = 0;
8296
8297
  let totalArchitectTokens = 0;
8297
8298
  let totalBuilderTokens = 0;
@@ -8307,6 +8308,8 @@ function computeSummary(entries, depthWeight) {
8307
8308
  errorCount++;
8308
8309
  if (e.locked)
8309
8310
  lockedCount++;
8311
+ if (e.disabled)
8312
+ disabledCount++;
8310
8313
  if (e.lastSynthesized === null)
8311
8314
  neverSynthesizedCount++;
8312
8315
  totalArchitectTokens += e.architectTokens ?? 0;
@@ -8331,6 +8334,7 @@ function computeSummary(entries, depthWeight) {
8331
8334
  stale: staleCount,
8332
8335
  errors: errorCount,
8333
8336
  locked: lockedCount,
8337
+ disabled: disabledCount,
8334
8338
  neverSynthesized: neverSynthesizedCount,
8335
8339
  tokens: {
8336
8340
  architect: totalArchitectTokens,
@@ -8808,6 +8812,7 @@ async function listMetas(config, watcher) {
8808
8812
  const emphasis = meta._emphasis ?? 1;
8809
8813
  const hasError = Boolean(meta._error);
8810
8814
  const locked = isLocked(normalizePath(node.metaPath));
8815
+ const disabled = meta._disabled === true;
8811
8816
  const neverSynth = !meta._generatedAt;
8812
8817
  // Compute staleness
8813
8818
  let stalenessSeconds;
@@ -8830,6 +8835,7 @@ async function listMetas(config, watcher) {
8830
8835
  lastSynthesized: meta._generatedAt ?? null,
8831
8836
  hasError,
8832
8837
  locked,
8838
+ disabled,
8833
8839
  architectTokens: archTokens > 0 ? archTokens : null,
8834
8840
  builderTokens: buildTokens > 0 ? buildTokens : null,
8835
8841
  criticTokens: critTokens > 0 ? critTokens : null,
@@ -9587,6 +9593,8 @@ const metaJsonSchema = z
9587
9593
  * Cleared on successful cycle.
9588
9594
  */
9589
9595
  _error: metaErrorSchema.optional(),
9596
+ /** When true, this meta is skipped during staleness scheduling. Manual trigger still works. */
9597
+ _disabled: z.boolean().optional(),
9590
9598
  })
9591
9599
  .loose();
9592
9600
 
@@ -11293,7 +11301,7 @@ class Scheduler {
11293
11301
  try {
11294
11302
  const result = await listMetas(this.config, this.watcher);
11295
11303
  const stale = result.entries
11296
- .filter((e) => e.stalenessSeconds > 0)
11304
+ .filter((e) => e.stalenessSeconds > 0 && !e.disabled)
11297
11305
  .map((e) => ({
11298
11306
  node: e.node,
11299
11307
  meta: e.meta,
@@ -11452,6 +11460,10 @@ const metasQuerySchema = z.object({
11452
11460
  .enum(['true', 'false'])
11453
11461
  .transform((v) => v === 'true')
11454
11462
  .optional(),
11463
+ disabled: z
11464
+ .enum(['true', 'false'])
11465
+ .transform((v) => v === 'true')
11466
+ .optional(),
11455
11467
  fields: z.string().optional(),
11456
11468
  });
11457
11469
  const metaDetailQuerySchema = z.object({
@@ -11482,6 +11494,9 @@ function registerMetasRoutes(app, deps) {
11482
11494
  if (query.locked !== undefined) {
11483
11495
  entries = entries.filter((e) => e.locked === query.locked);
11484
11496
  }
11497
+ if (query.disabled !== undefined) {
11498
+ entries = entries.filter((e) => e.disabled === query.disabled);
11499
+ }
11485
11500
  if (typeof query.staleHours === 'number') {
11486
11501
  entries = entries.filter((e) => e.stalenessSeconds >= query.staleHours * 3600);
11487
11502
  }
@@ -11497,6 +11512,7 @@ function registerMetasRoutes(app, deps) {
11497
11512
  'lastSynthesized',
11498
11513
  'hasError',
11499
11514
  'locked',
11515
+ 'disabled',
11500
11516
  'architectTokens',
11501
11517
  'builderTokens',
11502
11518
  'criticTokens',
@@ -11513,6 +11529,7 @@ function registerMetasRoutes(app, deps) {
11513
11529
  lastSynthesized: e.lastSynthesized,
11514
11530
  hasError: e.hasError,
11515
11531
  locked: e.locked,
11532
+ disabled: e.disabled,
11516
11533
  architectTokens: e.architectTokens,
11517
11534
  builderTokens: e.builderTokens,
11518
11535
  criticTokens: e.criticTokens,
@@ -11618,6 +11635,91 @@ function registerMetasRoutes(app, deps) {
11618
11635
  });
11619
11636
  }
11620
11637
 
11638
+ /**
11639
+ * PATCH /metas/:path — update user-settable reserved properties on a meta.
11640
+ *
11641
+ * Supported fields: _steer, _emphasis, _depth, _crossRefs, _disabled.
11642
+ * Set a field to null to remove it. Unknown keys are rejected.
11643
+ *
11644
+ * @module routes/metasUpdate
11645
+ */
11646
+ const updateBodySchema = z
11647
+ .object({
11648
+ _steer: z.union([z.string(), z.null()]).optional(),
11649
+ _emphasis: z.union([z.number().min(0), z.null()]).optional(),
11650
+ _depth: z.union([z.number(), z.null()]).optional(),
11651
+ _crossRefs: z.union([z.array(z.string()), z.null()]).optional(),
11652
+ _disabled: z.union([z.boolean(), z.null()]).optional(),
11653
+ })
11654
+ .strict();
11655
+ function registerMetasUpdateRoute(app, deps) {
11656
+ app.patch('/metas/:path', async (request, reply) => {
11657
+ const parseResult = updateBodySchema.safeParse(request.body);
11658
+ if (!parseResult.success) {
11659
+ return reply.status(400).send({
11660
+ error: 'BAD_REQUEST',
11661
+ message: parseResult.error.message,
11662
+ });
11663
+ }
11664
+ const updates = parseResult.data;
11665
+ const targetPath = normalizePath(decodeURIComponent(request.params.path));
11666
+ const metaDir = resolveMetaDir(targetPath);
11667
+ let meta;
11668
+ try {
11669
+ meta = (await readMetaJson(metaDir));
11670
+ }
11671
+ catch {
11672
+ return reply.status(404).send({
11673
+ error: 'NOT_FOUND',
11674
+ message: 'Meta path not found: ' + targetPath,
11675
+ });
11676
+ }
11677
+ const metaJsonPath = join(metaDir, 'meta.json');
11678
+ const KEYS = [
11679
+ '_steer',
11680
+ '_emphasis',
11681
+ '_depth',
11682
+ '_crossRefs',
11683
+ '_disabled',
11684
+ ];
11685
+ const toDelete = new Set();
11686
+ const toSet = {};
11687
+ for (const key of KEYS) {
11688
+ const value = updates[key];
11689
+ if (value === null) {
11690
+ toDelete.add(key);
11691
+ }
11692
+ else if (value !== undefined) {
11693
+ toSet[key] = value;
11694
+ }
11695
+ }
11696
+ const updated = {};
11697
+ for (const [k, v] of Object.entries(meta)) {
11698
+ if (!toDelete.has(k))
11699
+ updated[k] = v;
11700
+ }
11701
+ Object.assign(updated, toSet);
11702
+ await writeFile(metaJsonPath, JSON.stringify(updated, null, 2) + '\n');
11703
+ // Project the response — exclude the same large fields as the detail route.
11704
+ const defaultExclude = new Set([
11705
+ '_architect',
11706
+ '_builder',
11707
+ '_critic',
11708
+ '_content',
11709
+ '_feedback',
11710
+ ]);
11711
+ const projected = {};
11712
+ for (const [k, v] of Object.entries(updated)) {
11713
+ if (!defaultExclude.has(k))
11714
+ projected[k] = v;
11715
+ }
11716
+ return reply.send({
11717
+ path: metaDir,
11718
+ meta: projected,
11719
+ });
11720
+ });
11721
+ }
11722
+
11621
11723
  /**
11622
11724
  * GET /preview — dry-run synthesis preview.
11623
11725
  *
@@ -11911,7 +12013,7 @@ function registerSynthesizeRoute(app, deps) {
11911
12013
  });
11912
12014
  }
11913
12015
  const stale = result.entries
11914
- .filter((e) => e.stalenessSeconds > 0)
12016
+ .filter((e) => e.stalenessSeconds > 0 && !e.disabled)
11915
12017
  .map((e) => ({
11916
12018
  node: e.node,
11917
12019
  meta: e.meta,
@@ -11990,6 +12092,7 @@ function registerRoutes(app, deps) {
11990
12092
  });
11991
12093
  registerStatusRoute(app, deps);
11992
12094
  registerMetasRoutes(app, deps);
12095
+ registerMetasUpdateRoute(app);
11993
12096
  registerSynthesizeRoute(app, deps);
11994
12097
  registerPreviewRoute(app, deps);
11995
12098
  registerSeedRoute(app, deps);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karmaniverous/jeeves-meta",
3
- "version": "0.13.11",
3
+ "version": "0.14.0",
4
4
  "author": "Jason Williscroft",
5
5
  "description": "Fastify HTTP service for the Jeeves Meta synthesis engine",
6
6
  "license": "BSD-3-Clause",