@karmaniverous/jeeves-meta-openclaw 0.15.0 → 0.17.0-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
@@ -5,8 +5,8 @@ OpenClaw plugin for [jeeves-meta](https://github.com/karmaniverous/jeeves-meta/t
5
5
  ## Features
6
6
 
7
7
  - **Twelve tools** — standard: `meta_status`, `meta_config`, `meta_config_apply`, `meta_service`; custom: `meta_list`, `meta_detail`, `meta_trigger`, `meta_preview`, `meta_seed`, `meta_unlock`, `meta_queue` (list/clear/abort), `meta_update`
8
- - **MetaServiceClient** — typed HTTP client delegating all operations to the running service
9
- - **Phase-state awareness** — tools expose per-meta `_phaseState`, `owedPhase`, and phase-state summary from the service's phase-state machine
8
+ - **SDK client** — every tool calls the running service through `@karmaniverous/jeeves-meta-sdk`
9
+ - **Phase-state awareness** — tools expose per-meta `phaseState`, `owedPhase`, and phase-state summary from the service's phase-state machine
10
10
  - **Lazy config** — registers with no config; `configRoot` is resolved when a tool needs it
11
11
  - **Consumer skill** — `jeeves-meta` skill shipped through the plugin manifest
12
12
 
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
- import { resolvePluginSetting, resolveOptionalPluginSetting, fail, init, resolveWorkspacePath, getErrorMessage, fetchJson, postJson, ok, connectionFail, createPluginToolset, jeevesComponentDescriptorSchema, getPackageVersion } from '@karmaniverous/jeeves';
2
- import { getEndpoint, META_COMPONENT } from '@karmaniverous/jeeves-meta-core';
1
+ import { resolvePluginSetting, resolveOptionalPluginSetting, fail, init, resolveWorkspacePath, getErrorMessage, connectionFail, ok, createPluginToolset, jeevesComponentDescriptorSchema, getPackageVersion } from '@karmaniverous/jeeves';
2
+ import { META_CATALOG, META_COMPONENT } from '@karmaniverous/jeeves-meta-core';
3
3
  import { z } from 'zod';
4
+ import { isMetaError, errorBodyText, createMetaClient } from '@karmaniverous/jeeves-meta-sdk';
5
+ import { CatalogClientError } from '@karmaniverous/jeeves/catalog';
4
6
 
5
7
  /**
6
8
  * Shared constants for the jeeves-meta OpenClaw plugin.
@@ -109,137 +111,203 @@ function requireConfigRoot(tool, gate, needsConfigRoot = () => true) {
109
111
  }
110
112
 
111
113
  /**
112
- * Thin HTTP client for the jeeves-meta service.
114
+ * Tool result for a failed SDK call, worded as the 0.16.0 tools worded it
115
+ * (spec 4.4.7). A non-2xx response reads as `Error: HTTP {status}: {body}`,
116
+ * with the body exactly as received (now the standard error body, so the
117
+ * text carries the code and message); an unreachable service gets the
118
+ * connection guidance; anything else reads as `Error: {message}`. Pure.
113
119
  *
114
- * Plugin delegates all operations to the running service via HTTP.
115
- * Shared response types are imported from `@karmaniverous/jeeves-meta-core`.
120
+ * @module toolError
121
+ */
122
+ /**
123
+ * The error a status-0 `MetaError` wraps (the network error), so
124
+ * connection guidance sees the same cause it saw in 0.16.0.
116
125
  *
117
- * @module serviceClient
126
+ * @param error - A status-0 MetaError.
127
+ * @returns The underlying error, or the MetaError itself.
118
128
  */
119
- class MetaServiceClient {
120
- baseUrl;
121
- constructor(config) {
122
- this.baseUrl = config.apiUrl.replace(/\/$/, '');
123
- }
124
- /** Return the base URL (for error reporting). */
125
- getBaseUrl() {
126
- return this.baseUrl;
127
- }
128
- /** GET helper — returns parsed JSON. */
129
- async get(path) {
130
- return fetchJson(this.baseUrl + path);
131
- }
132
- /** POST helper — returns parsed JSON. */
133
- async post(path, body) {
134
- return postJson(this.baseUrl + path, body);
129
+ function underlying(error) {
130
+ const inner = error.cause instanceof CatalogClientError ? error.cause.cause : error.cause;
131
+ return inner ?? error;
132
+ }
133
+ /**
134
+ * Tool result for a failed call.
135
+ *
136
+ * @param error - What the SDK call threw.
137
+ * @param baseUrl - The service URL the call used.
138
+ * @returns The error result.
139
+ */
140
+ function toolError(error, baseUrl) {
141
+ if (!isMetaError(error))
142
+ return fail(error);
143
+ if (error.status >= 300) {
144
+ return fail(new Error(`HTTP ${String(error.status)}: ${errorBodyText(error)}`));
135
145
  }
136
- /**
137
- * Interpolate a parameterized endpoint path.
138
- * Replaces `:param` placeholders with URI-encoded values.
139
- */
140
- endpointPath(name, params) {
141
- let p = getEndpoint(name).path;
142
- if (params) {
143
- for (const [key, value] of Object.entries(params)) {
144
- p = p.replace(`:${key}`, encodeURIComponent(value));
146
+ if (error.issues !== undefined)
147
+ return fail(error);
148
+ return connectionFail(underlying(error), baseUrl, PLUGIN_ID);
149
+ }
150
+
151
+ /**
152
+ * Shared parts of the custom tools: catalog descriptions, the mapping of
153
+ * tool parameters to query strings, and the call wrapper that turns a
154
+ * failure into the 0.16.0-worded tool error. Pure.
155
+ *
156
+ * @module toolHelpers
157
+ */
158
+ /** A catalog entry's description. */
159
+ function entryDescription(name) {
160
+ const entry = META_CATALOG.entries.find((e) => e.name === name);
161
+ return entry?.description ?? '';
162
+ }
163
+ /** A number or boolean as a query string; anything else stays absent. */
164
+ const str = (value) => typeof value === 'number' || typeof value === 'boolean'
165
+ ? String(value)
166
+ : undefined;
167
+ /** A boolean filter as its query flag; undefined stays absent. */
168
+ const flag = (value) => value === undefined ? undefined : value ? 'true' : 'false';
169
+ /** A field list as the comma-separated query value. */
170
+ const list = (value) => Array.isArray(value) && value.length > 0 ? value.join(',') : undefined;
171
+ /** Run a call: its result, or the 0.16.0-worded error (`toolError`). */
172
+ function wrap(baseUrl, fn) {
173
+ return fn()
174
+ .then(ok)
175
+ .catch((error) => toolError(error, baseUrl));
176
+ }
177
+
178
+ /**
179
+ * The custom tools that change state: `meta_trigger`, `meta_seed`,
180
+ * `meta_unlock` and `meta_update`, each calling its catalog entry
181
+ * through the SDK.
182
+ *
183
+ * @module writeTools
184
+ */
185
+ function buildMetaTriggerTool(client, baseUrl) {
186
+ return {
187
+ name: 'meta_trigger',
188
+ description: entryDescription('synthesize'),
189
+ parameters: {
190
+ type: 'object',
191
+ properties: {
192
+ path: {
193
+ type: 'string',
194
+ description: 'Optional: specific .meta/ or owner path to synthesize.',
195
+ },
196
+ },
197
+ },
198
+ execute: async (_id, params) => wrap(baseUrl, () => client.synthesize(params.path ? { path: params.path } : {})),
199
+ };
200
+ }
201
+ function buildMetaSeedTool(client, baseUrl) {
202
+ return {
203
+ name: 'meta_seed',
204
+ description: entryDescription('seed'),
205
+ parameters: {
206
+ type: 'object',
207
+ properties: {
208
+ path: {
209
+ type: 'string',
210
+ description: 'Owner directory path to seed with .meta/ and meta.json.',
211
+ },
212
+ crossRefs: {
213
+ type: 'string',
214
+ description: 'JSON array of cross-ref owner paths (e.g. \'["<drive label>:/path/a","<drive label>:/path/b"]\'). Use server_drives to discover available drive labels.',
215
+ },
216
+ steer: {
217
+ type: 'string',
218
+ description: 'Steering prompt written as _steer in the initial meta.json.',
219
+ },
220
+ },
221
+ required: ['path'],
222
+ },
223
+ execute: async (_id, params) => {
224
+ let crossRefs;
225
+ if (typeof params.crossRefs === 'string' && params.crossRefs) {
226
+ try {
227
+ crossRefs = JSON.parse(params.crossRefs);
228
+ }
229
+ catch (e) {
230
+ const message = e instanceof Error ? e.message : String(e);
231
+ return ok({
232
+ error: 'Invalid JSON in crossRefs parameter.',
233
+ details: message,
234
+ });
235
+ }
145
236
  }
146
- }
147
- return p;
148
- }
149
- /** GET /status — service health + queue state. */
150
- async status() {
151
- return this.get(this.endpointPath('status'));
152
- }
153
- /** GET /metas — list all meta entities with summary. */
154
- async listMetas(params) {
155
- const qs = new URLSearchParams();
156
- if (params?.pathPrefix)
157
- qs.set('pathPrefix', params.pathPrefix);
158
- if (params?.hasError !== undefined)
159
- qs.set('hasError', String(params.hasError));
160
- if (params?.staleHours !== undefined)
161
- qs.set('staleHours', String(params.staleHours));
162
- if (params?.neverSynthesized !== undefined)
163
- qs.set('neverSynthesized', String(params.neverSynthesized));
164
- if (params?.locked !== undefined)
165
- qs.set('locked', String(params.locked));
166
- if (params?.disabled !== undefined)
167
- qs.set('disabled', String(params.disabled));
168
- if (params?.fields?.length)
169
- qs.set('fields', params.fields.join(','));
170
- const query = qs.toString();
171
- return this.get(this.endpointPath('listMetas') + (query ? '?' + query : ''));
172
- }
173
- /** PATCH /metas/:path — update user-settable reserved properties. */
174
- async update(metaPath, updates) {
175
- return fetchJson(`${this.baseUrl}${this.endpointPath('updateMeta', { path: metaPath })}`, {
176
- method: 'PATCH',
177
- headers: { 'Content-Type': 'application/json' },
178
- body: JSON.stringify(updates),
179
- });
180
- }
181
- /** GET /metas/:path — detail for a single meta. */
182
- async detail(metaPath, options) {
183
- const qs = new URLSearchParams();
184
- if (options?.includeArchive !== undefined)
185
- qs.set('includeArchive', String(options.includeArchive));
186
- if (options?.fields?.length)
187
- qs.set('fields', options.fields.join(','));
188
- const query = qs.toString();
189
- return this.get(this.endpointPath('metaDetail', { path: metaPath }) +
190
- (query ? '?' + query : ''));
191
- }
192
- /** GET /preview — dry-run next synthesis candidate. */
193
- async preview(path) {
194
- const qs = path ? '?path=' + encodeURIComponent(path) : '';
195
- return this.get(this.endpointPath('preview') + qs);
196
- }
197
- /** POST /synthesize — enqueue synthesis. */
198
- async synthesize(path) {
199
- return this.post(this.endpointPath('synthesize'), path ? { path } : {});
200
- }
201
- /** POST /seed — create .meta/ for a path. */
202
- async seed(path, crossRefs, steer) {
203
- const body = { path };
204
- if (crossRefs !== undefined)
205
- body.crossRefs = crossRefs;
206
- if (steer !== undefined)
207
- body.steer = steer;
208
- return this.post(this.endpointPath('seed'), body);
209
- }
210
- /** POST /unlock — remove .lock from a meta entity. */
211
- async unlock(path) {
212
- return this.post(this.endpointPath('unlock'), { path });
213
- }
214
- /** GET /config — query service config with optional JSONPath. */
215
- async config(path) {
216
- const qs = path ? '?path=' + encodeURIComponent(path) : '';
217
- return this.get(this.endpointPath('config') + qs);
218
- }
219
- /** GET /queue — current queue state. */
220
- async queue() {
221
- return this.get(this.endpointPath('queue'));
222
- }
223
- /** POST /queue/clear — remove all pending queue items. */
224
- async clearQueue() {
225
- return this.post(this.endpointPath('queueClear'), {});
226
- }
227
- /** POST /synthesize/abort — abort current synthesis. */
228
- async abort() {
229
- return this.post(this.endpointPath('abort'), {});
230
- }
237
+ const steer = typeof params.steer === 'string' && params.steer
238
+ ? params.steer
239
+ : undefined;
240
+ return wrap(baseUrl, () => client.seed({ path: params.path, crossRefs, steer }));
241
+ },
242
+ };
243
+ }
244
+ function buildMetaUnlockTool(client, baseUrl) {
245
+ return {
246
+ name: 'meta_unlock',
247
+ description: entryDescription('unlock'),
248
+ parameters: {
249
+ type: 'object',
250
+ properties: {
251
+ path: {
252
+ type: 'string',
253
+ description: 'Path to the .meta/ directory or owner directory.',
254
+ },
255
+ },
256
+ required: ['path'],
257
+ },
258
+ execute: async (_id, params) => wrap(baseUrl, () => client.unlock({ path: params.path })),
259
+ };
260
+ }
261
+ function buildMetaUpdateTool(client, baseUrl) {
262
+ return {
263
+ name: 'meta_update',
264
+ description: entryDescription('updateMeta'),
265
+ parameters: {
266
+ type: 'object',
267
+ properties: {
268
+ path: {
269
+ type: 'string',
270
+ description: 'Path to the .meta/ directory or owner directory.',
271
+ },
272
+ updates: {
273
+ type: 'object',
274
+ description: 'Properties to set. Supported: _steer, _emphasis, _depth, _crossRefs, _disabled. Set to null to remove.',
275
+ properties: {
276
+ _steer: { type: ['string', 'null'] },
277
+ _emphasis: { type: ['number', 'null'] },
278
+ _depth: { type: ['number', 'null'] },
279
+ _crossRefs: {
280
+ oneOf: [
281
+ { type: 'array', items: { type: 'string' } },
282
+ { type: 'null' },
283
+ ],
284
+ },
285
+ _disabled: { type: ['boolean', 'null'] },
286
+ },
287
+ },
288
+ },
289
+ required: ['path', 'updates'],
290
+ },
291
+ execute: async (_id, params) => wrap(baseUrl, () => client.updateMeta({
292
+ ...params.updates,
293
+ path: params.path,
294
+ })),
295
+ };
231
296
  }
232
297
 
233
298
  /**
234
299
  * Custom domain-specific tool descriptors for the meta plugin.
235
300
  *
236
301
  * These tools supplement the standard factory-produced tools
237
- * (meta_status, meta_config, meta_config_apply, meta_service).
302
+ * (meta_status, meta_config, meta_config_apply, meta_service). Each calls
303
+ * its catalog entry through the SDK (Decision 187); descriptions come from
304
+ * `META_CATALOG`; input schemas stay hand-written (Decision 188).
238
305
  *
239
306
  * @module customTools
240
307
  */
241
308
  /** Build the array of custom domain-specific tool descriptors. */
242
- function buildCustomTools(client, baseUrl) {
309
+ function buildCustomTools(baseUrl) {
310
+ const client = createMetaClient({ baseUrl });
243
311
  return [
244
312
  buildMetaListTool(client, baseUrl),
245
313
  buildMetaDetailTool(client, baseUrl),
@@ -251,16 +319,10 @@ function buildCustomTools(client, baseUrl) {
251
319
  buildMetaUpdateTool(client, baseUrl),
252
320
  ];
253
321
  }
254
- /** Wrap tool execution with connection error handling. */
255
- function wrap(baseUrl, fn) {
256
- return fn()
257
- .then(ok)
258
- .catch((error) => connectionFail(error, baseUrl, PLUGIN_ID));
259
- }
260
322
  function buildMetaListTool(client, baseUrl) {
261
323
  return {
262
324
  name: 'meta_list',
263
- description: getEndpoint('listMetas').description,
325
+ description: entryDescription('listMetas'),
264
326
  parameters: {
265
327
  type: 'object',
266
328
  properties: {
@@ -289,13 +351,13 @@ function buildMetaListTool(client, baseUrl) {
289
351
  execute: async (_id, params) => {
290
352
  const filter = params.filter;
291
353
  return wrap(baseUrl, () => client.listMetas({
292
- pathPrefix: params.pathPrefix,
293
- hasError: filter?.hasError,
294
- staleHours: filter?.staleHours,
295
- neverSynthesized: filter?.neverSynthesized,
296
- locked: filter?.locked,
297
- disabled: filter?.disabled,
298
- fields: params.fields,
354
+ pathPrefix: params.pathPrefix || undefined,
355
+ hasError: flag(filter?.hasError),
356
+ staleHours: str(filter?.staleHours),
357
+ neverSynthesized: flag(filter?.neverSynthesized),
358
+ locked: flag(filter?.locked),
359
+ disabled: flag(filter?.disabled),
360
+ fields: list(params.fields),
299
361
  }));
300
362
  },
301
363
  };
@@ -303,7 +365,7 @@ function buildMetaListTool(client, baseUrl) {
303
365
  function buildMetaDetailTool(client, baseUrl) {
304
366
  return {
305
367
  name: 'meta_detail',
306
- description: getEndpoint('metaDetail').description,
368
+ description: entryDescription('metaDetail'),
307
369
  parameters: {
308
370
  type: 'object',
309
371
  properties: {
@@ -323,16 +385,17 @@ function buildMetaDetailTool(client, baseUrl) {
323
385
  },
324
386
  required: ['path'],
325
387
  },
326
- execute: async (_id, params) => wrap(baseUrl, () => client.detail(params.path, {
327
- includeArchive: params.includeArchive,
328
- fields: params.fields,
388
+ execute: async (_id, params) => wrap(baseUrl, () => client.metaDetail({
389
+ path: params.path,
390
+ includeArchive: str(params.includeArchive),
391
+ fields: list(params.fields),
329
392
  })),
330
393
  };
331
394
  }
332
395
  function buildMetaPreviewTool(client, baseUrl) {
333
396
  return {
334
397
  name: 'meta_preview',
335
- description: getEndpoint('preview').description,
398
+ description: entryDescription('preview'),
336
399
  parameters: {
337
400
  type: 'object',
338
401
  properties: {
@@ -342,89 +405,13 @@ function buildMetaPreviewTool(client, baseUrl) {
342
405
  },
343
406
  },
344
407
  },
345
- execute: async (_id, params) => wrap(baseUrl, () => client.preview(params.path)),
346
- };
347
- }
348
- function buildMetaTriggerTool(client, baseUrl) {
349
- return {
350
- name: 'meta_trigger',
351
- description: getEndpoint('synthesize').description,
352
- parameters: {
353
- type: 'object',
354
- properties: {
355
- path: {
356
- type: 'string',
357
- description: 'Optional: specific .meta/ or owner path to synthesize.',
358
- },
359
- },
360
- },
361
- execute: async (_id, params) => wrap(baseUrl, () => client.synthesize(params.path)),
362
- };
363
- }
364
- function buildMetaSeedTool(client, baseUrl) {
365
- return {
366
- name: 'meta_seed',
367
- description: getEndpoint('seed').description,
368
- parameters: {
369
- type: 'object',
370
- properties: {
371
- path: {
372
- type: 'string',
373
- description: 'Owner directory path to seed with .meta/ and meta.json.',
374
- },
375
- crossRefs: {
376
- type: 'string',
377
- description: 'JSON array of cross-ref owner paths (e.g. \'["<drive label>:/path/a","<drive label>:/path/b"]\'). Use server_drives to discover available drive labels.',
378
- },
379
- steer: {
380
- type: 'string',
381
- description: 'Steering prompt written as _steer in the initial meta.json.',
382
- },
383
- },
384
- required: ['path'],
385
- },
386
- execute: async (_id, params) => {
387
- let crossRefs;
388
- if (typeof params.crossRefs === 'string' && params.crossRefs) {
389
- try {
390
- crossRefs = JSON.parse(params.crossRefs);
391
- }
392
- catch (e) {
393
- const message = e instanceof Error ? e.message : String(e);
394
- return ok({
395
- error: 'Invalid JSON in crossRefs parameter.',
396
- details: message,
397
- });
398
- }
399
- }
400
- const steer = typeof params.steer === 'string' && params.steer
401
- ? params.steer
402
- : undefined;
403
- return wrap(baseUrl, () => client.seed(params.path, crossRefs, steer));
404
- },
405
- };
406
- }
407
- function buildMetaUnlockTool(client, baseUrl) {
408
- return {
409
- name: 'meta_unlock',
410
- description: getEndpoint('unlock').description,
411
- parameters: {
412
- type: 'object',
413
- properties: {
414
- path: {
415
- type: 'string',
416
- description: 'Path to the .meta/ directory or owner directory.',
417
- },
418
- },
419
- required: ['path'],
420
- },
421
- execute: async (_id, params) => wrap(baseUrl, () => client.unlock(params.path)),
408
+ execute: async (_id, params) => wrap(baseUrl, () => client.preview(params.path ? { path: params.path } : {})),
422
409
  };
423
410
  }
424
411
  function buildMetaQueueTool(client, baseUrl) {
425
412
  return {
426
413
  name: 'meta_queue',
427
- description: `Queue management. list: ${getEndpoint('queue').description} clear: ${getEndpoint('queueClear').description} abort: ${getEndpoint('abort').description}`,
414
+ description: `Queue management. list: ${entryDescription('queue')} clear: ${entryDescription('queueClear')} abort: ${entryDescription('abort')}`,
428
415
  parameters: {
429
416
  type: 'object',
430
417
  properties: {
@@ -442,7 +429,7 @@ function buildMetaQueueTool(client, baseUrl) {
442
429
  case 'list':
443
430
  return wrap(baseUrl, () => client.queue());
444
431
  case 'clear':
445
- return wrap(baseUrl, () => client.clearQueue());
432
+ return wrap(baseUrl, () => client.queueClear());
446
433
  case 'abort':
447
434
  return wrap(baseUrl, () => client.abort());
448
435
  default:
@@ -451,39 +438,6 @@ function buildMetaQueueTool(client, baseUrl) {
451
438
  },
452
439
  };
453
440
  }
454
- function buildMetaUpdateTool(client, baseUrl) {
455
- return {
456
- name: 'meta_update',
457
- description: getEndpoint('updateMeta').description,
458
- parameters: {
459
- type: 'object',
460
- properties: {
461
- path: {
462
- type: 'string',
463
- description: 'Path to the .meta/ directory or owner directory.',
464
- },
465
- updates: {
466
- type: 'object',
467
- description: 'Properties to set. Supported: _steer, _emphasis, _depth, _crossRefs, _disabled. Set to null to remove.',
468
- properties: {
469
- _steer: { type: ['string', 'null'] },
470
- _emphasis: { type: ['number', 'null'] },
471
- _depth: { type: ['number', 'null'] },
472
- _crossRefs: {
473
- oneOf: [
474
- { type: 'array', items: { type: 'string' } },
475
- { type: 'null' },
476
- ],
477
- },
478
- _disabled: { type: ['boolean', 'null'] },
479
- },
480
- },
481
- },
482
- required: ['path', 'updates'],
483
- },
484
- execute: async (_id, params) => wrap(baseUrl, () => client.update(params.path, params.updates)),
485
- };
486
- }
487
441
 
488
442
  /**
489
443
  * Meta tool registrations for OpenClaw.
@@ -507,8 +461,7 @@ const CONFIG_ROOT_TOOLS = new Map([
507
461
  ['meta_service', (params) => params.action === 'install'],
508
462
  ]);
509
463
  /** Register all meta_* tools (standard + custom). */
510
- function registerMetaTools(api, client, descriptor, gate) {
511
- const baseUrl = client.getBaseUrl();
464
+ function registerMetaTools(api, baseUrl, descriptor, gate) {
512
465
  // Standard tools from factory: meta_status, meta_config, meta_config_apply, meta_service.
513
466
  // `apiUrl` is resolved on every call with the same resolution as the
514
467
  // custom tools, so the standard HTTP tools hit the configured service.
@@ -520,7 +473,7 @@ function registerMetaTools(api, client, descriptor, gate) {
520
473
  api.registerTool(needsConfigRoot ? requireConfigRoot(tool, gate, needsConfigRoot) : tool);
521
474
  }
522
475
  // Custom domain-specific tools
523
- for (const tool of buildCustomTools(client, baseUrl)) {
476
+ for (const tool of buildCustomTools(baseUrl)) {
524
477
  api.registerTool(tool);
525
478
  }
526
479
  }
@@ -587,9 +540,9 @@ function buildDescriptor() {
587
540
  * lazily when a tool that needs it runs.
588
541
  */
589
542
  function register(api) {
590
- const client = new MetaServiceClient({ apiUrl: getServiceUrl(api) });
543
+ const baseUrl = getServiceUrl(api).replace(/\/$/, '');
591
544
  warnIfConfigRootMissing(api);
592
- registerMetaTools(api, client, buildDescriptor(), createConfigRootGate(api));
545
+ registerMetaTools(api, baseUrl, buildDescriptor(), createConfigRootGate(api));
593
546
  }
594
547
 
595
548
  export { register as default, pluginConfigSchema };
@@ -2,8 +2,8 @@
2
2
  * Rollup configuration for the OpenClaw plugin package.
3
3
  * Single entry point: the plugin (ESM + declarations).
4
4
  *
5
- * Runtime dependencies (`@karmaniverous/jeeves`, `@karmaniverous/jeeves-meta-core`,
6
- * `zod`) are externalized and resolved from the plugin's installed dependencies.
5
+ * Every runtime dependency (and its sub-paths) is externalized and resolved
6
+ * from the plugin's installed dependencies.
7
7
  */
8
8
  import type { RollupOptions } from 'rollup';
9
9
  declare const _default: RollupOptions[];
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: jeeves-meta
3
3
  description: >
4
- Knowledge synthesis with jeeves-meta. Use when checking synthesis status, listing or inspecting .meta/ entities, triggering or seeding synthesis, managing the meta service, or troubleshooting stale, failed, or locked metas.
4
+ jeeves-meta knowledge synthesis: status, list or inspect .meta/ entities, trigger or seed synthesis, queue, service, stale, failed or locked metas.
5
5
  ---
6
6
 
7
7
  # jeeves-meta — OpenClaw Skill
@@ -30,7 +30,7 @@ List all `.meta/` directories with summary stats and per-meta projection. Suppor
30
30
 
31
31
  ### meta_detail
32
32
 
33
- Full detail for a single meta, with optional archive history. Includes `_phaseState` showing the per-phase state machine status.
33
+ Full detail for a single meta, with optional archive history. Includes `phaseState` showing the per-phase state machine status.
34
34
 
35
35
  **Parameters:**
36
36
 
@@ -74,7 +74,7 @@ Remove a stale `.lock` file from a meta entity. Locks are created during synthes
74
74
 
75
75
  ### meta_config
76
76
 
77
- Query the running service configuration. Supports optional JSONPath filtering to extract specific settings. Sensitive fields (e.g. `gatewayApiKey`) are redacted.
77
+ Query the running service configuration. Supports optional JSONPath filtering to extract specific settings. Sensitive fields (`gatewayApiKey`, `serverKey`) are redacted.
78
78
 
79
79
  **Parameters:**
80
80
 
@@ -154,8 +154,19 @@ Key settings:
154
154
  | `skipUnchanged` | true | Skip candidates with no changes since last synthesis |
155
155
  | `thinking` | `low` | Thinking level for spawned LLM sessions |
156
156
  | `port` | 1938 | HTTP API listen port |
157
-
158
- | `schedule` | `*/30 * * * *` | Cron expression for automatic synthesis scheduling | | `serverUrl` | `http://127.0.0.1:1934` | jeeves-server base URL for progress report links | | `templates` | (see docs) | Templates for phase start, end, and error progress reports | | `reportChannel` | (optional) | Gateway channel name (e.g. `slack`). Legacy: also used as target if `reportTarget` is unset. | | `reportTarget` | (optional) | Channel/user ID to send progress messages to | | `tier2ScanLimit` | 50 | Max all-fresh candidates to scan per tick in Tier 2 invalidation | | `workspaceDir` | OS tmpdir + `/jeeves-meta` | Directory for sub-agent output staging files | | `stagingRetries` | 10 | Max retries when staging file not yet visible after session completion (min 0) | | `stagingRetryDelayMs` | 250 | Delay between staging file retry attempts in ms (min 0) | | `previewDeltaFilesCap` | 50 | Max delta files included in `/preview` response; excess sets `deltaFilesTruncated: true` (min 1) | | `logging.level` | `info` | Log level (trace/debug/info/warn/error) | | `logging.file` | (optional) | Log file path |
157
+ | `schedule` | `*/30 * * * *` | Cron expression for automatic synthesis scheduling |
158
+ | `serverUrl` | `http://127.0.0.1:1934` | jeeves-server base URL for progress report links |
159
+ | `serverKey` | (optional) | jeeves-server machine key seed for progress-report links (required from jeeves-server 3.15.0). The service sends the derived key as `k`; on 401/403 it logs one warning and reports without links. Redacted in `meta_config`. |
160
+ | `templates` | (see docs) | Templates for phase start, end, and error progress reports |
161
+ | `reportChannel` | (optional) | Gateway channel name (e.g. `slack`). Legacy: also used as target if `reportTarget` is unset. |
162
+ | `reportTarget` | (optional) | Channel/user ID to send progress messages to |
163
+ | `tier2ScanLimit` | 50 | Max all-fresh candidates to scan per tick in Tier 2 invalidation |
164
+ | `workspaceDir` | OS tmpdir + `/jeeves-meta` | Directory for sub-agent output staging files |
165
+ | `stagingRetries` | 10 | Max retries when staging file not yet visible after session completion (min 0) |
166
+ | `stagingRetryDelayMs` | 250 | Delay between staging file retry attempts in ms (min 0) |
167
+ | `previewDeltaFilesCap` | 50 | Max delta files included in `/preview` response; excess sets `deltaFilesTruncated: true` (min 1) |
168
+ | `logging.level` | `info` | Log level (trace/debug/info/warn/error) |
169
+ | `logging.file` | (optional) | Log file path |
159
170
 
160
171
  ### Meta Discovery
161
172
 
@@ -425,10 +436,30 @@ The service exposes these endpoints (default port 1938):
425
436
  | POST | `/unlock` | Remove `.lock` file from a meta entity |
426
437
  | GET | `/config` | Query sanitized config with optional JSONPath (`?path=$.schedule`) |
427
438
  | POST | `/config/apply` | Apply a config patch (merge or replace) |
428
- | GET | `/queue` | Queue state: current (with phase), overrides, automatic, pending |
439
+ | GET | `/queue` | Queue state: `current` (with phase), `pending` overrides, `automatic` candidates |
429
440
  | POST | `/queue/clear` | Remove all override queue entries |
441
+ | GET | `/openapi.json` | OpenAPI 3.1 document of every route |
442
+
443
+ All endpoints return JSON and validate their requests (a malformed request is `HTTP 400` `FST_ERR_VALIDATION`). The OpenClaw plugin tools call these endpoints through `@karmaniverous/jeeves-meta-sdk`.
444
+
445
+ ### Error codes
446
+
447
+ A failed tool reads `Error: HTTP <status>: <body>`; the body is `{ statusCode, code, error, message }`. Act on the `code`:
448
+
449
+ | `code` | Status | What it means | What to do |
450
+ | --- | --- | --- | --- |
451
+ | `META_NOT_FOUND` | 404 | No meta at that path | Check the path with `meta_list` (owner path or `.meta/` path); create it with `meta_seed` |
452
+ | `META_ALREADY_EXISTS` | 409 | `meta_seed` on a path that already has `.meta/` | Nothing to create; use `meta_update` to change it |
453
+ | `META_NOT_LOCKED` | 409 | `meta_unlock` on a meta with no lock | Nothing to do; it is already unlocked |
454
+ | `META_WATCHER_UNAVAILABLE` | 503 | The service could not reach jeeves-watcher | Check `watcher_status`; retry once the watcher is up (see Troubleshooting) |
455
+ | `META_NOT_READY` | 503 | The service is starting | Retry in a few seconds; `meta_config` works meanwhile |
456
+ | `META_INVALID_JSONPATH` | 400 | `meta_config` with a JSONPath that cannot be evaluated | Fix the expression (e.g. `$.schedule`) |
457
+ | `META_CONFIG_UNAVAILABLE` | 500 | `meta_config_apply` on a service started without a config file | Restart the service with `--config <path>`, or edit the config file directly |
458
+ | `JEEVES_CONFIG_INVALID` | 400 | `meta_config_apply`: the patched config is invalid | Read `message` for the failing field; fix the patch |
459
+ | `FST_ERR_VALIDATION` | 400 | A tool sent a request its route rejects | Read `message`; check the tool's parameters |
460
+ | (no code) | 500 | An unexpected failure | Read the service log (`jeeves-meta service status`); report it |
430
461
 
431
- All endpoints return JSON. The OpenClaw plugin tools are thin wrappers around these endpoints.
462
+ No error carries `Retry-After`. A connection error (no HTTP status) means the service is unreachable: see Troubleshooting.
432
463
 
433
464
  ## Service CLI
434
465
 
@@ -438,7 +469,7 @@ The service package ships a CLI:
438
469
  jeeves-meta <command> [options]
439
470
  ```
440
471
 
441
- Commands: `start`, `status`, `list`, `detail`, `preview`, `synthesize`, `seed`, `unlock`, `abort`, `prune`, `config`, `queue list|clear`, `service install|start|stop|status|remove`.
472
+ Commands: `start`, `status`, `list`, `detail`, `preview`, `synthesize`, `seed`, `unlock`, `abort`, `prune` (no API route yet, #69), `config`, `queue list|clear`, `service install|start|stop|status|remove`.
442
473
 
443
474
  Config resolution: `--config` flag → `JEEVES_META_CONFIG` env var → error. All client commands support `-p, --port` to specify the service port (default: 1938). The `start` command uses `--config`/`-c` instead (port is read from the config file).
444
475
 
@@ -2,11 +2,12 @@
2
2
  * Custom domain-specific tool descriptors for the meta plugin.
3
3
  *
4
4
  * These tools supplement the standard factory-produced tools
5
- * (meta_status, meta_config, meta_config_apply, meta_service).
5
+ * (meta_status, meta_config, meta_config_apply, meta_service). Each calls
6
+ * its catalog entry through the SDK (Decision 187); descriptions come from
7
+ * `META_CATALOG`; input schemas stay hand-written (Decision 188).
6
8
  *
7
9
  * @module customTools
8
10
  */
9
11
  import { type ToolDescriptor } from '@karmaniverous/jeeves';
10
- import type { MetaServiceClient } from './serviceClient.js';
11
12
  /** Build the array of custom domain-specific tool descriptors. */
12
- export declare function buildCustomTools(client: MetaServiceClient, baseUrl: string): ToolDescriptor[];
13
+ export declare function buildCustomTools(baseUrl: string): ToolDescriptor[];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @module customTools.test
3
+ * What each custom tool sends (spec 4.4.7): tool parameters mapped to the
4
+ * SDK input as 0.16.0 mapped them to the URL (boolean filters as
5
+ * `true`/`false`, field lists comma-joined, absent values left out), and
6
+ * the tools' own parameter errors.
7
+ */
8
+ export {};
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Tool result for a failed SDK call, worded as the 0.16.0 tools worded it
3
+ * (spec 4.4.7). A non-2xx response reads as `Error: HTTP {status}: {body}`,
4
+ * with the body exactly as received (now the standard error body, so the
5
+ * text carries the code and message); an unreachable service gets the
6
+ * connection guidance; anything else reads as `Error: {message}`. Pure.
7
+ *
8
+ * @module toolError
9
+ */
10
+ import { type ToolResult } from '@karmaniverous/jeeves';
11
+ /**
12
+ * Tool result for a failed call.
13
+ *
14
+ * @param error - What the SDK call threw.
15
+ * @param baseUrl - The service URL the call used.
16
+ * @returns The error result.
17
+ */
18
+ export declare function toolError(error: unknown, baseUrl: string): ToolResult;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @module toolError.test
3
+ * Tool results for failed calls (spec 4.4.7), through real tools over a
4
+ * stubbed `fetch`: a non-2xx reply reads `HTTP {status}: {body}` with the
5
+ * body as received, an unreachable service gets the connection guidance,
6
+ * and a timeout, an invalid reply or a non-SDK error reads
7
+ * `Error: {message}`.
8
+ */
9
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared parts of the custom tools: catalog descriptions, the mapping of
3
+ * tool parameters to query strings, and the call wrapper that turns a
4
+ * failure into the 0.16.0-worded tool error. Pure.
5
+ *
6
+ * @module toolHelpers
7
+ */
8
+ import { type ToolResult } from '@karmaniverous/jeeves';
9
+ import { type MetaEntryName } from '@karmaniverous/jeeves-meta-core';
10
+ /** A catalog entry's description. */
11
+ export declare function entryDescription(name: MetaEntryName): string;
12
+ /** A number or boolean as a query string; anything else stays absent. */
13
+ export declare const str: (value: unknown) => string | undefined;
14
+ /** A boolean filter as its query flag; undefined stays absent. */
15
+ export declare const flag: (value: unknown) => "true" | "false" | undefined;
16
+ /** A field list as the comma-separated query value. */
17
+ export declare const list: (value: unknown) => string | undefined;
18
+ /** Run a call: its result, or the 0.16.0-worded error (`toolError`). */
19
+ export declare function wrap(baseUrl: string, fn: () => Promise<unknown>): Promise<ToolResult>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @module toolSnapshot.test
3
+ * The registered tools against plugin 0.16.0 (spec 4.4.7): the same tool
4
+ * names, and each tool's description and input schema unchanged, so agents
5
+ * see the same tools. `tools-0.16.0.json` was captured from the published
6
+ * `@karmaniverous/jeeves-meta-openclaw@0.16.0` with the same fake API.
7
+ * The standard tools come from jeeves' `createPluginToolset`; their input
8
+ * schemas changed with jeeves 0.11.0 (T2), as listed in `JEEVES_0_11`.
9
+ */
10
+ export {};
@@ -9,6 +9,5 @@
9
9
  */
10
10
  import { type JeevesComponentDescriptor, type PluginApi } from '@karmaniverous/jeeves';
11
11
  import { type ConfigRootGate } from './configRoot.js';
12
- import type { MetaServiceClient } from './serviceClient.js';
13
12
  /** Register all meta_* tools (standard + custom). */
14
- export declare function registerMetaTools(api: PluginApi, client: MetaServiceClient, descriptor: JeevesComponentDescriptor, gate: ConfigRootGate): void;
13
+ export declare function registerMetaTools(api: PluginApi, baseUrl: string, descriptor: JeevesComponentDescriptor, gate: ConfigRootGate): void;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The custom tools that change state: `meta_trigger`, `meta_seed`,
3
+ * `meta_unlock` and `meta_update`, each calling its catalog entry
4
+ * through the SDK.
5
+ *
6
+ * @module writeTools
7
+ */
8
+ import { type ToolDescriptor } from '@karmaniverous/jeeves';
9
+ import type { MetaClient } from '@karmaniverous/jeeves-meta-sdk';
10
+ export declare function buildMetaTriggerTool(client: MetaClient, baseUrl: string): ToolDescriptor;
11
+ export declare function buildMetaSeedTool(client: MetaClient, baseUrl: string): ToolDescriptor;
12
+ export declare function buildMetaUnlockTool(client: MetaClient, baseUrl: string): ToolDescriptor;
13
+ export declare function buildMetaUpdateTool(client: MetaClient, baseUrl: string): ToolDescriptor;
@@ -2,7 +2,7 @@
2
2
  "id": "jeeves-meta-openclaw",
3
3
  "name": "Jeeves Meta",
4
4
  "description": "Knowledge synthesis tools — trigger synthesis, view status, manage entities.",
5
- "version": "0.15.0",
5
+ "version": "0.17.0-0",
6
6
  "skills": ["dist/skills/jeeves-meta"],
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -4,24 +4,25 @@
4
4
  "url": "https://github.com/karmaniverous/jeeves-meta/issues"
5
5
  },
6
6
  "dependencies": {
7
- "@karmaniverous/jeeves": "0.6.0",
8
- "@karmaniverous/jeeves-meta-core": "^0.2.2",
9
- "zod": "^4.6.5"
7
+ "@karmaniverous/jeeves": "0.11.0",
8
+ "@karmaniverous/jeeves-meta-core": "^0.4.0-0",
9
+ "@karmaniverous/jeeves-meta-sdk": "^0.0.0 || ^0.1.0-0",
10
+ "zod": "^4.6.7"
10
11
  },
11
12
  "description": "OpenClaw plugin for jeeves-meta — synthesis tools and virtual rule registration",
12
13
  "devDependencies": {
13
- "@dotenvx/dotenvx": "^2.30.0",
14
+ "@dotenvx/dotenvx": "^2.38.2",
14
15
  "@rollup/plugin-commonjs": "^29.0.3",
15
16
  "@rollup/plugin-node-resolve": "^16.0.3",
16
- "@rollup/plugin-typescript": "^12.3.0",
17
+ "@rollup/plugin-typescript": "^12.3.1",
17
18
  "cross-env": "^10.1.0",
18
- "release-it": "^21.1.0",
19
- "rollup": "^4.63.5",
20
- "tslib": "^2.8.1",
21
- "vitest": "^5.0.2"
19
+ "release-it": "^21.1.2",
20
+ "rollup": "^4.68.3",
21
+ "tslib": "^2.9.1",
22
+ "vitest": "^5.1.0"
22
23
  },
23
24
  "engines": {
24
- "node": ">=22"
25
+ "node": ">=24.22.2"
25
26
  },
26
27
  "exports": {
27
28
  ".": {
@@ -61,7 +62,9 @@
61
62
  "tagName": "openclaw/${version}"
62
63
  },
63
64
  "github": {
64
- "release": true
65
+ "release": true,
66
+ "releaseName": "${name.split('/').pop()}@${version}",
67
+ "releaseNotes": "npx git-cliff --tag openclaw/${version} --unreleased --strip header"
65
68
  },
66
69
  "hooks": {
67
70
  "after:bump": [
@@ -82,7 +85,7 @@
82
85
  "git switch ${branchName}"
83
86
  ],
84
87
  "before:npm:release": [
85
- "npx git-cliff -o CHANGELOG.md",
88
+ "npx git-cliff --tag openclaw/${version} -o CHANGELOG.md",
86
89
  "git add -A"
87
90
  ]
88
91
  },
@@ -112,5 +115,5 @@
112
115
  },
113
116
  "type": "module",
114
117
  "types": "dist/index.d.ts",
115
- "version": "0.15.0"
118
+ "version": "0.17.0-0"
116
119
  }
@@ -1,101 +0,0 @@
1
- /**
2
- * Thin HTTP client for the jeeves-meta service.
3
- *
4
- * Plugin delegates all operations to the running service via HTTP.
5
- * Shared response types are imported from `@karmaniverous/jeeves-meta-core`.
6
- *
7
- * @module serviceClient
8
- */
9
- import { type GatewayDepHealth, type MetasResponse, type NextPhaseCandidate, type PhaseStateSummary, type ServiceState, type WatcherDepHealth } from '@karmaniverous/jeeves-meta-core';
10
- export type { GatewayDepHealth, MetasResponse, NextPhaseCandidate, PhaseStateSummary, ServiceState, WatcherDepHealth, };
11
- /**
12
- * Service status response from GET /status.
13
- *
14
- * The jeeves-core `createStatusHandler` wraps `getHealth()` output under
15
- * a top-level `health` key. Dependency info lives at `health.dependencies`.
16
- */
17
- export interface StatusResponse {
18
- /** Service name. */
19
- name: string;
20
- /** Service uptime in seconds. */
21
- uptime: number;
22
- /** Overall status (healthy, degraded, unhealthy). */
23
- status: string;
24
- /** Service version. */
25
- version?: string;
26
- /** Component-specific health details from getHealth(). */
27
- health: {
28
- /** Service-specific lifecycle state. */
29
- serviceState?: ServiceState;
30
- dependencies: {
31
- watcher: WatcherDepHealth;
32
- gateway: GatewayDepHealth;
33
- };
34
- /** Per-phase counts of each status, from the scheduler. */
35
- phaseStateSummary?: PhaseStateSummary;
36
- /** Next phase candidate from the scheduler. */
37
- nextPhase?: NextPhaseCandidate | null;
38
- [key: string]: unknown;
39
- };
40
- }
41
- /** Constructor config. */
42
- interface MetaServiceConfig {
43
- /** Base URL of the jeeves-meta service (e.g. http://127.0.0.1:1938). */
44
- apiUrl: string;
45
- }
46
- export declare class MetaServiceClient {
47
- private readonly baseUrl;
48
- constructor(config: MetaServiceConfig);
49
- /** Return the base URL (for error reporting). */
50
- getBaseUrl(): string;
51
- /** GET helper — returns parsed JSON. */
52
- private get;
53
- /** POST helper — returns parsed JSON. */
54
- private post;
55
- /**
56
- * Interpolate a parameterized endpoint path.
57
- * Replaces `:param` placeholders with URI-encoded values.
58
- */
59
- private endpointPath;
60
- /** GET /status — service health + queue state. */
61
- status(): Promise<StatusResponse>;
62
- /** GET /metas — list all meta entities with summary. */
63
- listMetas(params?: {
64
- pathPrefix?: string;
65
- hasError?: boolean;
66
- staleHours?: number;
67
- neverSynthesized?: boolean;
68
- locked?: boolean;
69
- disabled?: boolean;
70
- fields?: string[];
71
- }): Promise<MetasResponse>;
72
- /** PATCH /metas/:path — update user-settable reserved properties. */
73
- update(metaPath: string, updates: {
74
- _steer?: string | null;
75
- _emphasis?: number | null;
76
- _depth?: number | null;
77
- _crossRefs?: string[] | null;
78
- _disabled?: boolean | null;
79
- }): Promise<unknown>;
80
- /** GET /metas/:path — detail for a single meta. */
81
- detail(metaPath: string, options?: {
82
- includeArchive?: boolean | number;
83
- fields?: string[];
84
- }): Promise<unknown>;
85
- /** GET /preview — dry-run next synthesis candidate. */
86
- preview(path?: string): Promise<unknown>;
87
- /** POST /synthesize — enqueue synthesis. */
88
- synthesize(path?: string): Promise<unknown>;
89
- /** POST /seed — create .meta/ for a path. */
90
- seed(path: string, crossRefs?: string[], steer?: string): Promise<unknown>;
91
- /** POST /unlock — remove .lock from a meta entity. */
92
- unlock(path: string): Promise<unknown>;
93
- /** GET /config — query service config with optional JSONPath. */
94
- config(path?: string): Promise<unknown>;
95
- /** GET /queue — current queue state. */
96
- queue(): Promise<unknown>;
97
- /** POST /queue/clear — remove all pending queue items. */
98
- clearQueue(): Promise<unknown>;
99
- /** POST /synthesize/abort — abort current synthesis. */
100
- abort(): Promise<unknown>;
101
- }