@karmaniverous/jeeves-meta 0.5.0 → 0.6.1

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.
@@ -1060,10 +1060,13 @@ class GatewayExecutor {
1060
1060
  outputPath +
1061
1061
  '\n\n' +
1062
1062
  'Reply with ONLY the file path you wrote to. No other text.';
1063
- // Step 1: Spawn the sub-agent session
1063
+ // Step 1: Spawn the sub-agent session (unique label per cycle to avoid
1064
+ // "label already in use" errors — gateway labels persist after session completion)
1065
+ const labelBase = options?.label ?? 'jeeves-meta-synthesis';
1066
+ const label = labelBase + '-' + outputId.slice(0, 8);
1064
1067
  const spawnResult = await this.invoke('sessions_spawn', {
1065
1068
  task: taskWithOutput,
1066
- label: options?.label ?? 'jeeves-meta-synthesis',
1069
+ label,
1067
1070
  runTimeoutSeconds: timeoutSeconds,
1068
1071
  ...(options?.thinking ? { thinking: options.thinking } : {}),
1069
1072
  ...(options?.model ? { model: options.model } : {}),
@@ -2943,15 +2946,39 @@ async function checkDependency(url, path) {
2943
2946
  return { url, status: 'unreachable', checkedAt };
2944
2947
  }
2945
2948
  }
2949
+ /** Check watcher, surfacing initialScan.active as indexing state. */
2950
+ async function checkWatcher(url) {
2951
+ const checkedAt = new Date().toISOString();
2952
+ try {
2953
+ const res = await fetch(new URL('/status', url), {
2954
+ signal: AbortSignal.timeout(3000),
2955
+ });
2956
+ if (!res.ok)
2957
+ return { url, status: 'error', checkedAt };
2958
+ const data = (await res.json());
2959
+ const indexing = data.initialScan?.active === true;
2960
+ return {
2961
+ url,
2962
+ status: indexing ? 'indexing' : 'ok',
2963
+ checkedAt,
2964
+ indexing,
2965
+ };
2966
+ }
2967
+ catch {
2968
+ return { url, status: 'unreachable', checkedAt };
2969
+ }
2970
+ }
2946
2971
  function registerStatusRoute(app, deps) {
2947
2972
  app.get('/status', async () => {
2948
2973
  const { config, queue, scheduler, stats } = deps;
2949
2974
  // On-demand dependency checks
2950
2975
  const [watcherHealth, gatewayHealth] = await Promise.all([
2951
- checkDependency(config.watcherUrl, '/status'),
2952
- checkDependency(config.gatewayUrl, '/api/status'),
2976
+ checkWatcher(config.watcherUrl),
2977
+ checkDependency(config.gatewayUrl, '/status'),
2953
2978
  ]);
2954
- const degraded = watcherHealth.status !== 'ok' || gatewayHealth.status !== 'ok';
2979
+ const degraded = (watcherHealth.status !== 'ok' && watcherHealth.status !== 'indexing') ||
2980
+ gatewayHealth.status !== 'ok';
2981
+ const indexing = watcherHealth.status === 'indexing';
2955
2982
  // Determine status
2956
2983
  let status;
2957
2984
  if (deps.shuttingDown) {
@@ -2963,6 +2990,9 @@ function registerStatusRoute(app, deps) {
2963
2990
  else if (degraded) {
2964
2991
  status = 'degraded';
2965
2992
  }
2993
+ else if (indexing) {
2994
+ status = 'waiting';
2995
+ }
2966
2996
  else {
2967
2997
  status = 'idle';
2968
2998
  }
@@ -3357,14 +3387,7 @@ function createServer(options) {
3357
3387
  const app = Fastify({
3358
3388
  loggerInstance: options.logger,
3359
3389
  });
3360
- registerRoutes(app, {
3361
- config: options.config,
3362
- logger: options.logger,
3363
- queue: options.queue,
3364
- watcher: options.watcher,
3365
- scheduler: options.scheduler,
3366
- stats: options.stats,
3367
- });
3390
+ registerRoutes(app, options.deps);
3368
3391
  return app;
3369
3392
  }
3370
3393
 
@@ -3553,6 +3576,12 @@ class WatcherHealthCheck {
3553
3576
  return;
3554
3577
  }
3555
3578
  const data = (await res.json());
3579
+ // If rules were never successfully registered (startup failure),
3580
+ // attempt registration now that the watcher is reachable.
3581
+ if (!this.registrar.isRegistered) {
3582
+ this.logger.info('Rules not registered — attempting registration');
3583
+ await this.registrar.register();
3584
+ }
3556
3585
  await this.registrar.checkAndReregister(data.uptime);
3557
3586
  }
3558
3587
  catch (err) {
@@ -3607,11 +3636,7 @@ async function startService(config, configPath) {
3607
3636
  };
3608
3637
  const server = createServer({
3609
3638
  logger,
3610
- config,
3611
- queue,
3612
- watcher,
3613
- scheduler,
3614
- stats,
3639
+ deps: routeDeps,
3615
3640
  });
3616
3641
  // Start HTTP server
3617
3642
  try {
package/dist/index.d.ts CHANGED
@@ -1310,16 +1310,8 @@ declare function sleep(ms: number): Promise<void>;
1310
1310
  interface ServerOptions {
1311
1311
  /** Pino logger instance. */
1312
1312
  logger: Logger;
1313
- /** Synthesis queue instance. */
1314
- queue: SynthesisQueue;
1315
- /** Validated service configuration. */
1316
- config: ServiceConfig;
1317
- /** Watcher client for data queries. */
1318
- watcher: HttpWatcherClient;
1319
- /** Scheduler instance (null during tests). */
1320
- scheduler: Scheduler | null;
1321
- /** Mutable runtime stats. */
1322
- stats: ServiceStats;
1313
+ /** Shared route dependencies (mutable — late-bound properties like registrar are set after creation). */
1314
+ deps: RouteDeps;
1323
1315
  }
1324
1316
  /**
1325
1317
  * Create and configure the Fastify server.
package/dist/index.js CHANGED
@@ -1052,10 +1052,13 @@ class GatewayExecutor {
1052
1052
  outputPath +
1053
1053
  '\n\n' +
1054
1054
  'Reply with ONLY the file path you wrote to. No other text.';
1055
- // Step 1: Spawn the sub-agent session
1055
+ // Step 1: Spawn the sub-agent session (unique label per cycle to avoid
1056
+ // "label already in use" errors — gateway labels persist after session completion)
1057
+ const labelBase = options?.label ?? 'jeeves-meta-synthesis';
1058
+ const label = labelBase + '-' + outputId.slice(0, 8);
1056
1059
  const spawnResult = await this.invoke('sessions_spawn', {
1057
1060
  task: taskWithOutput,
1058
- label: options?.label ?? 'jeeves-meta-synthesis',
1061
+ label,
1059
1062
  runTimeoutSeconds: timeoutSeconds,
1060
1063
  ...(options?.thinking ? { thinking: options.thinking } : {}),
1061
1064
  ...(options?.model ? { model: options.model } : {}),
@@ -2939,15 +2942,39 @@ async function checkDependency(url, path) {
2939
2942
  return { url, status: 'unreachable', checkedAt };
2940
2943
  }
2941
2944
  }
2945
+ /** Check watcher, surfacing initialScan.active as indexing state. */
2946
+ async function checkWatcher(url) {
2947
+ const checkedAt = new Date().toISOString();
2948
+ try {
2949
+ const res = await fetch(new URL('/status', url), {
2950
+ signal: AbortSignal.timeout(3000),
2951
+ });
2952
+ if (!res.ok)
2953
+ return { url, status: 'error', checkedAt };
2954
+ const data = (await res.json());
2955
+ const indexing = data.initialScan?.active === true;
2956
+ return {
2957
+ url,
2958
+ status: indexing ? 'indexing' : 'ok',
2959
+ checkedAt,
2960
+ indexing,
2961
+ };
2962
+ }
2963
+ catch {
2964
+ return { url, status: 'unreachable', checkedAt };
2965
+ }
2966
+ }
2942
2967
  function registerStatusRoute(app, deps) {
2943
2968
  app.get('/status', async () => {
2944
2969
  const { config, queue, scheduler, stats } = deps;
2945
2970
  // On-demand dependency checks
2946
2971
  const [watcherHealth, gatewayHealth] = await Promise.all([
2947
- checkDependency(config.watcherUrl, '/status'),
2948
- checkDependency(config.gatewayUrl, '/api/status'),
2972
+ checkWatcher(config.watcherUrl),
2973
+ checkDependency(config.gatewayUrl, '/status'),
2949
2974
  ]);
2950
- const degraded = watcherHealth.status !== 'ok' || gatewayHealth.status !== 'ok';
2975
+ const degraded = (watcherHealth.status !== 'ok' && watcherHealth.status !== 'indexing') ||
2976
+ gatewayHealth.status !== 'ok';
2977
+ const indexing = watcherHealth.status === 'indexing';
2951
2978
  // Determine status
2952
2979
  let status;
2953
2980
  if (deps.shuttingDown) {
@@ -2959,6 +2986,9 @@ function registerStatusRoute(app, deps) {
2959
2986
  else if (degraded) {
2960
2987
  status = 'degraded';
2961
2988
  }
2989
+ else if (indexing) {
2990
+ status = 'waiting';
2991
+ }
2962
2992
  else {
2963
2993
  status = 'idle';
2964
2994
  }
@@ -3353,14 +3383,7 @@ function createServer(options) {
3353
3383
  const app = Fastify({
3354
3384
  loggerInstance: options.logger,
3355
3385
  });
3356
- registerRoutes(app, {
3357
- config: options.config,
3358
- logger: options.logger,
3359
- queue: options.queue,
3360
- watcher: options.watcher,
3361
- scheduler: options.scheduler,
3362
- stats: options.stats,
3363
- });
3386
+ registerRoutes(app, options.deps);
3364
3387
  return app;
3365
3388
  }
3366
3389
 
@@ -3549,6 +3572,12 @@ class WatcherHealthCheck {
3549
3572
  return;
3550
3573
  }
3551
3574
  const data = (await res.json());
3575
+ // If rules were never successfully registered (startup failure),
3576
+ // attempt registration now that the watcher is reachable.
3577
+ if (!this.registrar.isRegistered) {
3578
+ this.logger.info('Rules not registered — attempting registration');
3579
+ await this.registrar.register();
3580
+ }
3552
3581
  await this.registrar.checkAndReregister(data.uptime);
3553
3582
  }
3554
3583
  catch (err) {
@@ -3603,11 +3632,7 @@ async function startService(config, configPath) {
3603
3632
  };
3604
3633
  const server = createServer({
3605
3634
  logger,
3606
- config,
3607
- queue,
3608
- watcher,
3609
- scheduler,
3610
- stats,
3635
+ deps: routeDeps,
3611
3636
  });
3612
3637
  // Start HTTP server
3613
3638
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karmaniverous/jeeves-meta",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "author": "Jason Williscroft",
5
5
  "description": "Fastify HTTP service for the Jeeves Meta synthesis engine",
6
6
  "license": "BSD-3-Clause",
@@ -43,24 +43,27 @@
43
43
  "dependencies": {
44
44
  "commander": "^14",
45
45
  "croner": "^10",
46
- "fastify": "^5.7",
46
+ "fastify": "^5.8",
47
47
  "package-directory": "^8.2.0",
48
48
  "pino": "^10",
49
49
  "zod": "^4.3"
50
50
  },
51
51
  "devDependencies": {
52
+ "@dotenvx/dotenvx": "^1.55.1",
52
53
  "@rollup/plugin-commonjs": "^29.0.2",
53
54
  "@rollup/plugin-json": "^6.1.0",
54
55
  "@rollup/plugin-node-resolve": "^16.0.3",
55
56
  "@rollup/plugin-typescript": "^12.3.0",
56
- "@types/node": "^25.3.5",
57
- "@vitest/coverage-v8": "^4.0.18",
57
+ "@types/node": "^25.5.0",
58
+ "@vitest/coverage-v8": "^4.1.0",
59
+ "auto-changelog": "^2.5.0",
58
60
  "cross-env": "^10.1.0",
59
- "knip": "^5.85.0",
61
+ "knip": "^5.87.0",
62
+ "release-it": "^19.2.4",
60
63
  "rollup": "^4.59.0",
61
- "rollup-plugin-dts": "^6.3.0",
64
+ "rollup-plugin-dts": "^6.4.0",
62
65
  "tslib": "^2.8.1",
63
- "vitest": "^4.0.18"
66
+ "vitest": "^4.1.0"
64
67
  },
65
68
  "scripts": {
66
69
  "build": "rimraf dist && cross-env NO_COLOR=1 rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
@@ -106,5 +109,12 @@
106
109
  "npm": {
107
110
  "publish": true
108
111
  }
112
+ },
113
+ "auto-changelog": {
114
+ "output": "CHANGELOG.md",
115
+ "tagPrefix": "service/",
116
+ "unreleased": true,
117
+ "commitLimit": false,
118
+ "hideCredit": true
109
119
  }
110
120
  }