@openagents-org/agent-launcher 0.2.130 → 0.2.132
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/package.json +1 -1
- package/registry.json +7 -5
- package/src/adapters/base.js +130 -7
- package/src/adapters/claude.js +342 -255
- package/src/adapters/cursor.js +27 -8
- package/src/adapters/workspace-prompt.js +133 -28
- package/src/cli.js +85 -0
- package/src/daemon.js +12 -3
- package/src/github-client.js +165 -0
- package/src/index.js +13 -1
- package/src/installer.js +151 -19
- package/src/mcp-server.js +279 -91
- package/src/paths.js +31 -3
- package/src/registry.js +4 -4
- package/src/skill-catalog.js +125 -0
- package/src/tui.js +1 -16
- package/src/update-check.js +5 -25
- package/src/workspace-client.js +101 -6
package/package.json
CHANGED
package/registry.json
CHANGED
|
@@ -209,14 +209,16 @@
|
|
|
209
209
|
"order": 4,
|
|
210
210
|
"builtin": true,
|
|
211
211
|
"install": {
|
|
212
|
-
"binary": "agent",
|
|
213
|
-
"
|
|
214
|
-
|
|
212
|
+
"binary": "cursor-agent",
|
|
213
|
+
"binary_aliases": [
|
|
214
|
+
"agent"
|
|
215
|
+
],
|
|
216
|
+
"verify": "cursor-agent --version 2>/dev/null | head -1",
|
|
217
|
+
"verify_win": "cursor-agent --version 2>nul",
|
|
215
218
|
"requires": [],
|
|
216
219
|
"macos": "curl https://cursor.com/install -fsSL | bash",
|
|
217
220
|
"linux": "curl https://cursor.com/install -fsSL | bash",
|
|
218
|
-
"windows": "
|
|
219
|
-
"npm": "npm install -g @cursor/cli"
|
|
221
|
+
"windows": "\"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoProfile -ExecutionPolicy Bypass -Command \"Invoke-RestMethod -UseBasicParsing 'https://cursor.com/install?win32=true' | Invoke-Expression\""
|
|
220
222
|
},
|
|
221
223
|
"adapter": {
|
|
222
224
|
"module": "openagents.adapters.cursor",
|
package/src/adapters/base.js
CHANGED
|
@@ -53,6 +53,11 @@ class BaseAdapter {
|
|
|
53
53
|
// Per-channel task tracking for parallel execution
|
|
54
54
|
this._channelBusy = new Set();
|
|
55
55
|
this._channelQueues = {};
|
|
56
|
+
// Cached workspace.browser_enabled. Populated lazily on first read so we
|
|
57
|
+
// don't pay an HTTP roundtrip per message — adapters that toggle the
|
|
58
|
+
// workspace flag must reconnect/restart to pick up the change (matches
|
|
59
|
+
// the Python adapter behavior in workspace_prompt.py).
|
|
60
|
+
this._browserEnabledCache = null;
|
|
56
61
|
// Wall-clock timestamp of adapter init, used by the `status` control
|
|
57
62
|
// action to report uptime back to the channel. Reset on reinstantiation
|
|
58
63
|
// (e.g. after a `restart` IPC bounce) so uptime tracks "time since last
|
|
@@ -85,6 +90,19 @@ class BaseAdapter {
|
|
|
85
90
|
this._log(`Warning: join failed: ${e.message} \nStack: ${e.stack}`);
|
|
86
91
|
}
|
|
87
92
|
|
|
93
|
+
// Sync workspace-managed skills into disabledModules
|
|
94
|
+
try {
|
|
95
|
+
const agents = await this.client.getAgents(this.workspaceId, this.token);
|
|
96
|
+
const self = agents.find((a) => a.agentName === this.agentName);
|
|
97
|
+
if (self && self.enabledSkills) {
|
|
98
|
+
const { skillsToDisabledModules } = require('../skill-catalog');
|
|
99
|
+
this.disabledModules = skillsToDisabledModules(self.enabledSkills);
|
|
100
|
+
this._log(`Synced skills from workspace: disabled=[${[...this.disabledModules].join(',')}]`);
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
this._log(`Warning: skill sync failed (non-fatal): ${e.message}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
88
106
|
// Fast-path operations (control-event cursor + heartbeat + control poll)
|
|
89
107
|
// run BEFORE the message-cursor advance. Even though _skipExistingEvents
|
|
90
108
|
// is fast on a healthy backend, we don't want slash commands gated on
|
|
@@ -210,6 +228,8 @@ class BaseAdapter {
|
|
|
210
228
|
async _onControlAction(action, payload) {
|
|
211
229
|
if (action === 'status') {
|
|
212
230
|
await this._postStatusReport(payload);
|
|
231
|
+
} else if (action === 'routines') {
|
|
232
|
+
await this._postRoutinesReport(payload);
|
|
213
233
|
}
|
|
214
234
|
}
|
|
215
235
|
|
|
@@ -262,6 +282,74 @@ class BaseAdapter {
|
|
|
262
282
|
}
|
|
263
283
|
}
|
|
264
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Post a markdown table of the agent's active routines back to the
|
|
287
|
+
* requesting channel. Used by the `/routines` slash command. Each agent
|
|
288
|
+
* reports only routines it owns (created_by === openagents:<agentName>)
|
|
289
|
+
* so the user sees a clear "my routines" view per agent, mirroring how
|
|
290
|
+
* /status reports per-agent uptime.
|
|
291
|
+
*/
|
|
292
|
+
async _postRoutinesReport(payload) {
|
|
293
|
+
const channel = (payload && typeof payload === 'object') ? payload.channel : null;
|
|
294
|
+
if (!channel) return;
|
|
295
|
+
|
|
296
|
+
let routines = [];
|
|
297
|
+
try {
|
|
298
|
+
const data = await this.client.listRoutines(this.workspaceId, channel, this.token);
|
|
299
|
+
// Accept both the canonical `openagents:<name>` source and the bare
|
|
300
|
+
// `<name>` form. Agents that follow the workspace prompt verbatim
|
|
301
|
+
// produce the prefixed form, but some agents send the bare name when
|
|
302
|
+
// they construct the POST body themselves.
|
|
303
|
+
const prefixed = `openagents:${this.agentName}`;
|
|
304
|
+
routines = ((data && data.routines) || []).filter(
|
|
305
|
+
(r) => r.created_by === prefixed || r.created_by === this.agentName,
|
|
306
|
+
);
|
|
307
|
+
} catch (e) {
|
|
308
|
+
this._log(`Routines: failed to list: ${e && e.message ? e.message : e}`);
|
|
309
|
+
try {
|
|
310
|
+
await this.client.sendMessage(
|
|
311
|
+
this.workspaceId, channel, this.token,
|
|
312
|
+
`**Routines for \`${this.agentName}\`**\n\n_Failed to fetch routines._`,
|
|
313
|
+
{ senderType: 'agent', senderName: this.agentName, messageType: 'chat', sessionId: this._sessionId },
|
|
314
|
+
);
|
|
315
|
+
} catch {}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
let content;
|
|
320
|
+
if (!routines.length) {
|
|
321
|
+
content = `**Routines for \`${this.agentName}\`**\n\n_No active routines._`;
|
|
322
|
+
} else {
|
|
323
|
+
const rows = routines.map((r) => {
|
|
324
|
+
const schedule = (r.schedule_interval_minutes != null)
|
|
325
|
+
? `every ${r.schedule_interval_minutes} min`
|
|
326
|
+
: `${String(r.schedule_hour ?? 0).padStart(2, '0')}:${String(r.schedule_minute ?? 0).padStart(2, '0')} UTC` +
|
|
327
|
+
(r.schedule_days ? ` (days [${r.schedule_days.join(',')}])` : ' daily');
|
|
328
|
+
const next = r.next_fires_at || '—';
|
|
329
|
+
const name = String(r.name || '').replace(/\|/g, '\\|');
|
|
330
|
+
const id = String(r.id || '').slice(0, 8);
|
|
331
|
+
return `| \`${id}\` | ${name} | ${schedule} | ${next} |`;
|
|
332
|
+
});
|
|
333
|
+
content =
|
|
334
|
+
`**Routines for \`${this.agentName}\`** (${routines.length})\n\n` +
|
|
335
|
+
'| ID | Name | Schedule | Next fires |\n' +
|
|
336
|
+
'|---|---|---|---|\n' +
|
|
337
|
+
rows.join('\n');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
await this.client.sendMessage(this.workspaceId, channel, this.token, content, {
|
|
342
|
+
senderType: 'agent',
|
|
343
|
+
senderName: this.agentName,
|
|
344
|
+
messageType: 'chat',
|
|
345
|
+
metadata: { agent_mode: this._mode },
|
|
346
|
+
sessionId: this._sessionId,
|
|
347
|
+
});
|
|
348
|
+
} catch (e) {
|
|
349
|
+
this._log(`Routines: failed to post: ${e && e.message ? e.message : e}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
265
353
|
_hasActiveWork() {
|
|
266
354
|
return this._channelBusy.size > 0;
|
|
267
355
|
}
|
|
@@ -306,7 +394,7 @@ class BaseAdapter {
|
|
|
306
394
|
|
|
307
395
|
while (this._running) {
|
|
308
396
|
pollCount++;
|
|
309
|
-
let messages, rawCursor;
|
|
397
|
+
let messages, rawCursor, composingActive = false;
|
|
310
398
|
try {
|
|
311
399
|
const result = await this.client.pollPending(
|
|
312
400
|
this.workspaceId, this.agentName, this.token,
|
|
@@ -314,8 +402,9 @@ class BaseAdapter {
|
|
|
314
402
|
);
|
|
315
403
|
messages = result.messages;
|
|
316
404
|
rawCursor = result.cursor;
|
|
405
|
+
composingActive = !!result.composing;
|
|
317
406
|
if (pollCount <= 3 || pollCount % 20 === 0) {
|
|
318
|
-
this._log(`Poll #${pollCount}: ${messages.length} messages, cursor=${rawCursor || 'none'}`);
|
|
407
|
+
this._log(`Poll #${pollCount}: ${messages.length} messages, cursor=${rawCursor || 'none'}${composingActive ? ' composing' : ''}`);
|
|
319
408
|
}
|
|
320
409
|
} catch (e) {
|
|
321
410
|
this._log(`Poll #${pollCount} failed: ${e.message} \nStack: ${e.stack}`);
|
|
@@ -384,11 +473,26 @@ class BaseAdapter {
|
|
|
384
473
|
}
|
|
385
474
|
}
|
|
386
475
|
|
|
387
|
-
// Adaptive polling
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
|
|
476
|
+
// Adaptive polling with warm plateau:
|
|
477
|
+
// Active (messages incoming): 2s
|
|
478
|
+
// Warm (≤5 min since last msg): 5s
|
|
479
|
+
// Cooldown (5-7 min): 5s → 15s (ramp 1s per idle poll)
|
|
480
|
+
// Cold (>7 min): 15s
|
|
481
|
+
// The warm plateau keeps the agent responsive during typical user
|
|
482
|
+
// think-time between messages without hammering the backend.
|
|
483
|
+
const WARM_INTERVAL = 5000;
|
|
484
|
+
const WARM_POLLS = 60; // 60 × 5s = 5 minutes warm plateau
|
|
485
|
+
let delay;
|
|
486
|
+
if (incoming.length > 0) {
|
|
487
|
+
delay = 2000;
|
|
488
|
+
} else if (composingActive) {
|
|
489
|
+
delay = 2000;
|
|
490
|
+
idleCount = Math.min(idleCount, WARM_POLLS);
|
|
491
|
+
} else if (idleCount <= WARM_POLLS) {
|
|
492
|
+
delay = WARM_INTERVAL;
|
|
493
|
+
} else {
|
|
494
|
+
delay = Math.min(WARM_INTERVAL + (idleCount - WARM_POLLS) * 1000, 15000);
|
|
495
|
+
}
|
|
392
496
|
await this._sleep(delay);
|
|
393
497
|
}
|
|
394
498
|
}
|
|
@@ -636,6 +740,25 @@ class BaseAdapter {
|
|
|
636
740
|
_sleep(ms) {
|
|
637
741
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
638
742
|
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Return whether the workspace has the Browser Fabric viewer toggle on.
|
|
746
|
+
* Cached for the lifetime of the adapter — restart to pick up a flip.
|
|
747
|
+
* Falls back to false on error so the prompt builders don't accidentally
|
|
748
|
+
* inject the strong directive against an older backend that can't route
|
|
749
|
+
* to Browser Fabric.
|
|
750
|
+
*/
|
|
751
|
+
async getBrowserEnabled() {
|
|
752
|
+
if (this._browserEnabledCache === null) {
|
|
753
|
+
try {
|
|
754
|
+
const meta = await this.client.getWorkspaceMetadata(this.workspaceId, this.token);
|
|
755
|
+
this._browserEnabledCache = !!(meta && meta.browserEnabled);
|
|
756
|
+
} catch (e) {
|
|
757
|
+
this._browserEnabledCache = false;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return this._browserEnabledCache;
|
|
761
|
+
}
|
|
639
762
|
}
|
|
640
763
|
|
|
641
764
|
// ------------------------------------------------------------------
|