rentabots-sdk 1.7.14 → 1.7.17

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/dist/index.js CHANGED
@@ -71,7 +71,7 @@ exports.MessageSchema = zod_1.z.object({
71
71
  })
72
72
  });
73
73
  // --- CORE SDK ENGINE ---
74
- let SDK_VERSION = '1.7.14'; // fallback when package.json is unavailable
74
+ let SDK_VERSION = '1.7.17'; // fallback when package.json is unavailable
75
75
  try {
76
76
  const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
77
77
  SDK_VERSION = pkg.version;
@@ -658,13 +658,22 @@ class Agent extends events_1.EventEmitter {
658
658
  async syncFromCloud() {
659
659
  this.logInternal("Synchronizing missions and bids from cloud...");
660
660
  try {
661
- // 1. Sync Active Missions
661
+ // 1. Sync Active Missions (authoritative cloud state)
662
662
  const activeRes = await this.api.get('jobs?status=in_progress');
663
663
  const myActive = activeRes.data.data.filter((j) => j.agentId === this.agentId);
664
- for (const raw of myActive) {
665
- if (!this.activeMissions.has(raw.id)) {
666
- this.activeMissions.set(raw.id, this.enrichJob(raw));
664
+ const liveIds = new Set(myActive.map((j) => j.id));
665
+ // Prune stale local active missions so autopilot can resume after completion
666
+ for (const localId of Array.from(this.activeMissions.keys())) {
667
+ if (!liveIds.has(localId)) {
668
+ const stale = this.activeMissions.get(localId);
669
+ if (stale)
670
+ this.completedMissions.set(localId, { ...stale, status: 'completed' });
671
+ this.activeMissions.delete(localId);
672
+ this.logInternal(`Uplink: Pruned stale active mission ${localId}`);
667
673
  }
674
+ }
675
+ for (const raw of myActive) {
676
+ this.activeMissions.set(raw.id, this.enrichJob(raw));
668
677
  this.socket?.emit('join_mission', raw.id);
669
678
  this.logInternal(`Uplink: Monitoring active mission ${raw.id}`);
670
679
  }
package/init.js CHANGED
@@ -185,7 +185,7 @@ async function main() {
185
185
  await agent.connect();
186
186
  await agent.sendMessage(job.id, "👷 Worker Unit [PID " + process.pid + "] deployed. Analyzing requirements...");
187
187
 
188
- const task = 'PROJECT: ' + job.title + '. BRIEF: ' + job.description + '. INSTRUCTION: Execute this task. Create all files in the workspace.';
188
+ const task = 'PROJECT: ' + job.title + '. BRIEF: ' + job.description + '. INSTRUCTION: Execute this task in the current directory. Do not leave the workspace. You MUST produce tangible deliverables (code + README + run/test notes) in local files.';
189
189
 
190
190
  // Use shell command string (not array args)
191
191
  const taskArg = JSON.stringify(task);
@@ -206,36 +206,53 @@ async function main() {
206
206
 
207
207
  let overallSuccess = false;
208
208
  let lastOutput = '';
209
+ let cycle = 0;
210
+ const maxCycles = Number(process.env.RENTABOTS_MAX_RECOVERY_CYCLES || 0); // 0 = infinite
209
211
 
210
212
  try {
211
- for (const cmd of cmdCandidates) {
212
- let exitCode = -1;
213
- let output = '';
213
+ while (!overallSuccess) {
214
+ cycle += 1;
215
+
216
+ for (const cmd of cmdCandidates) {
217
+ let exitCode = -1;
218
+ let output = '';
219
+
220
+ for (let attempt = 1; attempt <= 3; attempt++) {
221
+ const result = await agent.execute(job.id, cmd, { timeout: 1200000, shell: true }); // 20m
222
+ exitCode = result.exitCode;
223
+ output = result.output || '';
224
+
225
+ if (exitCode === 0) {
226
+ const low = (output || '').toLowerCase();
227
+ const pseudoSuccess = low.includes('usage: openclaw') || low.includes('pass --to') || low.includes('unknown command');
228
+ if (!pseudoSuccess) {
229
+ overallSuccess = true;
230
+ lastOutput = output;
231
+ break;
232
+ }
233
+ }
214
234
 
215
- for (let attempt = 1; attempt <= 2; attempt++) {
216
- const result = await agent.execute(job.id, cmd, { timeout: 900000, shell: true });
217
- exitCode = result.exitCode;
218
- output = result.output || '';
219
-
220
- if (exitCode === 0) {
221
- overallSuccess = true;
222
235
  lastOutput = output;
223
- break;
236
+ if (attempt < 3) {
237
+ await agent.sendMessage(job.id, 'âš ī¸ OpenClaw run delayed. Retrying...');
238
+ await new Promise(r => setTimeout(r, 7000));
239
+ }
224
240
  }
225
241
 
226
- lastOutput = output;
227
- if (attempt < 2) {
228
- await agent.sendMessage(job.id, 'âš ī¸ OpenClaw run delayed. Retrying once...');
229
- await new Promise(r => setTimeout(r, 5000));
242
+ if (overallSuccess) break;
243
+ if (lastOutput.toLowerCase().includes('unknown command')) {
244
+ await agent.sendMessage(job.id, 'â„šī¸ Switching OpenClaw command compatibility mode...');
245
+ continue;
230
246
  }
231
247
  }
232
248
 
233
249
  if (overallSuccess) break;
234
- if (lastOutput.toLowerCase().includes('unknown command')) {
235
- await agent.sendMessage(job.id, 'â„šī¸ Switching OpenClaw command compatibility mode...');
236
- continue;
237
- }
238
- break;
250
+
251
+ await agent.setProgress(job.id, Math.min(95, 15 + cycle * 2));
252
+ await agent.sendMessage(job.id, '🛟 Recovery cycle ' + cycle + ' failed. Auto-retrying until completion...');
253
+
254
+ if (maxCycles > 0 && cycle >= maxCycles) break;
255
+ await new Promise(r => setTimeout(r, 15000));
239
256
  }
240
257
  } finally {
241
258
  clearInterval(heartbeat);
@@ -257,10 +274,21 @@ async function main() {
257
274
  };
258
275
  walk(workDir);
259
276
 
277
+ let fallbackGenerated = false;
260
278
  if (files.length === 0) {
261
279
  await agent.setProgress(job.id, 40);
262
- await agent.sendMessage(job.id, "âš ī¸ I did not generate deliverable files yet. Please clarify expected output (files/format), and I will continue.");
263
- return;
280
+ await agent.sendMessage(job.id, "âš ī¸ No deliverable files were produced yet. Entering forced-build fallback and generating starter deliverables now.");
281
+
282
+ const readme = '# Mission Output\n\nTitle: ' + job.title + '\n\nDescription: ' + job.description + '\n\n## Status\n- Auto-generated fallback deliverable because runtime produced no files.\n- Worker is continuing execution and awaiting optional clarifications.';
283
+ const questions = '# Clarifications Needed\n\n1. Exact output file name(s)?\n2. Provide sample input data?\n3. Any strict acceptance criteria/tests?';
284
+ const script = "# TODO: Implement mission logic\n# Mission: " + job.title + "\n\nprint('Placeholder fallback script generated. Update with final implementation.')\n";
285
+
286
+ fs.writeFileSync(path.join(workDir, 'README.md'), readme);
287
+ fs.writeFileSync(path.join(workDir, 'CLARIFICATIONS.md'), questions);
288
+ fs.writeFileSync(path.join(workDir, 'solution.py'), script);
289
+
290
+ files.push('README.md', 'CLARIFICATIONS.md', 'solution.py');
291
+ fallbackGenerated = true;
264
292
  }
265
293
 
266
294
  const repoRes = await agent.getRepo(job.id);
@@ -286,6 +314,33 @@ async function main() {
286
314
  return;
287
315
  }
288
316
 
317
+ // Lightweight QA: ensure deliverables are relevant to mission intent
318
+ const missionText = (job.title + ' ' + job.description).toLowerCase();
319
+ const required = [];
320
+ if (missionText.includes('python')) required.push('python');
321
+ if (missionText.includes('csv')) required.push('csv');
322
+ if (missionText.includes('blank') || missionText.includes('whitespace')) required.push('strip');
323
+
324
+ let combinedText = '';
325
+ for (const rel of files) {
326
+ const ext = path.extname(rel).toLowerCase();
327
+ if (['.js','.ts','.tsx','.jsx','.json','.md','.txt','.py','.yml','.yaml','.html','.css','.csv'].includes(ext)) {
328
+ try { combinedText += '\n' + fs.readFileSync(path.join(workDir, rel), 'utf8').toLowerCase(); } catch (_) {}
329
+ }
330
+ }
331
+ const missing = required.filter(k => !combinedText.includes(k));
332
+ if (missing.length > 0) {
333
+ await agent.setProgress(job.id, 70);
334
+ await agent.sendMessage(job.id, 'âš ī¸ QA check failed: output appears misaligned with mission (' + missing.join(', ') + ' not found). I will revise now.');
335
+ return;
336
+ }
337
+
338
+ if (fallbackGenerated) {
339
+ await agent.setProgress(job.id, 85);
340
+ await agent.sendMessage(job.id, '🛟 Draft fallback deliverables uploaded for review (not final). Repo: ' + (repo?.id ? ('https://rentabots.com/repos/' + repo.id) : 'mission repository') + '. I will continue refinement after your confirmation.');
341
+ return;
342
+ }
343
+
289
344
  await agent.setProgress(job.id, 100);
290
345
  if (repo?.id) {
291
346
  await agent.sendMessage(job.id, "✅ Execution complete. Deliverables uploaded: " + uploaded + " files. Repo: https://rentabots.com/repos/" + repo.id);
package/init_templates.js CHANGED
@@ -169,7 +169,7 @@ async function main() {
169
169
  await agent.sendMessage(job.id, "👷 Worker Unit [PID " + process.pid + "] deployed to workspace. Analyzing requirements...");
170
170
 
171
171
  // Construct the prompt for the autonomous brain
172
- const task = \`PROJECT: \${job.title}. BRIEF: \${job.description}. INSTRUCTION: Execute this task in the current directory. Do not leave the workspace.\`;
172
+ const task = \`PROJECT: \${job.title}. BRIEF: \${job.description}. INSTRUCTION: Execute this task in the current directory. Do not leave the workspace. You MUST produce tangible deliverables (code + README + run/test notes) in local files.\`;
173
173
 
174
174
  // --- đŸĻž EXECUTE VIA OPENCLAW BRAIN ---
175
175
  const taskArg = JSON.stringify(task);
@@ -190,36 +190,54 @@ async function main() {
190
190
 
191
191
  let overallSuccess = false;
192
192
  let lastOutput = '';
193
+ let cycle = 0;
194
+ const maxCycles = Number(process.env.RENTABOTS_MAX_RECOVERY_CYCLES || 0); // 0 = infinite
193
195
 
194
196
  try {
195
- for (const cmd of cmdCandidates) {
196
- let exitCode = -1;
197
- let output = '';
197
+ while (!overallSuccess) {
198
+ cycle += 1;
199
+
200
+ for (const cmd of cmdCandidates) {
201
+ let exitCode = -1;
202
+ let output = '';
203
+
204
+ for (let attempt = 1; attempt <= 3; attempt++) {
205
+ const result = await agent.execute(job.id, cmd, { timeout: 1200000, shell: true }); // 20m
206
+ exitCode = result.exitCode;
207
+ output = result.output || '';
208
+
209
+ if (exitCode === 0) {
210
+ const low = (output || '').toLowerCase();
211
+ const pseudoSuccess = low.includes('usage: openclaw') || low.includes('pass --to') || low.includes('unknown command');
212
+ if (!pseudoSuccess) {
213
+ overallSuccess = true;
214
+ lastOutput = output;
215
+ break;
216
+ }
217
+ }
198
218
 
199
- for (let attempt = 1; attempt <= 2; attempt++) {
200
- const result = await agent.execute(job.id, cmd, { timeout: 900000, shell: true });
201
- exitCode = result.exitCode;
202
- output = result.output || '';
203
-
204
- if (exitCode === 0) {
205
- overallSuccess = true;
206
219
  lastOutput = output;
207
- break;
220
+ if (attempt < 3) {
221
+ await agent.sendMessage(job.id, 'âš ī¸ OpenClaw run delayed. Retrying...');
222
+ await new Promise(r => setTimeout(r, 7000));
223
+ }
208
224
  }
209
225
 
210
- lastOutput = output;
211
- if (attempt < 2) {
212
- await agent.sendMessage(job.id, 'âš ī¸ OpenClaw run delayed. Retrying once...');
213
- await new Promise(r => setTimeout(r, 5000));
226
+ if (overallSuccess) break;
227
+
228
+ if (lastOutput.toLowerCase().includes('unknown command')) {
229
+ await agent.sendMessage(job.id, 'â„šī¸ Switching OpenClaw command compatibility mode...');
230
+ continue;
214
231
  }
215
232
  }
216
233
 
217
234
  if (overallSuccess) break;
218
- if (lastOutput.toLowerCase().includes('unknown command')) {
219
- await agent.sendMessage(job.id, 'â„šī¸ Switching OpenClaw command compatibility mode...');
220
- continue;
221
- }
222
- break;
235
+
236
+ await agent.setProgress(job.id, Math.min(95, 15 + cycle * 2));
237
+ await agent.sendMessage(job.id, '🛟 Recovery cycle ' + cycle + ' failed. Auto-retrying until completion...');
238
+
239
+ if (maxCycles > 0 && cycle >= maxCycles) break;
240
+ await new Promise(r => setTimeout(r, 15000));
223
241
  }
224
242
  } finally {
225
243
  clearInterval(heartbeat);
@@ -241,10 +259,21 @@ async function main() {
241
259
  };
242
260
  walk(workDir);
243
261
 
262
+ let fallbackGenerated = false;
244
263
  if (files.length === 0) {
245
264
  await agent.setProgress(job.id, 40);
246
- await agent.sendMessage(job.id, "âš ī¸ I did not generate deliverable files yet. Please clarify expected output (files/format), and I will continue.");
247
- return;
265
+ await agent.sendMessage(job.id, "âš ī¸ No deliverable files were produced yet. Entering forced-build fallback and generating starter deliverables now.");
266
+
267
+ const readme = '# Mission Output\n\nTitle: ' + job.title + '\n\nDescription: ' + job.description + '\n\n## Status\n- Auto-generated fallback deliverable because runtime produced no files.\n- Worker is continuing execution and awaiting optional clarifications.';
268
+ const questions = '# Clarifications Needed\n\n1. Exact output file name(s)?\n2. Provide sample input data?\n3. Any strict acceptance criteria/tests?';
269
+ const script = "# TODO: Implement mission logic\n# Mission: " + job.title + "\n\nprint('Placeholder fallback script generated. Update with final implementation.')\n";
270
+
271
+ fs.writeFileSync(path.join(workDir, 'README.md'), readme);
272
+ fs.writeFileSync(path.join(workDir, 'CLARIFICATIONS.md'), questions);
273
+ fs.writeFileSync(path.join(workDir, 'solution.py'), script);
274
+
275
+ files.push('README.md', 'CLARIFICATIONS.md', 'solution.py');
276
+ fallbackGenerated = true;
248
277
  }
249
278
 
250
279
  const repoRes = await agent.getRepo(job.id);
@@ -270,6 +299,33 @@ async function main() {
270
299
  return;
271
300
  }
272
301
 
302
+ // Lightweight QA: ensure deliverables are relevant to mission intent
303
+ const missionText = (job.title + ' ' + job.description).toLowerCase();
304
+ const required = [];
305
+ if (missionText.includes('python')) required.push('python');
306
+ if (missionText.includes('csv')) required.push('csv');
307
+ if (missionText.includes('blank') || missionText.includes('whitespace')) required.push('strip');
308
+
309
+ let combinedText = '';
310
+ for (const rel of files) {
311
+ const ext = path.extname(rel).toLowerCase();
312
+ if (['.js','.ts','.tsx','.jsx','.json','.md','.txt','.py','.yml','.yaml','.html','.css','.csv'].includes(ext)) {
313
+ try { combinedText += '\n' + fs.readFileSync(path.join(workDir, rel), 'utf8').toLowerCase(); } catch (_) {}
314
+ }
315
+ }
316
+ const missing = required.filter(k => !combinedText.includes(k));
317
+ if (missing.length > 0) {
318
+ await agent.setProgress(job.id, 70);
319
+ await agent.sendMessage(job.id, 'âš ī¸ QA check failed: output appears misaligned with mission (' + missing.join(', ') + ' not found). I will revise now.');
320
+ return;
321
+ }
322
+
323
+ if (fallbackGenerated) {
324
+ await agent.setProgress(job.id, 85);
325
+ await agent.sendMessage(job.id, '🛟 Draft fallback deliverables uploaded for review (not final). Repo: ' + (repo?.id ? ('https://rentabots.com/repos/' + repo.id) : 'mission repository') + '. I will continue refinement after your confirmation.');
326
+ return;
327
+ }
328
+
273
329
  await agent.setProgress(job.id, 100);
274
330
  if (repo?.id) {
275
331
  await agent.sendMessage(job.id, "✅ Execution complete. Deliverables uploaded: " + uploaded + " files. Repo: https://rentabots.com/repos/" + repo.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rentabots-sdk",
3
- "version": "1.7.14",
3
+ "version": "1.7.17",
4
4
  "description": "Official SDK for RentaBots AI Agent Marketplace",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",