@karmaniverous/jeeves-meta 0.16.1 → 0.16.2

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
@@ -14,6 +14,8 @@ HTTP service for the Jeeves knowledge synthesis engine. Provides a Fastify API,
14
14
  - **Cross-meta references** — `_crossRefs` declares relationships to other metas; referenced `_content` included as architect/builder context
15
15
  - **Archive management** — timestamped snapshots with configurable pruning
16
16
  - **Lock staging** — write to `.lock` → copy to `meta.json` → archive (crash-safe)
17
+ - **Staging file retry** — configurable retries (`stagingRetries`, `stagingRetryDelayMs`) when sub-agent output file is not yet visible after session completion (network/NFS latency)
18
+ - **Preview delta cap** — `/preview` response includes `deltaFilesTruncated` flag and `deltaCount` total when delta files exceed `previewDeltaFilesCap`
17
19
  - **Virtual rule registration** — registers 3 watcher inference rules at startup with retry
18
20
  - **Progress reporting** — real-time synthesis events via gateway channel messages
19
21
  - **Graceful shutdown** — stop scheduler, release locks, close server
@@ -806,6 +806,14 @@ const serviceConfigSchema = metaConfigSchema.extend({
806
806
  * Rules are evaluated in order; last match wins for steer/crossRefs.
807
807
  */
808
808
  autoSeed: z.array(autoSeedRuleSchema).optional().default([]),
809
+ /** Optional workspace directory for synthesis staging files. Defaults to `os.tmpdir() + '/jeeves-meta'`. */
810
+ workspaceDir: z.string().optional(),
811
+ /** Max retries when staging file not yet visible after session completion. Default: 10. */
812
+ stagingRetries: z.number().int().min(0).default(10),
813
+ /** Delay between staging file retries in ms. Default: 250. */
814
+ stagingRetryDelayMs: z.number().int().min(0).default(250),
815
+ /** Maximum number of delta files returned in /preview response. Default: 50. */
816
+ previewDeltaFilesCap: z.number().int().min(1).default(50),
809
817
  });
810
818
 
811
819
  /**
@@ -933,12 +941,16 @@ class GatewayExecutor {
933
941
  apiKey;
934
942
  pollIntervalMs;
935
943
  workspaceDir;
944
+ stagingRetries;
945
+ stagingRetryDelayMs;
936
946
  controller = new AbortController();
937
947
  constructor(options = {}) {
938
948
  this.gatewayUrl = (options.gatewayUrl ?? 'http://127.0.0.1:18789').replace(/\/+$/, '');
939
949
  this.apiKey = options.apiKey;
940
950
  this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
941
951
  this.workspaceDir = options.workspaceDir ?? join(tmpdir(), 'jeeves-meta');
952
+ this.stagingRetries = options.stagingRetries ?? 10;
953
+ this.stagingRetryDelayMs = options.stagingRetryDelayMs ?? 250;
942
954
  }
943
955
  /** Remove a temp output file if it exists. */
944
956
  cleanupOutputFile(outputPath) {
@@ -1168,10 +1180,23 @@ class GatewayExecutor {
1168
1180
  // No output or partial output — throw for _state recovery (§3.16.6)
1169
1181
  throw new SpawnTimeoutError('Gateway-side timeout detected (session status: timeout)', outputPath);
1170
1182
  }
1171
- // Normal completion — read output from file
1183
+ // Normal completion — read staging file with retry for delayed visibility
1172
1184
  const output = this.readStagingFile(outputPath);
1173
1185
  if (output !== undefined)
1174
1186
  return { output, tokens };
1187
+ // Staging file not yet visible — retry with bounded grace window
1188
+ for (let i = 0; i < this.stagingRetries; i++) {
1189
+ await sleepAsync(this.stagingRetryDelayMs);
1190
+ // Check abort after yield point — signal may have been
1191
+ // set externally (e.g. operator abort) during the sleep.
1192
+ if (this.aborted) {
1193
+ this.cleanupOutputFile(outputPath);
1194
+ throw new SpawnAbortedError();
1195
+ }
1196
+ const retryOutput = this.readStagingFile(outputPath);
1197
+ if (retryOutput !== undefined)
1198
+ return { output: retryOutput, tokens };
1199
+ }
1175
1200
  // Fallback: extract from message content if file wasn't written.
1176
1201
  // Skip ANNOUNCE_SKIP sentinel messages — the real output is in
1177
1202
  // a preceding assistant message (the file write).
@@ -4452,9 +4477,10 @@ function registerPreviewRoute(app, deps) {
4452
4477
  ownedFiles: scopeFiles.length,
4453
4478
  childMetas: targetNode.children.length,
4454
4479
  deltaFiles: deltaFiles
4455
- .slice(0, 50)
4480
+ .slice(0, config.previewDeltaFilesCap)
4456
4481
  .map((f) => ({ path: f, action: 'modified' })),
4457
4482
  deltaCount: deltaFiles.length,
4483
+ deltaFilesTruncated: deltaFiles.length > config.previewDeltaFilesCap,
4458
4484
  },
4459
4485
  estimatedTokens,
4460
4486
  // New phase-state fields (additive)
@@ -5156,6 +5182,9 @@ async function startService(config, configPath) {
5156
5182
  const executor = new GatewayExecutor({
5157
5183
  gatewayUrl: config.gatewayUrl,
5158
5184
  apiKey: config.gatewayApiKey,
5185
+ workspaceDir: config.workspaceDir,
5186
+ stagingRetries: config.stagingRetries,
5187
+ stagingRetryDelayMs: config.stagingRetryDelayMs,
5159
5188
  });
5160
5189
  // Runtime stats (mutable, shared with routes)
5161
5190
  const stats = {
@@ -18,6 +18,10 @@ export interface GatewayExecutorOptions {
18
18
  pollIntervalMs?: number;
19
19
  /** Workspace directory for output staging. Default: OS temp dir + /jeeves-meta. */
20
20
  workspaceDir?: string;
21
+ /** Max retries when staging file not yet visible after session completion. Default: 10. */
22
+ stagingRetries?: number;
23
+ /** Delay between retries in ms. Default: 250. */
24
+ stagingRetryDelayMs?: number;
21
25
  }
22
26
  /**
23
27
  * MetaExecutor that spawns OpenClaw sessions via the gateway's
@@ -32,6 +36,8 @@ export declare class GatewayExecutor implements MetaExecutor {
32
36
  private readonly apiKey;
33
37
  private readonly pollIntervalMs;
34
38
  private readonly workspaceDir;
39
+ private readonly stagingRetries;
40
+ private readonly stagingRetryDelayMs;
35
41
  private controller;
36
42
  constructor(options?: GatewayExecutorOptions);
37
43
  /** Remove a temp output file if it exists. */
package/dist/index.js CHANGED
@@ -1295,6 +1295,14 @@ const serviceConfigSchema = metaConfigSchema.extend({
1295
1295
  * Rules are evaluated in order; last match wins for steer/crossRefs.
1296
1296
  */
1297
1297
  autoSeed: z.array(autoSeedRuleSchema).optional().default([]),
1298
+ /** Optional workspace directory for synthesis staging files. Defaults to `os.tmpdir() + '/jeeves-meta'`. */
1299
+ workspaceDir: z.string().optional(),
1300
+ /** Max retries when staging file not yet visible after session completion. Default: 10. */
1301
+ stagingRetries: z.number().int().min(0).default(10),
1302
+ /** Delay between staging file retries in ms. Default: 250. */
1303
+ stagingRetryDelayMs: z.number().int().min(0).default(250),
1304
+ /** Maximum number of delta files returned in /preview response. Default: 50. */
1305
+ previewDeltaFilesCap: z.number().int().min(1).default(50),
1298
1306
  });
1299
1307
 
1300
1308
  /**
@@ -1467,12 +1475,16 @@ class GatewayExecutor {
1467
1475
  apiKey;
1468
1476
  pollIntervalMs;
1469
1477
  workspaceDir;
1478
+ stagingRetries;
1479
+ stagingRetryDelayMs;
1470
1480
  controller = new AbortController();
1471
1481
  constructor(options = {}) {
1472
1482
  this.gatewayUrl = (options.gatewayUrl ?? 'http://127.0.0.1:18789').replace(/\/+$/, '');
1473
1483
  this.apiKey = options.apiKey;
1474
1484
  this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
1475
1485
  this.workspaceDir = options.workspaceDir ?? join(tmpdir(), 'jeeves-meta');
1486
+ this.stagingRetries = options.stagingRetries ?? 10;
1487
+ this.stagingRetryDelayMs = options.stagingRetryDelayMs ?? 250;
1476
1488
  }
1477
1489
  /** Remove a temp output file if it exists. */
1478
1490
  cleanupOutputFile(outputPath) {
@@ -1702,10 +1714,23 @@ class GatewayExecutor {
1702
1714
  // No output or partial output — throw for _state recovery (§3.16.6)
1703
1715
  throw new SpawnTimeoutError('Gateway-side timeout detected (session status: timeout)', outputPath);
1704
1716
  }
1705
- // Normal completion — read output from file
1717
+ // Normal completion — read staging file with retry for delayed visibility
1706
1718
  const output = this.readStagingFile(outputPath);
1707
1719
  if (output !== undefined)
1708
1720
  return { output, tokens };
1721
+ // Staging file not yet visible — retry with bounded grace window
1722
+ for (let i = 0; i < this.stagingRetries; i++) {
1723
+ await sleepAsync(this.stagingRetryDelayMs);
1724
+ // Check abort after yield point — signal may have been
1725
+ // set externally (e.g. operator abort) during the sleep.
1726
+ if (this.aborted) {
1727
+ this.cleanupOutputFile(outputPath);
1728
+ throw new SpawnAbortedError();
1729
+ }
1730
+ const retryOutput = this.readStagingFile(outputPath);
1731
+ if (retryOutput !== undefined)
1732
+ return { output: retryOutput, tokens };
1733
+ }
1709
1734
  // Fallback: extract from message content if file wasn't written.
1710
1735
  // Skip ANNOUNCE_SKIP sentinel messages — the real output is in
1711
1736
  // a preceding assistant message (the file write).
@@ -4710,9 +4735,10 @@ function registerPreviewRoute(app, deps) {
4710
4735
  ownedFiles: scopeFiles.length,
4711
4736
  childMetas: targetNode.children.length,
4712
4737
  deltaFiles: deltaFiles
4713
- .slice(0, 50)
4738
+ .slice(0, config.previewDeltaFilesCap)
4714
4739
  .map((f) => ({ path: f, action: 'modified' })),
4715
4740
  deltaCount: deltaFiles.length,
4741
+ deltaFilesTruncated: deltaFiles.length > config.previewDeltaFilesCap,
4716
4742
  },
4717
4743
  estimatedTokens,
4718
4744
  // New phase-state fields (additive)
@@ -5386,6 +5412,9 @@ async function startService(config, configPath) {
5386
5412
  const executor = new GatewayExecutor({
5387
5413
  gatewayUrl: config.gatewayUrl,
5388
5414
  apiKey: config.gatewayApiKey,
5415
+ workspaceDir: config.workspaceDir,
5416
+ stagingRetries: config.stagingRetries,
5417
+ stagingRetryDelayMs: config.stagingRetryDelayMs,
5389
5418
  });
5390
5419
  // Runtime stats (mutable, shared with routes)
5391
5420
  const stats = {
@@ -49,6 +49,10 @@ export declare const serviceConfigSchema: z.ZodObject<{
49
49
  crossRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
50
50
  parentDepth: z.ZodOptional<z.ZodNumber>;
51
51
  }, z.core.$strip>>>>;
52
+ workspaceDir: z.ZodOptional<z.ZodString>;
53
+ stagingRetries: z.ZodDefault<z.ZodNumber>;
54
+ stagingRetryDelayMs: z.ZodDefault<z.ZodNumber>;
55
+ previewDeltaFilesCap: z.ZodDefault<z.ZodNumber>;
52
56
  }, z.core.$strip>;
53
57
  /** Inferred type for service configuration. */
54
58
  export type ServiceConfig = z.infer<typeof serviceConfigSchema>;
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@karmaniverous/jeeves": "^0.5.12",
11
- "@karmaniverous/jeeves-meta-core": "^0.2.0",
11
+ "@karmaniverous/jeeves-meta-core": "^0.2.1",
12
12
  "commander": "^14",
13
13
  "croner": "^10",
14
14
  "fastify": "^5.8",
@@ -110,5 +110,5 @@
110
110
  },
111
111
  "type": "module",
112
112
  "types": "dist/index.d.ts",
113
- "version": "0.16.1"
113
+ "version": "0.16.2"
114
114
  }