@okrlinkhub/agent-factory 0.1.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/LICENSE +201 -0
- package/README.md +345 -0
- package/dist/client/_generated/_ignore.d.ts +1 -0
- package/dist/client/_generated/_ignore.d.ts.map +1 -0
- package/dist/client/_generated/_ignore.js +3 -0
- package/dist/client/_generated/_ignore.js.map +1 -0
- package/dist/client/index.d.ts +320 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +509 -0
- package/dist/client/index.js.map +1 -0
- package/dist/component/_generated/api.d.ts +44 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +694 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +121 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +78 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/config.d.ts +260 -0
- package/dist/component/config.d.ts.map +1 -0
- package/dist/component/config.js +154 -0
- package/dist/component/config.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +3 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/identity.d.ts +98 -0
- package/dist/component/identity.d.ts.map +1 -0
- package/dist/component/identity.js +371 -0
- package/dist/component/identity.js.map +1 -0
- package/dist/component/lib.d.ts +4 -0
- package/dist/component/lib.d.ts.map +1 -0
- package/dist/component/lib.js +4 -0
- package/dist/component/lib.js.map +1 -0
- package/dist/component/providers/fly.d.ts +39 -0
- package/dist/component/providers/fly.d.ts.map +1 -0
- package/dist/component/providers/fly.js +145 -0
- package/dist/component/providers/fly.js.map +1 -0
- package/dist/component/queue.d.ts +294 -0
- package/dist/component/queue.d.ts.map +1 -0
- package/dist/component/queue.js +1278 -0
- package/dist/component/queue.js.map +1 -0
- package/dist/component/scheduler.d.ts +89 -0
- package/dist/component/scheduler.d.ts.map +1 -0
- package/dist/component/scheduler.js +275 -0
- package/dist/component/scheduler.js.map +1 -0
- package/dist/component/schema.d.ts +518 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +243 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +6 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +102 -0
- package/src/client/_generated/_ignore.ts +1 -0
- package/src/client/index.test.ts +48 -0
- package/src/client/index.ts +586 -0
- package/src/client/setup.test.ts +26 -0
- package/src/component/_generated/api.ts +60 -0
- package/src/component/_generated/component.ts +902 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +156 -0
- package/src/component/config.ts +236 -0
- package/src/component/convex.config.ts +3 -0
- package/src/component/identity.ts +450 -0
- package/src/component/lib.test.ts +319 -0
- package/src/component/lib.ts +30 -0
- package/src/component/providers/fly.ts +226 -0
- package/src/component/queue.ts +1544 -0
- package/src/component/scheduler.ts +362 -0
- package/src/component/schema.ts +336 -0
- package/src/component/setup.test.ts +11 -0
- package/src/react/index.ts +7 -0
- package/src/test.ts +18 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# Convex Agent Factory
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/@example%2Fagent-factory)
|
|
4
|
+
|
|
5
|
+
A Convex component for hydration-based orchestration of OpenClaw agents on a generic worker pool (Fly Machines first, provider abstraction built-in).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Create a `convex.config.ts` file in your app's `convex/` folder and install the
|
|
10
|
+
component by calling `use`:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
// convex/convex.config.ts
|
|
14
|
+
import { defineApp } from "convex/server";
|
|
15
|
+
import agentFactory from "@okrlinkhub/agent-factory/convex.config.js";
|
|
16
|
+
|
|
17
|
+
const app = defineApp();
|
|
18
|
+
app.use(agentFactory);
|
|
19
|
+
|
|
20
|
+
export default app;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### First required setup: mandatory secrets for every worker/agent
|
|
26
|
+
|
|
27
|
+
Before running worker autoscaling (enqueue trigger, cron, or manual reconcile), you must
|
|
28
|
+
store **both** secrets in the component secret store:
|
|
29
|
+
|
|
30
|
+
- `convex.url`
|
|
31
|
+
- `fly.apiToken`
|
|
32
|
+
|
|
33
|
+
Every spawned worker/agent needs these values at runtime. Manual "Start Workers" can work
|
|
34
|
+
when you pass values inline from the UI, but automatic paths (enqueue + cron) rely on
|
|
35
|
+
these stored secrets.
|
|
36
|
+
|
|
37
|
+
If one is missing, reconcile fails with errors like:
|
|
38
|
+
- `Missing Convex URL. Import an active 'convex.url' secret or pass convexUrl explicitly.`
|
|
39
|
+
- `Missing Fly API token. Import an active 'fly.apiToken' secret or pass flyApiToken explicitly.`
|
|
40
|
+
|
|
41
|
+
Set them once:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npx convex run example:importSecret '{
|
|
45
|
+
"secretRef": "convex.url",
|
|
46
|
+
"plaintextValue": "https://<your-convex-deployment>.convex.cloud"
|
|
47
|
+
}'
|
|
48
|
+
|
|
49
|
+
npx convex run example:importSecret '{
|
|
50
|
+
"secretRef": "fly.apiToken",
|
|
51
|
+
"plaintextValue": "fly_XXXXXXXXXXXXXXXX"
|
|
52
|
+
}'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Verify status:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
npx convex run example:secretStatus '{
|
|
59
|
+
"secretRefs": ["convex.url", "fly.apiToken", "telegram.botToken"]
|
|
60
|
+
}'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
In the example UI (`example/src/App.tsx`), this is shown as step
|
|
64
|
+
`0) Mandatory: configure convex.url secret`; make sure `fly.apiToken` is also imported
|
|
65
|
+
as an active component secret.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { components } from "./_generated/api";
|
|
69
|
+
import { mutation } from "./_generated/server";
|
|
70
|
+
import { v } from "convex/values";
|
|
71
|
+
|
|
72
|
+
export const enqueueTelegramMessage = mutation({
|
|
73
|
+
args: { text: v.string(), chatId: v.string() },
|
|
74
|
+
handler: async (ctx, args) => {
|
|
75
|
+
return await ctx.runMutation(components.agentFactory.lib.enqueue, {
|
|
76
|
+
conversationId: `telegram:${args.chatId}`,
|
|
77
|
+
agentKey: "default",
|
|
78
|
+
payload: {
|
|
79
|
+
provider: "telegram",
|
|
80
|
+
providerUserId: args.chatId,
|
|
81
|
+
messageText: args.text,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
After enqueue, a **queue processor runtime** must process the queue by calling:
|
|
89
|
+
- `components.agentFactory.lib.claim`
|
|
90
|
+
- `components.agentFactory.lib.getHydrationBundle`
|
|
91
|
+
- `components.agentFactory.lib.heartbeat`
|
|
92
|
+
- `components.agentFactory.lib.complete` or `components.agentFactory.lib.fail`
|
|
93
|
+
|
|
94
|
+
Worker autoscaling reconcile now follows a hybrid model:
|
|
95
|
+
- `enqueue` schedules an immediate async reconcile trigger (`runAfter(0, ...)`)
|
|
96
|
+
- a periodic cron fallback is still recommended to recover from missed triggers
|
|
97
|
+
- desired worker count is conversation-aware, so multiple queued messages on the same `conversationId` do not over-scale worker spawn
|
|
98
|
+
|
|
99
|
+
In this project setup, the queue processor runtime is **Fly worker-only** (not the consumer webhook app).
|
|
100
|
+
The consumer app receives ingress and enqueues, while Fly workers dequeue and execute jobs.
|
|
101
|
+
The worker should consume tenant-specific tokens from the hydration payload (resolved by the component), not from global Fly env vars.
|
|
102
|
+
|
|
103
|
+
### Cron fallback every 5 minutes
|
|
104
|
+
|
|
105
|
+
In your Convex app, add a cron fallback for reconcile:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { cronJobs } from "convex/server";
|
|
109
|
+
import { api } from "./_generated/api";
|
|
110
|
+
|
|
111
|
+
const crons = cronJobs();
|
|
112
|
+
|
|
113
|
+
crons.interval(
|
|
114
|
+
"agent-factory reconcile workers fallback",
|
|
115
|
+
{ minutes: 5 },
|
|
116
|
+
api.example.startWorkers,
|
|
117
|
+
{},
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
export default crons;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This cron is a safety net. The primary path remains enqueue-triggered reconcile.
|
|
124
|
+
|
|
125
|
+
### LLM configuration (Fly env)
|
|
126
|
+
|
|
127
|
+
LLM selection is intentionally **not stored in `agentProfiles`** anymore.
|
|
128
|
+
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.
|
|
129
|
+
|
|
130
|
+
Why:
|
|
131
|
+
- keeps model routing as infrastructure/runtime concern
|
|
132
|
+
- avoids per-agent schema coupling to a specific LLM field
|
|
133
|
+
- lets you switch model/provider with a Fly deploy or env change only
|
|
134
|
+
|
|
135
|
+
Practical notes:
|
|
136
|
+
- set model/provider env on the Fly app (`fly secrets set` / `[env]` in `fly.toml`)
|
|
137
|
+
- keep `agentProfiles` focused on identity, skills, docs, and secrets references
|
|
138
|
+
- worker image tag stays centralized in `src/component/config.ts` (`DEFAULT_WORKER_IMAGE`)
|
|
139
|
+
|
|
140
|
+
If you use `exposeApi(...)`, the worker contract is available directly on the consumer API surface:
|
|
141
|
+
- `workerClaim`
|
|
142
|
+
- `workerHydrationBundle`
|
|
143
|
+
- `workerHeartbeat`
|
|
144
|
+
- `workerComplete`
|
|
145
|
+
- `workerFail`
|
|
146
|
+
|
|
147
|
+
### HTTP Routes
|
|
148
|
+
|
|
149
|
+
You can mount an ingress webhook route in your app:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { httpRouter } from "convex/server";
|
|
153
|
+
import { registerRoutes } from "@okrlinkhub/agent-factory";
|
|
154
|
+
import { components } from "./_generated/api";
|
|
155
|
+
|
|
156
|
+
const http = httpRouter();
|
|
157
|
+
|
|
158
|
+
registerRoutes(http, components.agentFactory, {
|
|
159
|
+
pathPrefix: "/agent-factory",
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
export default http;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
This exposes:
|
|
166
|
+
- `POST /agent-factory/telegram/webhook` -> enqueue-only (no business processing)
|
|
167
|
+
|
|
168
|
+
Important: the webhook/router only receives ingress and enqueues.
|
|
169
|
+
Do not point Telegram directly to Fly worker machines.
|
|
170
|
+
Use webhook -> consumer app (Next.js/Vercel) -> Convex queue -> Fly workers (pull-based processing).
|
|
171
|
+
|
|
172
|
+
### One-time Telegram pairing and internal user mapping
|
|
173
|
+
|
|
174
|
+
The component can keep the user-to-agent mapping internally through `identityBindings`.
|
|
175
|
+
You can bind your consumer user id directly to an `agentKey` without managing a custom
|
|
176
|
+
table in the consumer app.
|
|
177
|
+
|
|
178
|
+
Typical one-time pairing flow:
|
|
179
|
+
|
|
180
|
+
1. Your app authenticates the user and creates a one-time pairing code via
|
|
181
|
+
`createPairingCode`.
|
|
182
|
+
2. User opens Telegram deep-link (`/start <pairingCode>`).
|
|
183
|
+
3. `registerRoutes(...)` webhook consumes the pairing code and performs
|
|
184
|
+
`bindUserAgent` automatically with `source: "telegram_pairing"` and
|
|
185
|
+
Telegram ids from the update.
|
|
186
|
+
4. Webhook ingress then resolves the binding internally and enqueues with the mapped
|
|
187
|
+
`agentKey`.
|
|
188
|
+
|
|
189
|
+
Available pairing APIs (via `exposeApi(...)`):
|
|
190
|
+
- `createPairingCode`
|
|
191
|
+
- `getPairingCodeStatus`
|
|
192
|
+
|
|
193
|
+
Telegram token storage (multi-tenant):
|
|
194
|
+
- store tenant token in component secrets with an agent-scoped ref (for example `telegram.botToken.<agentKey>`)
|
|
195
|
+
- include that ref in `agentProfiles.secretsRef`
|
|
196
|
+
- worker gets resolved plaintext from hydration bundle (`telegramBotToken`) at runtime
|
|
197
|
+
- do not use a single global `TELEGRAM_BOT_TOKEN` on Fly app
|
|
198
|
+
|
|
199
|
+
`registerRoutes(...)` supports this behavior with:
|
|
200
|
+
- `resolveAgentKeyFromBinding` (default `true`)
|
|
201
|
+
- `fallbackAgentKey` (default `"default"`)
|
|
202
|
+
- `requireBindingForTelegram` (default `false`, when `true` rejects unbound users)
|
|
203
|
+
|
|
204
|
+
Special handling for `/start`:
|
|
205
|
+
- `/start <pairingCode>` attempts pairing consumption and does not enqueue the command.
|
|
206
|
+
- invalid `/start` payload returns `200` with pairing error details to avoid Telegram retries.
|
|
207
|
+
|
|
208
|
+
## Architecture
|
|
209
|
+
|
|
210
|
+
```mermaid
|
|
211
|
+
flowchart LR
|
|
212
|
+
telegramWebhook[TelegramWebhook] --> appRouter[Consumer Router NextOrVite]
|
|
213
|
+
appRouter --> enqueueMutation[ConvexEnqueueMutation]
|
|
214
|
+
enqueueMutation --> messageQueue[ConvexMessageQueue]
|
|
215
|
+
messageQueue --> claimLoop[FlyWorkerProcessingLoop]
|
|
216
|
+
claimLoop --> hydrateStep[HydrateFromConvex]
|
|
217
|
+
hydrateStep --> runEngine[OpenClawEngineExecution]
|
|
218
|
+
runEngine --> telegramSend[TelegramDirectReply]
|
|
219
|
+
claimLoop --> heartbeatLease[HeartbeatAndLeaseRenewal]
|
|
220
|
+
heartbeatLease --> cleanupTask[PeriodicLeaseCleanup]
|
|
221
|
+
cleanupTask --> messageQueue
|
|
222
|
+
schedulerNode[ConvexSchedulerAndAutoscaler] --> flyProvider[FlyMachinesProvider]
|
|
223
|
+
flyProvider --> flyWorkers[FlyWorkerMachines]
|
|
224
|
+
flyWorkers --> claimLoop
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Data model
|
|
228
|
+
|
|
229
|
+
Core tables:
|
|
230
|
+
- `agentProfiles`
|
|
231
|
+
- `conversations`
|
|
232
|
+
- `messageQueue`
|
|
233
|
+
- `workers`
|
|
234
|
+
- `secrets`
|
|
235
|
+
|
|
236
|
+
Hydration-optimized tables:
|
|
237
|
+
- `workspaceDocuments`
|
|
238
|
+
- `agentSkills`
|
|
239
|
+
- `skillAssets`
|
|
240
|
+
- `hydrationSnapshots`
|
|
241
|
+
- `conversationHydrationCache`
|
|
242
|
+
- `dataSnapshots`
|
|
243
|
+
|
|
244
|
+
## Recent updates
|
|
245
|
+
|
|
246
|
+
- `idleTimeoutMs` aligned to 5 minutes and `workers.scheduledShutdownAt` now tracks idle lifecycle from `lastClaimAt`.
|
|
247
|
+
- Pre-stop drain protocol added: worker snapshots `/data` before termination and uploads archive metadata into `dataSnapshots`.
|
|
248
|
+
- Restore on boot added: new workers can rehydrate from latest snapshot archive.
|
|
249
|
+
- Hydration improved with `conversationHydrationCache` delta usage.
|
|
250
|
+
- `skillAssets` now contributes to hydration snapshot and fingerprint rebuilds.
|
|
251
|
+
- Worker control/snapshot APIs exposed for runtime loop (`workerControlState`, snapshot upload/finalize/fail, restore lookup).
|
|
252
|
+
|
|
253
|
+
## OpenClaw workspace mapping
|
|
254
|
+
|
|
255
|
+
| OpenClaw source | Convex table |
|
|
256
|
+
|---|---|
|
|
257
|
+
| `AGENTS.md`, `SOUL.md`, `USER.md`, `IDENTITY.md`, `HEARTBEAT.md`, `TOOLS.md` | `workspaceDocuments` |
|
|
258
|
+
| `memory/YYYY-MM-DD.md`, `MEMORY.md` | `workspaceDocuments` + `hydrationSnapshots.memoryWindow` |
|
|
259
|
+
| `skills/*/SKILL.md` | `agentSkills` |
|
|
260
|
+
| `skills/*/scripts/*`, `skills/*/config/*` metadata | `skillAssets` |
|
|
261
|
+
| Compiled hydration payload | `hydrationSnapshots` |
|
|
262
|
+
| Conversation-specific deltas | `conversationHydrationCache` |
|
|
263
|
+
|
|
264
|
+
## Failure model
|
|
265
|
+
|
|
266
|
+
- Worker crash during processing does not lose data.
|
|
267
|
+
- Each claimed job has a lease (`leaseId`, `leaseExpiresAt`) and heartbeat.
|
|
268
|
+
- Cleanup job requeues expired `processing` jobs and unlocks conversations.
|
|
269
|
+
- Retry uses exponential backoff with dead-letter fallback.
|
|
270
|
+
|
|
271
|
+
## Config-first
|
|
272
|
+
|
|
273
|
+
`src/component/config.ts` defines type-safe policies:
|
|
274
|
+
- queue policy
|
|
275
|
+
- retry policy
|
|
276
|
+
- lease policy
|
|
277
|
+
- scaling policy
|
|
278
|
+
- hydration policy
|
|
279
|
+
- provider config
|
|
280
|
+
|
|
281
|
+
## Fly.io provider notes
|
|
282
|
+
|
|
283
|
+
The current provider implementation uses Fly Machines API endpoints for:
|
|
284
|
+
- create machine
|
|
285
|
+
- list machines
|
|
286
|
+
- cordon machine
|
|
287
|
+
- terminate machine
|
|
288
|
+
|
|
289
|
+
### Worker image update procedure (local builder, no Depot)
|
|
290
|
+
|
|
291
|
+
When you update the worker runtime (for example in `openclaw-okr-image/worker.mjs`), use this flow to publish and roll out safely.
|
|
292
|
+
|
|
293
|
+
1) Deploy with Fly local builder (explicitly disabling Depot):
|
|
294
|
+
|
|
295
|
+
```sh
|
|
296
|
+
cd /path/to/openclaw-okr-image
|
|
297
|
+
fly deploy --local-only --depot=false --yes
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
2) If deployment fails with `CONVEX_URL not set`, set the secret and retry:
|
|
301
|
+
|
|
302
|
+
```sh
|
|
303
|
+
fly secrets set CONVEX_URL="https://<your-convex-deployment>.convex.cloud" -a agent-factory-workers
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
3) Capture the new image tag from deploy output (for example
|
|
307
|
+
`registry.fly.io/agent-factory-workers:deployment-XXXXXXXXXXXX`), then update
|
|
308
|
+
`src/component/config.ts` in this repo:
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
export const DEFAULT_WORKER_IMAGE =
|
|
312
|
+
"registry.fly.io/agent-factory-workers:deployment-XXXXXXXXXXXX";
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
4) Verify rollout:
|
|
316
|
+
|
|
317
|
+
```sh
|
|
318
|
+
fly status -a agent-factory-workers
|
|
319
|
+
fly logs -a agent-factory-workers --no-tail
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
5) (Recommended) Commit the `DEFAULT_WORKER_IMAGE` update so scheduler-driven
|
|
323
|
+
spawns use the exact image that was just deployed.
|
|
324
|
+
|
|
325
|
+
Recommended runtime split:
|
|
326
|
+
- Consumer app (Next.js/Vercel): webhook ingress + enqueue only
|
|
327
|
+
- Fly worker app: claim/heartbeat/complete/fail loop
|
|
328
|
+
|
|
329
|
+
Anti-pattern to avoid:
|
|
330
|
+
- Telegram webhook -> Fly worker HTTP endpoint
|
|
331
|
+
- Reason: workers are batch processors, may be scaled to zero, and should not be used as public ingress.
|
|
332
|
+
- Global Fly env `TELEGRAM_BOT_TOKEN` for all tenants
|
|
333
|
+
- Reason: breaks multi-tenant isolation and forces shared bot credentials.
|
|
334
|
+
|
|
335
|
+
References:
|
|
336
|
+
- https://docs.machines.dev/
|
|
337
|
+
- https://fly.io/docs/machines/api/machines-resource/
|
|
338
|
+
- https://docs.convex.dev/components/authoring
|
|
339
|
+
|
|
340
|
+
## Development
|
|
341
|
+
|
|
342
|
+
```sh
|
|
343
|
+
npm i
|
|
344
|
+
npm run dev
|
|
345
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=_ignore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_ignore.d.ts","sourceRoot":"","sources":["../../../src/client/_generated/_ignore.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_ignore.js","sourceRoot":"","sources":["../../../src/client/_generated/_ignore.ts"],"names":[],"mappings":";AAAA,kEAAkE"}
|