@11agents/cli 0.1.18 → 0.1.19

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 CHANGED
@@ -125,17 +125,29 @@ export async function handleRuntimeTask(task) {
125
125
  }
126
126
  ```
127
127
 
128
- ## MCP Project Sync Server
129
-
130
- Run the local MCP server over stdio:
131
-
132
- ```bash
133
- 11agents mcp start
128
+ ## Hosted MCP
129
+
130
+ Runtime agents and MCP clients should use the hosted project MCP endpoint:
131
+
132
+ ```json
133
+ {
134
+ "mcpServers": {
135
+ "11agents-project-sync": {
136
+ "url": "https://app.11agents.ai/mcp",
137
+ "transport": "streamable-http",
138
+ "headers": {
139
+ "Authorization": "Bearer $PROJECT_SWARM_TOKEN"
140
+ }
141
+ }
142
+ }
143
+ }
134
144
  ```
135
145
 
136
- Configure your MCP client to run that command. The server automatically reads matching project tokens from `~/.11agents/credentials`; a tool-call token can still be passed explicitly when needed. The server exposes:
146
+ The hosted server exposes project-scoped tools such as:
137
147
 
138
148
  - `knowledge_sync` — pull/push the project knowledge base between cloud and `~/.11agents/<project>/knowledge_base/`.
149
+ - `database_sync` — pull/push the cloud project database snapshot.
150
+ - `agent_planning` — list, import, generate, and dispatch agent calendar work.
139
151
 
140
152
  ## Telemetry Compatibility
141
153
 
package/bin/11agents.js CHANGED
@@ -21,13 +21,12 @@ Usage:
21
21
  11agents -v | --version
22
22
  11agents runtime scan
23
23
  11agents runtime register [--server <url>] [--token <token>] [--machine <key>]
24
- 11agents daemon start [--server <url>] [--token <token>] [--machine <key>] [--task-interval <seconds>] [--project-refresh-interval <seconds>] [--background]
24
+ 11agents daemon start [--server <url>] [--token <token>] [--machine <key>] [--concurrency <n>] [--task-interval <seconds>] [--project-refresh-interval <seconds>] [--background]
25
25
  11agents daemon status
26
26
  11agents daemon stop
27
27
  11agents daemon start --handler ./worker.js # optional custom worker override
28
28
  11agents logs daemon [--tail 200]
29
29
  11agents logs task <task-id> [--project <slug>] [--tail 120]
30
- 11agents mcp start
31
30
  11agents validate <file>
32
31
  11agents push batch <file>
33
32
  11agents push artifact --workspace <slug> --agent <key> --platform x --type post --external-id <id>
@@ -42,7 +41,7 @@ Environment:
42
41
  GTM_WRITES_TOKEN control-plane token for runtime registration
43
42
  ELEVENAGENTS_MACHINE stable machine key, defaults to hostname
44
43
  GTM_SWARM_TOKEN project swarm token for push/node commands
45
- ~/.11agents/credentials project token map for MCP knowledge sync
44
+ ~/.11agents/credentials project token map for hosted MCP and sync commands
46
45
 
47
46
  Runtime task workspace:
48
47
  Daemon project headquarters live under ~/.11agents/<project>/.
@@ -99,6 +98,7 @@ async function main() {
99
98
  console.log(`log: ${result.logPath}`)
100
99
  return
101
100
  }
101
+ if (flags.concurrency === undefined) flags.concurrency = '3'
102
102
  await startRuntimeDaemon(flags)
103
103
  return
104
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@11agents/cli",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "11agents local runtime and telemetry CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -908,8 +908,8 @@ async function prepareRuntimeTask(task, flags, deps, config) {
908
908
  const database = await syncDatabaseIfNeeded({ task, workdir, config, flags, deps })
909
909
  const skills = await materializeSkillsIfChanged({ task, workdir, flags, deps })
910
910
 
911
- // Resolve and inject the project token so MCP subprocesses (e.g. `11agents mcp start`)
912
- // spawned by the runtime agent can authenticate without needing credentials on disk.
911
+ // Resolve and inject the project token so hosted MCP calls spawned by the
912
+ // runtime agent can authenticate without needing credentials on disk.
913
913
  const projectToken = await projectSyncToken(projectTokenCandidatesForTask(task, flags), flags, deps)
914
914
  const env = {
915
915
  ...process.env,
@@ -1228,156 +1228,160 @@ function defaultTaskHandler(flags, deps) {
1228
1228
  }
1229
1229
  }
1230
1230
 
1231
- async function claimAndRunRuntimeTasks(registration, flags, deps, handlerModule, retryState = createRetryState(), heartbeatIntervalMs = 15000) {
1232
- if (!handlerModule) return 0
1233
- const config = configFromFlags(flags)
1234
- const machineKey = registration?.machine?.machine_key || machineOverride(flags) || ''
1235
- let handled = 0
1236
-
1237
- for (const runtime of registration?.runtimes || []) {
1238
- if (!runtime?.id) continue
1239
- const claim = await runWithDaemonRetry('claim runtime task', () => (
1240
- deps.requestJson('/api/runtime/tasks/claim', {
1241
- method: 'POST',
1242
- body: {
1243
- runtime_id: runtime.id,
1244
- machine_key: machineKey,
1245
- },
1246
- config,
1247
- })
1248
- ), deps, retryState)
1249
- const task = claim?.task
1250
- if (!task) continue
1251
-
1252
- const runtimeTask = {
1253
- ...task,
1254
- runtime_id: task.runtime_id || runtime.id,
1255
- runtime: {
1256
- provider: runtime.provider,
1257
- model: runtime.model || '',
1258
- ...(task.runtime || {}),
1259
- id: task.runtime?.id || runtime.id,
1260
- machine_key: task.runtime?.machine_key || machineKey,
1231
+ async function runOneRuntimeTaskSlot(runtime, config, machineKey, registration, flags, deps, handlerModule, retryState, heartbeatIntervalMs) {
1232
+ const claim = await runWithDaemonRetry('claim runtime task', () => (
1233
+ deps.requestJson('/api/runtime/tasks/claim', {
1234
+ method: 'POST',
1235
+ body: {
1236
+ runtime_id: runtime.id,
1237
+ machine_key: machineKey,
1261
1238
  },
1239
+ config,
1240
+ })
1241
+ ), deps, retryState)
1242
+ const task = claim?.task
1243
+ if (!task) return false
1244
+
1245
+ const runtimeTask = {
1246
+ ...task,
1247
+ runtime_id: task.runtime_id || runtime.id,
1248
+ runtime: {
1249
+ provider: runtime.provider,
1250
+ model: runtime.model || '',
1251
+ ...(task.runtime || {}),
1252
+ id: task.runtime?.id || runtime.id,
1253
+ machine_key: task.runtime?.machine_key || machineKey,
1254
+ },
1255
+ }
1256
+
1257
+ deps.log(JSON.stringify({ claimed: runtimeTask.id, runtime_id: runtime.id }, null, 2))
1258
+ await writeCurrentClaim(deps, runtimeTask, machineKey)
1259
+ let completion = null
1260
+ let executionContext = null
1261
+ if (runtimeTask.workspace?.slug) {
1262
+ const token = await projectSyncToken(projectTokenCandidatesForTask(runtimeTask, flags), flags, deps)
1263
+ const syncConfig = { ...config, token }
1264
+ try {
1265
+ await runWithTaskRetry('sync knowledge base', () => (
1266
+ deps.syncKnowledge({
1267
+ project: runtimeTask.workspace.slug,
1268
+ mode: 'pull',
1269
+ server: flags.server,
1270
+ token,
1271
+ }, {
1272
+ requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
1273
+ log: () => {},
1274
+ })
1275
+ ), deps)
1276
+ } catch (error) {
1277
+ completion = {
1278
+ comment: `Knowledge sync pull failed before task execution: ${errorMessage(error)}`,
1279
+ status: 'failed',
1280
+ }
1281
+ }
1282
+ }
1283
+ if (!completion) {
1284
+ executionContext = await prepareRuntimeTask(runtimeTask, flags, deps, config)
1285
+ runtimeTask.execution_context = executionContext
1286
+ await updateRunMeta(executionContext.run_dir, {
1287
+ task_id: String(runtimeTask.id || ''),
1288
+ runtime_id: String(runtimeTask.runtime_id || ''),
1289
+ provider: runtimeTask.runtime?.provider || runtime.provider || '',
1290
+ project_slug: executionContext.project_slug,
1291
+ agent: agentNameForTask(runtimeTask),
1292
+ issue_title: runtimeTask.issue?.title || '',
1293
+ started_at: new Date().toISOString(),
1294
+ })
1295
+ try {
1296
+ completion = await runWithRuntimeHeartbeat(
1297
+ () => handlerModule.handleRuntimeTask(runtimeTask),
1298
+ registration,
1299
+ flags,
1300
+ deps,
1301
+ heartbeatIntervalMs
1302
+ )
1303
+ } catch (error) {
1304
+ completion = {
1305
+ comment: error instanceof Error ? error.message : String(error),
1306
+ status: 'failed',
1307
+ }
1308
+ } finally {
1309
+ await rm(executionContext.tmp_dir, { recursive: true, force: true })
1262
1310
  }
1311
+ }
1312
+ if (executionContext) {
1313
+ await writeRunFile(executionContext.run_dir, 'completion.json', JSON.stringify(normalizeTaskCompletion(runtimeTask, completion), null, 2))
1314
+ await updateRunMeta(executionContext.run_dir, { ended_at: new Date().toISOString() })
1315
+ }
1316
+ if (executionContext) {
1317
+ await appendTaskMemoryDelta({ task: runtimeTask, completion, workdir: executionContext.workdir })
1318
+ }
1263
1319
 
1264
- deps.log(JSON.stringify({ claimed: runtimeTask.id, runtime_id: runtime.id }, null, 2))
1265
- await writeCurrentClaim(deps, runtimeTask, machineKey)
1266
- let completion = null
1267
- let executionContext = null
1268
- if (runtimeTask.workspace?.slug) {
1269
- const token = await projectSyncToken(projectTokenCandidatesForTask(runtimeTask, flags), flags, deps)
1270
- const syncConfig = { ...config, token }
1320
+ if (executionContext && runtimeTask.workspace?.slug) {
1321
+ const isDeepOrganize = Boolean(knowledgeDeepOrganizeSpec(runtimeTask))
1322
+ const syncBack = isDeepOrganize ? deps.mcpKnowledgeSync : deps.syncKnowledge
1323
+ const token = await projectSyncToken(projectTokenCandidatesForTask(runtimeTask, flags), flags, deps)
1324
+ const syncConfig = { ...config, token }
1325
+ const pushKnowledge = () => syncBack({
1326
+ project: runtimeTask.workspace.slug,
1327
+ mode: 'push',
1328
+ server: flags.server,
1329
+ token,
1330
+ }, {
1331
+ requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
1332
+ log: () => {},
1333
+ })
1334
+ if (isDeepOrganize) {
1271
1335
  try {
1272
- await runWithTaskRetry('sync knowledge base', () => (
1273
- deps.syncKnowledge({
1274
- project: runtimeTask.workspace.slug,
1275
- mode: 'pull',
1276
- server: flags.server,
1277
- token,
1278
- }, {
1279
- requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
1280
- log: () => {},
1281
- })
1282
- ), deps)
1336
+ await pushKnowledge()
1283
1337
  } catch (error) {
1338
+ const message = `Knowledge sync push failed before task completion: ${errorMessage(error)}. If a knowledge_snapshot is included in completion, the platform will write it directly.`
1284
1339
  completion = {
1285
- comment: `Knowledge sync pull failed before task execution: ${errorMessage(error)}`,
1286
- status: 'failed',
1340
+ ...(completion && typeof completion === 'object' ? completion : {}),
1341
+ comment: [String(completion?.comment || completion?.summary || '').trim(), message].filter(Boolean).join('\n\n'),
1342
+ memory_delta: [String(completion?.memory_delta || completion?.memoryDelta || '').trim(), message].filter(Boolean).join('\n'),
1287
1343
  }
1288
1344
  }
1289
- }
1290
- if (!completion) {
1291
- executionContext = await prepareRuntimeTask(runtimeTask, flags, deps, config)
1292
- runtimeTask.execution_context = executionContext
1293
- await updateRunMeta(executionContext.run_dir, {
1294
- task_id: String(runtimeTask.id || ''),
1295
- runtime_id: String(runtimeTask.runtime_id || ''),
1296
- provider: runtimeTask.runtime?.provider || runtime.provider || '',
1297
- project_slug: executionContext.project_slug,
1298
- agent: agentNameForTask(runtimeTask),
1299
- issue_title: runtimeTask.issue?.title || '',
1300
- started_at: new Date().toISOString(),
1301
- })
1345
+ } else {
1302
1346
  try {
1303
- completion = await runWithRuntimeHeartbeat(
1304
- () => handlerModule.handleRuntimeTask(runtimeTask),
1305
- registration,
1306
- flags,
1307
- deps,
1308
- heartbeatIntervalMs
1309
- )
1347
+ await runWithTaskRetry('sync knowledge base back to cloud', pushKnowledge, deps)
1310
1348
  } catch (error) {
1349
+ const message = `Knowledge sync push failed before task completion: ${errorMessage(error)}`
1311
1350
  completion = {
1312
- comment: error instanceof Error ? error.message : String(error),
1351
+ ...(completion && typeof completion === 'object' ? completion : {}),
1352
+ comment: [String(completion?.comment || completion?.summary || '').trim(), message].filter(Boolean).join('\n\n'),
1353
+ memory_delta: [String(completion?.memory_delta || completion?.memoryDelta || '').trim(), message].filter(Boolean).join('\n'),
1313
1354
  status: 'failed',
1314
1355
  }
1315
- } finally {
1316
- await rm(executionContext.tmp_dir, { recursive: true, force: true })
1317
1356
  }
1318
1357
  }
1319
- if (executionContext) {
1320
- await writeRunFile(executionContext.run_dir, 'completion.json', JSON.stringify(normalizeTaskCompletion(runtimeTask, completion), null, 2))
1321
- await updateRunMeta(executionContext.run_dir, { ended_at: new Date().toISOString() })
1322
- }
1323
- if (executionContext) {
1324
- await appendTaskMemoryDelta({ task: runtimeTask, completion, workdir: executionContext.workdir })
1325
- }
1358
+ }
1326
1359
 
1327
- if (executionContext && runtimeTask.workspace?.slug) {
1328
- const isDeepOrganize = Boolean(knowledgeDeepOrganizeSpec(runtimeTask))
1329
- const syncBack = isDeepOrganize ? deps.mcpKnowledgeSync : deps.syncKnowledge
1330
- const token = await projectSyncToken(projectTokenCandidatesForTask(runtimeTask, flags), flags, deps)
1331
- const syncConfig = { ...config, token }
1332
- const pushKnowledge = () => syncBack({
1333
- project: runtimeTask.workspace.slug,
1334
- mode: 'push',
1335
- server: flags.server,
1336
- token,
1337
- }, {
1338
- requestJson: (apiPath, options = {}) => deps.requestJson(apiPath, { ...options, config: syncConfig }),
1339
- log: () => {},
1340
- })
1341
- if (isDeepOrganize) {
1342
- try {
1343
- await pushKnowledge()
1344
- } catch (error) {
1345
- const message = `Knowledge sync push failed before task completion: ${errorMessage(error)}. If a knowledge_snapshot is included in completion, the platform will write it directly.`
1346
- completion = {
1347
- ...(completion && typeof completion === 'object' ? completion : {}),
1348
- comment: [String(completion?.comment || completion?.summary || '').trim(), message].filter(Boolean).join('\n\n'),
1349
- memory_delta: [String(completion?.memory_delta || completion?.memoryDelta || '').trim(), message].filter(Boolean).join('\n'),
1350
- }
1351
- }
1352
- } else {
1353
- try {
1354
- await runWithTaskRetry('sync knowledge base back to cloud', pushKnowledge, deps)
1355
- } catch (error) {
1356
- const message = `Knowledge sync push failed before task completion: ${errorMessage(error)}`
1357
- completion = {
1358
- ...(completion && typeof completion === 'object' ? completion : {}),
1359
- comment: [String(completion?.comment || completion?.summary || '').trim(), message].filter(Boolean).join('\n\n'),
1360
- memory_delta: [String(completion?.memory_delta || completion?.memoryDelta || '').trim(), message].filter(Boolean).join('\n'),
1361
- status: 'failed',
1362
- }
1363
- }
1364
- }
1365
- }
1360
+ const body = normalizeTaskCompletion(runtimeTask, completion)
1361
+ const result = await runWithDaemonRetry('complete runtime task', () => (
1362
+ deps.requestJson('/api/runtime/tasks/complete', {
1363
+ method: 'POST',
1364
+ body,
1365
+ config,
1366
+ })
1367
+ ), deps, retryState)
1368
+ await clearCurrentClaim(deps)
1369
+ deps.log(JSON.stringify(result, null, 2))
1370
+ return true
1371
+ }
1366
1372
 
1367
- const body = normalizeTaskCompletion(runtimeTask, completion)
1368
- const result = await runWithDaemonRetry('complete runtime task', () => (
1369
- deps.requestJson('/api/runtime/tasks/complete', {
1370
- method: 'POST',
1371
- body,
1372
- config,
1373
- })
1374
- ), deps, retryState)
1375
- await clearCurrentClaim(deps)
1376
- deps.log(JSON.stringify(result, null, 2))
1377
- handled += 1
1378
- }
1373
+ async function claimAndRunRuntimeTasks(registration, flags, deps, handlerModule, retryState = createRetryState(), heartbeatIntervalMs = 15000, maxConcurrent = 1) {
1374
+ if (!handlerModule) return 0
1375
+ const runtimes = (registration?.runtimes || []).filter(r => r?.id)
1376
+ if (!runtimes.length) return 0
1377
+ const config = configFromFlags(flags)
1378
+ const machineKey = registration?.machine?.machine_key || machineOverride(flags) || ''
1379
1379
 
1380
- return handled
1380
+ const slots = Array.from({ length: maxConcurrent }, (_, i) => runtimes[i % runtimes.length])
1381
+ const results = await Promise.allSettled(
1382
+ slots.map(runtime => runOneRuntimeTaskSlot(runtime, config, machineKey, registration, flags, deps, handlerModule, retryState, heartbeatIntervalMs))
1383
+ )
1384
+ return results.filter(r => r.status === 'fulfilled' && r.value === true).length
1381
1385
  }
1382
1386
 
1383
1387
  export async function startRuntimeDaemon(flags = {}, deps = {}) {
@@ -1386,6 +1390,7 @@ export async function startRuntimeDaemon(flags = {}, deps = {}) {
1386
1390
  const scanIntervalMs = Number(flag(flags, 'scan-interval', '60')) * 1000
1387
1391
  const taskIntervalMs = Number(flag(flags, 'task-interval', flag(flags, 'heartbeat-interval', '15'))) * 1000
1388
1392
  const projectRefreshIntervalMs = Number(flag(flags, 'project-refresh-interval', '1800')) * 1000
1393
+ const maxConcurrent = Math.max(1, Number(flag(flags, 'concurrency', '1')) || 1)
1389
1394
  const once = Boolean(flags.once)
1390
1395
  const handlerPath = flag(flags, 'handler')
1391
1396
 
@@ -1413,7 +1418,7 @@ export async function startRuntimeDaemon(flags = {}, deps = {}) {
1413
1418
  'Runtime task failed locally: daemon restarted with a persisted claimed task that had not completed.'
1414
1419
  ), resolvedDeps, retryState)
1415
1420
  await syncRuntimeProjectMetadataBestEffort(flags, resolvedDeps)
1416
- await claimAndRunRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs)
1421
+ await claimAndRunRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs, maxConcurrent)
1417
1422
  if (once) return
1418
1423
 
1419
1424
  let lastScan = Date.now()
@@ -1432,7 +1437,7 @@ export async function startRuntimeDaemon(flags = {}, deps = {}) {
1432
1437
  lastHeartbeat = now
1433
1438
  }
1434
1439
  if (now - lastTaskPoll >= taskIntervalMs) {
1435
- await claimAndRunRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs)
1440
+ await claimAndRunRuntimeTasks(registration, flags, resolvedDeps, handlerModule, retryState, heartbeatIntervalMs, maxConcurrent)
1436
1441
  lastTaskPoll = now
1437
1442
  }
1438
1443
  if (now - lastProjectRefresh >= projectRefreshIntervalMs) {