@okrlinkhub/agent-factory 3.0.1 → 3.0.3

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.
Files changed (46) hide show
  1. package/README.md +185 -31
  2. package/dist/client/index.d.ts +11 -6
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +16 -0
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/api.d.ts +2 -0
  7. package/dist/component/_generated/api.d.ts.map +1 -1
  8. package/dist/component/_generated/api.js.map +1 -1
  9. package/dist/component/_generated/component.d.ts +60 -0
  10. package/dist/component/_generated/component.d.ts.map +1 -1
  11. package/dist/component/flyCleanup.d.ts +32 -0
  12. package/dist/component/flyCleanup.d.ts.map +1 -0
  13. package/dist/component/flyCleanup.js +272 -0
  14. package/dist/component/flyCleanup.js.map +1 -0
  15. package/dist/component/lib.d.ts +1 -0
  16. package/dist/component/lib.d.ts.map +1 -1
  17. package/dist/component/lib.js +1 -0
  18. package/dist/component/lib.js.map +1 -1
  19. package/dist/component/providers/fly.d.ts +23 -2
  20. package/dist/component/providers/fly.d.ts.map +1 -1
  21. package/dist/component/providers/fly.js +15 -3
  22. package/dist/component/providers/fly.js.map +1 -1
  23. package/dist/component/pushing.d.ts +4 -4
  24. package/dist/component/queue.d.ts +32 -16
  25. package/dist/component/queue.d.ts.map +1 -1
  26. package/dist/component/queue.js +44 -2
  27. package/dist/component/queue.js.map +1 -1
  28. package/dist/component/scheduler.d.ts +8 -8
  29. package/dist/component/scheduler.d.ts.map +1 -1
  30. package/dist/component/scheduler.js +59 -12
  31. package/dist/component/scheduler.js.map +1 -1
  32. package/dist/component/schema.d.ts +30 -28
  33. package/dist/component/schema.d.ts.map +1 -1
  34. package/dist/component/schema.js +1 -0
  35. package/dist/component/schema.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/client/index.ts +16 -0
  38. package/src/component/_generated/api.ts +2 -0
  39. package/src/component/_generated/component.ts +72 -0
  40. package/src/component/flyCleanup.ts +386 -0
  41. package/src/component/lib.test.ts +280 -4
  42. package/src/component/lib.ts +1 -0
  43. package/src/component/providers/fly.ts +39 -5
  44. package/src/component/queue.ts +55 -2
  45. package/src/component/scheduler.ts +100 -16
  46. package/src/component/schema.ts +1 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Convex Agent Factory
2
2
 
3
- [![npm version](https://badge.fury.io/js/@example%2Fagent-factory.svg)](https://badge.fury.io/js/@example%2Fagent-factory)
3
+ [npm version](https://badge.fury.io/js/@example%2Fagent-factory)
4
4
 
5
5
  A Convex component for hydration-based orchestration of OpenClaw agents on a generic worker pool (Fly Machines first, provider abstraction built-in).
6
6
 
@@ -25,24 +25,28 @@ export default app;
25
25
  Version `1.0.0` introduces a **worker lifecycle breaking change**.
26
26
 
27
27
  What changed:
28
+
28
29
  - `workers.status` is no longer binary.
29
30
  - New persisted statuses are now possible: `draining` and `stopping`.
30
31
  - The lifecycle is now `active -> draining -> stopping -> stopped`.
31
32
  - `active` now means **claimable**, not just "row exists and machine once existed".
32
33
 
33
34
  Current status values:
35
+
34
36
  - `active`: worker is healthy and can claim new jobs.
35
37
  - `draining`: worker must stop claiming and is waiting for final snapshot / shutdown progression.
36
38
  - `stopping`: final snapshot is ready or provider teardown is in progress / pending retry.
37
39
  - `stopped`: terminal state for that worker instance. Stopped workers are never reactivated.
38
40
 
39
41
  Important compatibility notes:
42
+
40
43
  - **No manual data migration is required** if your existing rows only contain `active` or `stopped`.
41
44
  - **Consumer code may require updates** if it assumes `worker.status` can only be `active` or `stopped`.
42
45
  - Any exhaustive `switch` / `if` logic, dashboards, alerts, or admin tools that parse worker status must handle `draining` and `stopping`.
43
46
  - `workerControlState` is stricter now: workers in non-claimable states, stale-heartbeat workers, and overdue workers return `shouldStop = true`.
44
47
 
45
48
  Recommended upgrade checklist:
49
+
46
50
  1. Upgrade the package to `1.0.0`.
47
51
  2. Regenerate Convex bindings in the consumer app.
48
52
  3. Update any consumer-side status handling for `workers.status`.
@@ -72,6 +76,7 @@ export default crons;
72
76
  Version `2.0.0` introduces a **conversation identity breaking change**.
73
77
 
74
78
  What changed:
79
+
75
80
  - `conversationId` is now required for worker snapshot upload and restore APIs.
76
81
  - `dataSnapshots.conversationId` is now mandatory in persisted storage.
77
82
  - Snapshot restore no longer falls back to the latest archive for `workspaceId + agentKey`.
@@ -79,6 +84,7 @@ What changed:
79
84
  - Telegram pairing no longer changes the conversation lineage used for chat history and snapshots.
80
85
 
81
86
  Important warnings:
87
+
82
88
  - This release is intentionally **not backward compatible** with legacy snapshots created without `conversationId`.
83
89
  - Existing non-prod agents, snapshots, bindings, and conversations created with the old model should be deleted before rollout.
84
90
  - If a worker runtime or consumer app still calls snapshot APIs without `conversationId`, the call will now fail at validation time.
@@ -86,6 +92,7 @@ Important warnings:
86
92
  - If you have custom dashboards, scripts, or admin tools that query snapshots only by `agentKey`, they must be updated to scope by `workspaceId + agentKey + conversationId`.
87
93
 
88
94
  Quick upgrade checklist:
95
+
89
96
  1. Delete legacy non-production agents, snapshots, conversations, and identity bindings created before this release.
90
97
  2. Upgrade the package to `2.0.0`.
91
98
  3. Regenerate Convex bindings in the consumer app.
@@ -95,6 +102,7 @@ Quick upgrade checklist:
95
102
  7. Smoke-test one manual user-agent flow, one Telegram-paired flow, and one worker snapshot restore flow before wider rollout.
96
103
 
97
104
  Recommended release notes to communicate to consumers:
105
+
98
106
  - treat this as a major upgrade, not a safe drop-in patch;
99
107
  - start from a clean non-prod environment;
100
108
  - roll out workers and consumer app together;
@@ -107,11 +115,13 @@ Recommended release notes to communicate to consumers:
107
115
  Starting with this release, the component also exposes an additive set of **user-facing aggregate APIs** for building pages like `MyAgent` and `MyAgentNew` without reconstructing state in the consumer app.
108
116
 
109
117
  What stays in the consumer app:
118
+
110
119
  - naming policy for agents and Telegram usernames
111
120
  - product-specific onboarding copy
112
121
  - cron presets or local `agentSettings`
113
122
 
114
123
  What is now exposed directly by the component:
124
+
115
125
  - user agent overview and active/history lookup
116
126
  - onboarding and pairing state
117
127
  - conversation view and queue items for a user agent
@@ -119,6 +129,7 @@ What is now exposed directly by the component:
119
129
  - user-centric snapshot listing and latest snapshot lookup
120
130
 
121
131
  Core APIs added for this pattern:
132
+
122
133
  - `listUserAgents`
123
134
  - `getUserAgent`
124
135
  - `getActiveUserAgent`
@@ -187,6 +198,7 @@ when you pass values inline from the UI, but automatic paths (enqueue + cron) re
187
198
  these stored secrets.
188
199
 
189
200
  If one is missing, reconcile fails with errors like:
201
+
190
202
  - `Missing Convex URL. Import an active 'convex.url' secret or pass convexUrl explicitly.`
191
203
  - `Missing Fly API token. Import an active 'fly.apiToken' secret or pass flyApiToken explicitly.`
192
204
 
@@ -205,6 +217,7 @@ npx convex run example:importSecret '{
205
217
  ```
206
218
 
207
219
  Important URL mapping:
220
+
208
221
  - Fly worker environment variable `CONVEX_URL` must use the `.convex.cloud` URL.
209
222
  - Component secret `convex.url` must use the `.convex.site` URL (used by component workflows and webhook-facing integration paths).
210
223
 
@@ -247,6 +260,7 @@ export const enqueueTelegramMessage = mutation({
247
260
  ```
248
261
 
249
262
  After enqueue, a **queue processor runtime** must process the queue by calling:
263
+
250
264
  - `components.agentFactory.lib.claim`
251
265
  - `components.agentFactory.lib.getHydrationBundle`
252
266
  - `components.agentFactory.lib.heartbeat`
@@ -257,6 +271,7 @@ explicitly alongside `workspaceId` and `agentKey`; the component no longer suppo
257
271
  fallbacks that select the latest archive for an agent without matching the conversation.
258
272
 
259
273
  Worker autoscaling reconcile now follows a hybrid model:
274
+
260
275
  - `enqueue` schedules an immediate async reconcile trigger (`runAfter(0, ...)`)
261
276
  - a periodic cron fallback is still recommended to recover from missed triggers
262
277
  - desired worker count is conversation-aware, so multiple queued messages on the same `conversationId` do not over-scale worker spawn
@@ -287,6 +302,89 @@ export default crons;
287
302
 
288
303
  This cron is a safety net. The primary path remains enqueue-triggered reconcile.
289
304
 
305
+ ### Component Fly cleanup for billing protection
306
+
307
+ The package now supports a dedicated Fly cleanup action in the component itself. The intent is to
308
+ protect the consumer's billing by giving every integration the same tested cleanup path instead of
309
+ reimplementing destructive Fly logic in each consumer app.
310
+
311
+ The public component action is exposed as:
312
+
313
+ - `components.agentFactory.lib.runFlyCleanup`
314
+
315
+ What the action does:
316
+
317
+ - resolves the target Fly app from `providerRuntimeConfig` unless the caller passes an explicit override
318
+ - reads `fly.apiToken` from the component secret store unless the caller passes an explicit override
319
+ - inventories machines and destroys them per machine ID
320
+ - verifies machine count again
321
+ - inventories volumes and destroys them per volume ID
322
+ - verifies volume count again and returns a report with counts, warnings, and errors
323
+
324
+ What the consumer still owns:
325
+
326
+ - choosing whether to run this policy at all
327
+ - choosing the schedule window
328
+ - optionally exposing an admin-only helper/wrapper
329
+
330
+ Thin wrapper through `exposeApi(...)`:
331
+
332
+ ```ts
333
+ const {
334
+ startWorkers,
335
+ runFlyCleanup,
336
+ } = exposeApi(components.agentFactory, {
337
+ providerConfig: EXAMPLE_PROVIDER_CONFIG,
338
+ auth: async (ctx, operation) => {
339
+ const userId = await getAuthUserId(ctx);
340
+ if (userId === null && operation.type === "write") {
341
+ throw new Error("Unauthorized");
342
+ }
343
+ return userId;
344
+ },
345
+ });
346
+ ```
347
+
348
+ This keeps the consumer surface small: the wrapper only forwards auth and the local
349
+ `providerConfig`, while the package owns the actual Fly inventory, destroy, and verification logic.
350
+
351
+ Minimal consumer cron wiring:
352
+
353
+ ```ts
354
+ import { cronJobs } from "convex/server";
355
+ import { api } from "./_generated/api";
356
+
357
+ const crons = cronJobs();
358
+
359
+ crons.interval(
360
+ "agent-factory reconcile workers fallback",
361
+ { minutes: 5 },
362
+ api.example.startWorkers,
363
+ {},
364
+ );
365
+
366
+ crons.cron(
367
+ "agent-factory nightly fly cleanup",
368
+ "0 3 * * *",
369
+ api.example.runFlyCleanup,
370
+ {},
371
+ );
372
+
373
+ export default crons;
374
+ ```
375
+
376
+ Recommended consumer helper:
377
+
378
+ - keep it thin
379
+ - call `components.agentFactory.lib.runFlyCleanup` directly, or expose `runFlyCleanup` through `exposeApi(...)`
380
+ - avoid duplicating inventory, destroy sequencing, or verification logic in the consumer
381
+
382
+ Operational prerequisites:
383
+
384
+ - `fly.apiToken` must be present as an active component secret
385
+ - the effective `providerRuntimeConfig` must point at the Fly app you want to protect
386
+ - the cleanup remains intentionally destructive, so it should only target a single explicit app per deployment
387
+
290
388
  ### Agent pushing schedule (hourly dispatcher)
291
389
 
292
390
  For agent pushing, the recommended scheduler is an hourly cron that dispatches due jobs:
@@ -308,6 +406,7 @@ export default crons;
308
406
  ```
309
407
 
310
408
  Important product constraint:
409
+
311
410
  - job configuration supports only fixed schedule slots (`HH:mm`, plus weekday/day-of-month)
312
411
  - minute-based recurrence ("every N minutes") is intentionally not supported
313
412
 
@@ -318,16 +417,19 @@ Admin broadcast is also supported through `sendBroadcastToAllActiveAgents`, whic
318
417
  The model/provider is controlled by Fly worker environment variables (for example `OPENCLAW_AGENT_MODEL`, `MOONSHOT_API_KEY`, `OPENAI_API_KEY`) and applied at runtime by the worker image bootstrap.
319
418
 
320
419
  Why:
420
+
321
421
  - keeps model routing as infrastructure/runtime concern
322
422
  - avoids per-agent schema coupling to a specific LLM field
323
423
  - lets you switch model/provider with a Fly deploy or env change only
324
424
 
325
425
  Practical notes:
426
+
326
427
  - set model/provider env on the Fly app (`fly secrets set` / `[env]` in `fly.toml`)
327
428
  - keep `agentProfiles` focused on identity, bridge configuration, and secrets references
328
429
  - worker image tag stays centralized in `src/component/config.ts` (`DEFAULT_WORKER_IMAGE`)
329
430
 
330
431
  If you use `exposeApi(...)`, the worker contract is available directly on the consumer API surface:
432
+
331
433
  - `workerClaim`
332
434
  - `workerHydrationBundle`
333
435
  - `workerHeartbeat`
@@ -339,6 +441,7 @@ If you use `exposeApi(...)`, the worker contract is available directly on the co
339
441
  `agent-factory` does **not** execute `agent-bridge` tools.
340
442
 
341
443
  Its role stops at:
444
+
342
445
  - storing bridge settings on the agent profile
343
446
  - resolving bridge secrets from the component secret store
344
447
  - exposing `bridgeRuntimeConfig` in hydration
@@ -346,7 +449,7 @@ Its role stops at:
346
449
 
347
450
  Tool execution belongs to the OpenClaw worker runtime / worker image, not to `agent-factory`.
348
451
 
349
- 1) Configure an agent profile with bridge settings:
452
+ 1. Configure an agent profile with bridge settings:
350
453
 
351
454
  ```ts
352
455
  await ctx.runMutation(components.agentFactory.lib.configureAgent, {
@@ -363,7 +466,7 @@ await ctx.runMutation(components.agentFactory.lib.configureAgent, {
363
466
  });
364
467
  ```
365
468
 
366
- 2) Import bridge service key in component secrets:
469
+ 1. Import bridge service key in component secrets:
367
470
 
368
471
  ```sh
369
472
  npx convex run example:importSecret '{
@@ -373,6 +476,7 @@ npx convex run example:importSecret '{
373
476
  ```
374
477
 
375
478
  Naming convention supported by hydration resolver:
479
+
376
480
  - per-agent service key: `agent-bridge.serviceKey.<agentKey>` (recommended)
377
481
  - global service key fallback: `agent-bridge.serviceKey`
378
482
  - optional profile override: `bridgeConfig.serviceKeySecretRef`
@@ -399,6 +503,7 @@ Do **not** treat `agent-factory` as the place where `bridge.<functionKey>` tool
399
503
  If your OpenClaw agents use `agent-bridge`, that execution flow must live in the worker runtime itself.
400
504
 
401
505
  Fallback env (worker-side only, used when hydration misses values):
506
+
402
507
  - `OPENCLAW_AGENT_BRIDGE_BASE_URL` or `AGENT_BRIDGE_BASE_URL`
403
508
  - `OPENCLAW_SERVICE_ID` or `AGENT_BRIDGE_SERVICE_ID`
404
509
  - `OPENCLAW_SERVICE_KEY` or `AGENT_BRIDGE_SERVICE_KEY`
@@ -408,12 +513,14 @@ Fallback env (worker-side only, used when hydration misses values):
408
513
 
409
514
  When `agent-factory` is used together with `agent-bridge`, spawned workers may need these environment variables available in their runtime:
410
515
 
411
- | Env var | Component secret ref | Purpose |
412
- |---------|----------------------|---------|
413
- | `OPENCLAW_SERVICE_ID` | `agent-bridge.serviceId` | Service identity for bridge auth |
414
- | `OPENCLAW_SERVICE_KEY` | `agent-bridge.serviceKey` | Service key for bridge auth |
516
+
517
+ | Env var | Component secret ref | Purpose |
518
+ | -------------------------------- | ---------------------------------- | -------------------------------------------------- |
519
+ | `OPENCLAW_SERVICE_ID` | `agent-bridge.serviceId` | Service identity for bridge auth |
520
+ | `OPENCLAW_SERVICE_KEY` | `agent-bridge.serviceKey` | Service key for bridge auth |
415
521
  | `OPENCLAW_LINKING_SHARED_SECRET` | `agent-bridge.linkingSharedSecret` | Shared secret for `execute-on-behalf` user linking |
416
522
 
523
+
417
524
  The scheduler forwards these from the component secret store into each machine's env at spawn time. These values prepare the worker runtime for bridge usage; they do not implement bridge tool execution inside `agent-factory`.
418
525
 
419
526
  Import all three into the component secret store:
@@ -445,6 +552,7 @@ export default http;
445
552
  ```
446
553
 
447
554
  This exposes:
555
+
448
556
  - `POST /agent-factory/telegram/webhook` -> enqueue-only (no business processing)
449
557
 
450
558
  Important: the webhook/router only receives ingress and enqueues.
@@ -472,6 +580,7 @@ await configureTelegramWebhook({
472
580
  ```
473
581
 
474
582
  This API:
583
+
475
584
  - loads bot token from component secrets (active secret for `secretRef`)
476
585
  - calls Telegram `setWebhook`
477
586
  - verifies status with `getWebhookInfo`
@@ -483,31 +592,35 @@ Typical one-time pairing flow:
483
592
 
484
593
  1. Configure webhook and verify `isReady === true` via `configureTelegramWebhook`.
485
594
  2. Your app authenticates the user and creates a one-time pairing code via
486
- `createPairingCode`.
595
+ `createPairingCode`.
487
596
  3. User opens Telegram deep-link (`/start <pairingCode>`).
488
597
  4. `registerRoutes(...)` webhook consumes the pairing code and performs
489
- `bindUserAgent` automatically with `source: "telegram_pairing"` and
598
+ `bindUserAgent` automatically with `source: "telegram_pairing"` and
490
599
  Telegram ids from the update.
491
600
  5. Webhook ingress then resolves the binding internally and enqueues with the mapped
492
- `agentKey`.
601
+ `agentKey`.
493
602
 
494
603
  Available pairing APIs (via `exposeApi(...)`):
604
+
495
605
  - `createPairingCode`
496
606
  - `getPairingCodeStatus`
497
607
  - `configureTelegramWebhook`
498
608
 
499
609
  Telegram token storage (multi-tenant):
610
+
500
611
  - store tenant token in component secrets with an agent-scoped ref (for example `telegram.botToken.<agentKey>`)
501
612
  - include that ref in `agentProfiles.secretsRef`
502
613
  - worker gets resolved plaintext from hydration bundle (`telegramBotToken`) at runtime
503
614
  - do not use a single global `TELEGRAM_BOT_TOKEN` on Fly app
504
615
 
505
616
  `registerRoutes(...)` supports this behavior with:
617
+
506
618
  - `resolveAgentKeyFromBinding` (default `true`)
507
619
  - `fallbackAgentKey` (default `"default"`)
508
620
  - `requireBindingForTelegram` (default `false`, when `true` rejects unbound users)
509
621
 
510
622
  Special handling for `/start`:
623
+
511
624
  - `/start <pairingCode>` attempts pairing consumption and does not enqueue the command.
512
625
  - invalid `/start` payload returns `200` with pairing error details to avoid Telegram retries.
513
626
 
@@ -530,9 +643,12 @@ flowchart LR
530
643
  flyWorkers --> claimLoop
531
644
  ```
532
645
 
646
+
647
+
533
648
  ## Data model
534
649
 
535
650
  Core tables:
651
+
536
652
  - `agentProfiles`
537
653
  - `conversations`
538
654
  - `messageQueue`
@@ -540,6 +656,7 @@ Core tables:
540
656
  - `secrets`
541
657
 
542
658
  Hydration/runtime tables:
659
+
543
660
  - `conversationHydrationCache`
544
661
  - `dataSnapshots`
545
662
 
@@ -558,12 +675,14 @@ Hydration/runtime tables:
558
675
 
559
676
  ## OpenClaw workspace persistence
560
677
 
561
- | OpenClaw source | Persistence layer |
562
- |---|---|
563
- | `AGENTS.md`, `SOUL.md`, `USER.md`, `IDENTITY.md`, `HEARTBEAT.md`, `TOOLS.md` | worker filesystem backup (`/data/workspace`) |
564
- | `memory/YYYY-MM-DD.md`, `MEMORY.md` | worker filesystem backup (`/data/workspace`) |
565
- | Skills and related assets | bundled directly in worker image (`openclaw-okr-image`) |
566
- | Conversation-specific deltas | `conversationHydrationCache` |
678
+
679
+ | OpenClaw source | Persistence layer |
680
+ | ---------------------------------------------------------------------------- | ------------------------------------------------------- |
681
+ | `AGENTS.md`, `SOUL.md`, `USER.md`, `IDENTITY.md`, `HEARTBEAT.md`, `TOOLS.md` | worker filesystem backup (`/data/workspace`) |
682
+ | `memory/YYYY-MM-DD.md`, `MEMORY.md` | worker filesystem backup (`/data/workspace`) |
683
+ | Skills and related assets | bundled directly in worker image (`openclaw-okr-image`) |
684
+ | Conversation-specific deltas | `conversationHydrationCache` |
685
+
567
686
 
568
687
  ## Failure model
569
688
 
@@ -576,6 +695,7 @@ Hydration/runtime tables:
576
695
  ## Config-first
577
696
 
578
697
  `src/component/config.ts` defines type-safe policies:
698
+
579
699
  - queue policy
580
700
  - retry policy
581
701
  - lease policy
@@ -585,6 +705,7 @@ Hydration/runtime tables:
585
705
  ## Fly.io provider notes
586
706
 
587
707
  The current provider implementation uses Fly Machines API endpoints for:
708
+
588
709
  - create machine
589
710
  - list machines
590
711
  - cordon machine
@@ -596,11 +717,13 @@ Do **not** share the same Fly app across multiple Convex backends/components tha
596
717
  their own queue polling/reconcile loop.
597
718
 
598
719
  Why this is required:
720
+
599
721
  - workers in a Fly app share the same control plane (create/list/stop),
600
722
  - each backend computes desired capacity from its own queue state only,
601
723
  - mixed backends in one app can stop each other's machines or produce unpredictable polling behavior.
602
724
 
603
725
  Recommended pattern:
726
+
604
727
  - one Convex backend -> one dedicated Fly app (for example `agent-factory-workers-prod`)
605
728
  - another Convex backend -> another dedicated Fly app (for example `agent-factory-workers-staging`)
606
729
  - keep `providerConfig.appName` and worker image registry aligned per backend/environment.
@@ -608,27 +731,32 @@ Recommended pattern:
608
731
  ### Worker image setup (required first step for custom skills)
609
732
 
610
733
  Any new skill you want inside OpenClaw agents must be added to the worker image source repo:
611
- - https://github.com/okrlinkhub/openclaw-okr-image
734
+
735
+ - [https://github.com/okrlinkhub/openclaw-okr-image](https://github.com/okrlinkhub/openclaw-okr-image)
612
736
 
613
737
  Fork this repository to maintain your own image with your custom skills/assets.
614
738
 
615
739
  For `globalSkills` managed by this component, the recommended runtime pattern is different:
740
+
616
741
  - store the source of truth in component tables `globalSkills`, `globalSkillVersions`, `globalSkillReleases`
617
742
  - treat each skill as a mini filesystem bundle (`files[]`), not as a single `sourceJs` blob
618
743
  - expose them through `getWorkerGlobalSkillsManifest`
619
744
  - let the worker image materialize them into `OPENCLAW_SKILLS_DIR` during prestart, before the OpenClaw gateway boots
620
745
 
621
746
  The manifest now carries an explicit on-disk layout contract for OpenClaw workspace skills:
747
+
622
748
  - `layoutVersion = openclaw-workspace-skill-v1`
623
749
  - `skillDirName`
624
750
  - `files[]` with `path`, `content`, `sha256`
625
751
 
626
752
  Breaking change in `3.0.0`:
753
+
627
754
  - `sourceJs` has been removed from the global skill model
628
755
  - existing legacy global skill rows must be deleted before moving to `3.0.0`
629
756
  - existing legacy skills must be republished as full bundles
630
757
 
631
758
  Bundle contract for `3.0.0`:
759
+
632
760
  - required user files:
633
761
  - `SKILL.md`
634
762
  - `scripts/index.mjs` or `scripts/index.cjs` (must match `moduleFormat`)
@@ -642,6 +770,7 @@ Extract a `Bundle files JSON` payload from an existing OpenClaw skill directory:
642
770
  Use this when you already have a correctly materialized skill inside an OpenClaw workspace and want to republish it as a `3.0.0` global skill bundle.
643
771
 
644
772
  Important:
773
+
645
774
  - run the command against the skill directory itself (for example `/path/to/workspace/skills/agent-bridge`)
646
775
  - the command automatically excludes `.af-global-skill.json`
647
776
  - hidden files other than `.af-global-skill.json` are excluded by default
@@ -720,11 +849,13 @@ EOF
720
849
  ```
721
850
 
722
851
  The resulting JSON should contain files like:
852
+
723
853
  - `SKILL.md`
724
854
  - `scripts/index.mjs`
725
855
  - any extra files such as `scripts/agent-bridge-cli.mjs`
726
856
 
727
857
  Recommended worker bootstrap order:
858
+
728
859
  1. restore snapshot into `/data`
729
860
  2. fetch `workerGlobalSkillsManifest`
730
861
  3. verify checksums and materialize skills atomically into `OPENCLAW_SKILLS_DIR`
@@ -733,11 +864,12 @@ Recommended worker bootstrap order:
733
864
  This avoids the historical race where the gateway could start before restored or DB-backed skills were present on disk.
734
865
 
735
866
  First required flow:
736
- 1) Take the image repo (fork/clone your own `openclaw-okr-image`).
737
- 2) Build and deploy it on your own Fly app.
738
- - Recommended build mode: remote Fly builder, `depot` disabled, `--remote-only`.
739
- 3) Use the published image as reference in `src/component/config.ts` (`DEFAULT_WORKER_IMAGE` is the source of truth).
740
- 4) Repeat the same process for every runtime/skills update.
867
+
868
+ 1. Take the image repo (fork/clone your own `openclaw-okr-image`).
869
+ 2. Build and deploy it on your own Fly app.
870
+ - Recommended build mode: remote Fly builder, `depot` disabled, `--remote-only`.
871
+ 3. Use the published image as reference in `src/component/config.ts` (`DEFAULT_WORKER_IMAGE` is the source of truth).
872
+ 4. Repeat the same process for every runtime/skills update.
741
873
 
742
874
  **Enterprise security model**: The worker image enforces a security policy where only skills explicitly included by the image maintainer are installed by default. Any other skills that may be present in the workspace are automatically removed on each worker startup. This ensures that only approved, vetted skills from the image source can execute within your OpenClaw agents.
743
875
 
@@ -745,20 +877,21 @@ First required flow:
745
877
 
746
878
  When you update the worker runtime (for example in `openclaw-okr-image/worker.mjs`), use this flow to publish and roll out safely.
747
879
 
748
- 1) Deploy with remote Fly builder (explicitly disabling Depot):
880
+ 1. Deploy with remote Fly builder (explicitly disabling Depot):
749
881
 
750
882
  ```sh
751
883
  cd /path/to/openclaw-okr-image
752
884
  fly deploy --remote-only --depot=false --yes
753
885
  ```
754
886
 
755
- 2) If deployment fails with `CONVEX_URL not set`, set the secret and retry:
887
+ 1. If deployment fails with `CONVEX_URL not set`, set the secret and retry:
756
888
 
757
889
  ```sh
758
890
  fly secrets set CONVEX_URL="https://<your-convex-deployment>.convex.cloud" -a <your-fly-worker-app>
759
891
  ```
760
892
 
761
- 3) Capture the new image tag from deploy output (for example
893
+ 1. Capture the new image tag from deploy output (for example
894
+
762
895
  `registry.fly.io/<your-fly-worker-app>:deployment-XXXXXXXXXXXX`), then update
763
896
  `src/component/config.ts` in this repo:
764
897
 
@@ -767,30 +900,34 @@ export const DEFAULT_WORKER_IMAGE =
767
900
  "registry.fly.io/<your-fly-worker-app>:deployment-XXXXXXXXXXXX";
768
901
  ```
769
902
 
770
- 4) Verify rollout:
903
+ 1. Verify rollout:
771
904
 
772
905
  ```sh
773
906
  fly status -a <your-fly-worker-app>
774
907
  fly logs -a <your-fly-worker-app> --no-tail
775
908
  ```
776
909
 
777
- 5) (Recommended) Commit the `DEFAULT_WORKER_IMAGE` update so scheduler-driven
910
+ 1. (Recommended) Commit the `DEFAULT_WORKER_IMAGE` update so scheduler-driven
911
+
778
912
  spawns use the exact image that was just deployed.
779
913
 
780
914
  Recommended runtime split:
915
+
781
916
  - Consumer app (Next.js/Vercel): webhook ingress + enqueue only
782
917
  - Fly worker app: claim/heartbeat/complete/fail loop
783
918
 
784
919
  Anti-pattern to avoid:
920
+
785
921
  - Telegram webhook -> Fly worker HTTP endpoint
786
922
  - Reason: workers are batch processors, may be scaled to zero, and should not be used as public ingress.
787
923
  - Global Fly env `TELEGRAM_BOT_TOKEN` for all tenants
788
924
  - Reason: breaks multi-tenant isolation and forces shared bot credentials.
789
925
 
790
926
  References:
791
- - https://docs.machines.dev/
792
- - https://fly.io/docs/machines/api/machines-resource/
793
- - https://docs.convex.dev/components/authoring
927
+
928
+ - [https://docs.machines.dev/](https://docs.machines.dev/)
929
+ - [https://fly.io/docs/machines/api/machines-resource/](https://fly.io/docs/machines/api/machines-resource/)
930
+ - [https://docs.convex.dev/components/authoring](https://docs.convex.dev/components/authoring)
794
931
 
795
932
  ## Development
796
933
 
@@ -799,4 +936,21 @@ npm i
799
936
  npm run dev
800
937
  ```
801
938
 
802
- Upgrade note for older releases: version `0.2.14` makes `agentProfiles.providerUserId`, `agentProfiles.soulMd`, `agentProfiles.clientMd`, and `agentProfiles.skills` optional only to let you clean them safely. Before upgrading to version `0.2.15`, where those fields are expected to be removed from the schema, install `0.2.14`, run `components.agentFactory.lib.clearDeprecatedAgentProfileFields` from Convex Dashboard, and make sure a second run returns `updated = 0`. This avoids schema validation issues caused by leftover stored values during the upgrade to `0.2.15`.
939
+ ### Release validation note
940
+
941
+ For npm releases cut from `develop`, known failures in the `example` Vitest suite are currently treated as non-blocking release noise.
942
+
943
+ What we still verify before publishing:
944
+
945
+ - `npm run lint`
946
+ - `npm run typecheck`
947
+ - `npm pack --dry-run`
948
+ - focused package tests when a change touches runtime behavior outside the example app
949
+
950
+ What we intentionally do not require for publish:
951
+
952
+ - a fully green `npm test` run when the remaining failures are limited to the `example` app test surface and do not affect the published package itself
953
+
954
+ This choice was applied for the `3.0.2` npm release after confirming the package checks above passed and the remaining instability was in the example-only test flow.
955
+
956
+ Upgrade note for older releases: version `0.2.14` makes `agentProfiles.providerUserId`, `agentProfiles.soulMd`, `agentProfiles.clientMd`, and `agentProfiles.skills` optional only to let you clean them safely. Before upgrading to version `0.2.15`, where those fields are expected to be removed from the schema, install `0.2.14`, run `components.agentFactory.lib.clearDeprecatedAgentProfileFields` from Convex Dashboard, and make sure a second run returns `updated = 0`. This avoids schema validation issues caused by leftover stored values during the upgrade to `0.2.15`.
@@ -62,7 +62,6 @@ export declare function exposeApi(component: ComponentApi, options: {
62
62
  limit?: number | undefined;
63
63
  }, Promise<any>>;
64
64
  enqueue: import("convex/server").RegisteredMutation<"public", {
65
- metadata?: Record<string, string> | undefined;
66
65
  providerConfig?: {
67
66
  appName: string;
68
67
  kind: "fly" | "runpod" | "ecs";
@@ -73,13 +72,14 @@ export declare function exposeApi(component: ComponentApi, options: {
73
72
  volumePath: string;
74
73
  volumeSizeGb: number;
75
74
  } | undefined;
75
+ metadata?: Record<string, string> | undefined;
76
76
  priority?: number | undefined;
77
77
  attachments?: {
78
78
  sizeBytes?: number | undefined;
79
79
  fileName?: string | undefined;
80
80
  mimeType?: string | undefined;
81
- status: "expired" | "ready";
82
81
  storageId: string;
82
+ status: "expired" | "ready";
83
83
  kind: "photo" | "video" | "audio" | "voice" | "document";
84
84
  telegramFileId: string;
85
85
  expiresAt: number;
@@ -239,8 +239,8 @@ export declare function exposeApi(component: ComponentApi, options: {
239
239
  sizeBytes: number;
240
240
  }, Promise<any>>;
241
241
  workerFailSnapshotUpload: import("convex/server").RegisteredMutation<"public", {
242
- error: string;
243
242
  workerId: string;
243
+ error: string;
244
244
  snapshotId: string;
245
245
  }, Promise<any>>;
246
246
  workerLatestSnapshotForRestore: import("convex/server").RegisteredQuery<"public", {
@@ -272,14 +272,14 @@ export declare function exposeApi(component: ComponentApi, options: {
272
272
  }[];
273
273
  }, Promise<any>>;
274
274
  globalSkillsList: import("convex/server").RegisteredQuery<"public", {
275
- status?: "active" | "disabled" | undefined;
276
275
  limit?: number | undefined;
277
276
  releaseChannel?: "stable" | "canary" | undefined;
277
+ status?: "active" | "disabled" | undefined;
278
278
  }, Promise<any>>;
279
279
  globalSkillsSetStatus: import("convex/server").RegisteredMutation<"public", {
280
280
  actor?: string | undefined;
281
- status: "active" | "disabled";
282
281
  slug: string;
282
+ status: "active" | "disabled";
283
283
  }, Promise<any>>;
284
284
  globalSkillsDelete: import("convex/server").RegisteredMutation<"public", {
285
285
  slug: string;
@@ -302,8 +302,8 @@ export declare function exposeApi(component: ComponentApi, options: {
302
302
  version: null | number;
303
303
  }[]>>;
304
304
  startWorkers: import("convex/server").RegisteredAction<"public", {
305
- workspaceId?: string | undefined;
306
305
  flyApiToken?: string | undefined;
306
+ workspaceId?: string | undefined;
307
307
  convexUrl?: string | undefined;
308
308
  scalingPolicy?: {
309
309
  maxWorkers: number;
@@ -325,6 +325,11 @@ export declare function exposeApi(component: ComponentApi, options: {
325
325
  appName: string;
326
326
  volumeId: string;
327
327
  }, Promise<any>>;
328
+ runFlyCleanup: import("convex/server").RegisteredAction<"public", {
329
+ flyApiToken?: string | undefined;
330
+ machineConcurrency?: number | undefined;
331
+ volumeConcurrency?: number | undefined;
332
+ }, Promise<any>>;
328
333
  recoverQueue: import("convex/server").RegisteredAction<"public", {
329
334
  nowMs?: number | undefined;
330
335
  workspaceId?: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAC;AAmFhC,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,gBAAgB,EAChB,0BAA0B,EAC1B,0BAA0B,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,GACjC,MAAM,aAAa,CAAC;AAErB,wBAAgB,SAAS,CACvB,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE;IACP,IAAI,EAAE,CACJ,GAAG,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,EACnB,SAAS,EACL;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAChB;QACE,IAAI,EAAE,OAAO,CAAC;QACd,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,KACF,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAo3CulqB,CAAC;2BAAmC,CAAC;;wBAAiE,CAAC;;wBAAwG,CAAC;yBAAiC,CAAC;;;;;6BAAyK,CAAC;;oBAA+D,CAAC;;;yBAAqH,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAA28N,CAAC;2BAAmC,CAAC;;wBAAiE,CAAC;;wBAAwG,CAAC;yBAAiC,CAAC;;;;;6BAAyK,CAAC;;oBAA+D,CAAC;;;yBAAqH,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA7Txs6B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,YAAY,EACvB,EACE,UAA6B,EAC7B,eAAe,EACf,0BAAiC,EACjC,gBAA4B,EAC5B,yBAAiC,EACjC,cAAc,GACf,GAAE;IACD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC;IAC9C,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,cAAc,CAAC,EAAE,cAAc,CAAC;CAC5B,QAmLP"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAC;AAmFhC,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,gBAAgB,EAChB,0BAA0B,EAC1B,0BAA0B,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,GACjC,MAAM,aAAa,CAAC;AAErB,wBAAgB,SAAS,CACvB,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE;IACP,IAAI,EAAE,CACJ,GAAG,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,EACnB,SAAS,EACL;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAChB;QACE,IAAI,EAAE,OAAO,CAAC;QACd,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,KACF,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAo4CojtB,CAAC;2BAAmC,CAAC;;wBAAiE,CAAC;;wBAAwG,CAAC;yBAAiC,CAAC;;;;;6BAAyK,CAAC;;oBAA+D,CAAC;;;yBAAqH,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAA28N,CAAC;2BAAmC,CAAC;;wBAAiE,CAAC;;wBAAwG,CAAC;yBAAiC,CAAC;;;;;6BAAyK,CAAC;;oBAA+D,CAAC;;;yBAAqH,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA7Trq9B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,YAAY,EACvB,EACE,UAA6B,EAC7B,eAAe,EACf,0BAAiC,EACjC,gBAA4B,EAC5B,yBAAiC,EACjC,cAAc,GACf,GAAE;IACD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC;IAC9C,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,cAAc,CAAC,EAAE,cAAc,CAAC;CAC5B,QAmLP"}