@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.
- package/README.md +185 -31
- package/dist/client/index.d.ts +11 -6
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +16 -0
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +2 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +60 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/flyCleanup.d.ts +32 -0
- package/dist/component/flyCleanup.d.ts.map +1 -0
- package/dist/component/flyCleanup.js +272 -0
- package/dist/component/flyCleanup.js.map +1 -0
- package/dist/component/lib.d.ts +1 -0
- package/dist/component/lib.d.ts.map +1 -1
- package/dist/component/lib.js +1 -0
- package/dist/component/lib.js.map +1 -1
- package/dist/component/providers/fly.d.ts +23 -2
- package/dist/component/providers/fly.d.ts.map +1 -1
- package/dist/component/providers/fly.js +15 -3
- package/dist/component/providers/fly.js.map +1 -1
- package/dist/component/pushing.d.ts +4 -4
- package/dist/component/queue.d.ts +32 -16
- package/dist/component/queue.d.ts.map +1 -1
- package/dist/component/queue.js +44 -2
- package/dist/component/queue.js.map +1 -1
- package/dist/component/scheduler.d.ts +8 -8
- package/dist/component/scheduler.d.ts.map +1 -1
- package/dist/component/scheduler.js +59 -12
- package/dist/component/scheduler.js.map +1 -1
- package/dist/component/schema.d.ts +30 -28
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +1 -0
- package/dist/component/schema.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +16 -0
- package/src/component/_generated/api.ts +2 -0
- package/src/component/_generated/component.ts +72 -0
- package/src/component/flyCleanup.ts +386 -0
- package/src/component/lib.test.ts +280 -4
- package/src/component/lib.ts +1 -0
- package/src/component/providers/fly.ts +39 -5
- package/src/component/queue.ts +55 -2
- package/src/component/scheduler.ts +100 -16
- package/src/component/schema.ts +1 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Convex Agent Factory
|
|
2
2
|
|
|
3
|
-
[
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
|
414
|
-
| `
|
|
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
|
-
|
|
595
|
+
`createPairingCode`.
|
|
487
596
|
3. User opens Telegram deep-link (`/start <pairingCode>`).
|
|
488
597
|
4. `registerRoutes(...)` webhook consumes the pairing code and performs
|
|
489
|
-
|
|
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
|
-
|
|
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
|
-
|
|
562
|
-
|
|
563
|
-
|
|
|
564
|
-
| `
|
|
565
|
-
|
|
|
566
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
792
|
-
- https://
|
|
793
|
-
- https://docs.
|
|
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
|
-
|
|
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`.
|
package/dist/client/index.d.ts
CHANGED
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
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"}
|