@11agents/cli 0.1.20 → 0.1.22

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@11agents/cli",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "11agents local runtime and telemetry CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'node:child_process'
2
2
  import { createHash } from 'node:crypto'
3
3
  import { appendFileSync, readFileSync } from 'node:fs'
4
- import { appendFile, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'
4
+ import { appendFile, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises'
5
5
  import os from 'node:os'
6
6
  import { dirname, resolve } from 'node:path'
7
7
  import path from 'node:path'
@@ -312,6 +312,63 @@ function normalizeTaskCompletion(task, completion) {
312
312
  return body
313
313
  }
314
314
 
315
+ function runtimeAssetContentType(filePath) {
316
+ const ext = path.extname(filePath).toLowerCase()
317
+ if (ext === '.html') return 'text/html; charset=utf-8'
318
+ if (ext === '.png') return 'image/png'
319
+ if (ext === '.webp') return 'image/webp'
320
+ if (ext === '.jpg' || ext === '.jpeg') return 'image/jpeg'
321
+ if (ext === '.mp4') return 'video/mp4'
322
+ if (ext === '.md') return 'text/markdown; charset=utf-8'
323
+ if (ext === '.json') return 'application/json; charset=utf-8'
324
+ return 'application/octet-stream'
325
+ }
326
+
327
+ function isInsideDir(filePath, dir) {
328
+ if (!filePath || !dir) return false
329
+ const relative = path.relative(resolve(dir), resolve(filePath))
330
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))
331
+ }
332
+
333
+ function localMarkdownLinkTargets(text) {
334
+ const targets = new Set()
335
+ const linkPattern = /!?\[[^\]]*\]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/g
336
+ for (const match of String(text || '').matchAll(linkPattern)) {
337
+ const raw = String(match[1] || '').trim()
338
+ if (!raw || /^https?:\/\//i.test(raw) || raw.startsWith('/api/')) continue
339
+ let decoded = raw
340
+ try { decoded = decodeURI(raw) } catch {}
341
+ if (path.isAbsolute(decoded)) targets.add(decoded)
342
+ }
343
+ return [...targets]
344
+ }
345
+
346
+ async function attachRuntimeAssetsToCompletion(body, task, executionContext) {
347
+ if (!executionContext || !body?.comment) return body
348
+ const allowedDirs = [executionContext.agent_results_dir, executionContext.run_dir].filter(Boolean)
349
+ const projectSlug = projectSlugForTask(task)
350
+ const assets = []
351
+ let nextComment = String(body.comment || '')
352
+ for (const target of localMarkdownLinkTargets(nextComment)) {
353
+ if (!allowedDirs.some(dir => isInsideDir(target, dir))) continue
354
+ const fileStat = await stat(target).catch(() => null)
355
+ if (!fileStat?.isFile()) continue
356
+ const bytes = await readFile(target)
357
+ const assetHash = createHash('sha256').update(target).digest('hex')
358
+ const url = `/api/projects/${encodeURIComponent(projectSlug)}/assets/${assetHash}`
359
+ nextComment = nextComment.split(target).join(url)
360
+ assets.push({
361
+ original_path: target,
362
+ asset_hash: assetHash,
363
+ filename: path.basename(target),
364
+ content_type: runtimeAssetContentType(target),
365
+ size_bytes: fileStat.size,
366
+ body_base64: bytes.toString('base64'),
367
+ })
368
+ }
369
+ return assets.length ? { ...body, comment: nextComment, assets } : body
370
+ }
371
+
315
372
  function failedClaimCompletionBody(claim, comment) {
316
373
  return {
317
374
  task_id: String(claim?.task_id || ''),
@@ -992,6 +1049,9 @@ function buildCodexPrompt(task) {
992
1049
  '4. tmp_dir: Scratch/working files only. This directory is deleted by the CLI after the task finishes — do NOT put deliverables here.',
993
1050
  '5. workdir: Treat as read-only project context except for the four agent directories and knowledge_base above.',
994
1051
  '',
1052
+ 'MCP tool guidance:',
1053
+ '- If the task needs image generation, use the hosted 11agents MCP `image_generate` tool when available.',
1054
+ '',
995
1055
  'Task context:',
996
1056
  compactJson({
997
1057
  queue_event: task.queue_event,
@@ -999,7 +1059,6 @@ function buildCodexPrompt(task) {
999
1059
  issue: task.issue,
1000
1060
  trigger_summary: task.trigger_summary,
1001
1061
  thread_memory: task.thread_memory,
1002
- project_knowledge: task.project_knowledge,
1003
1062
  }),
1004
1063
  '',
1005
1064
  'Assigned agent context:',
@@ -1011,7 +1070,6 @@ function buildCodexPrompt(task) {
1011
1070
  memory: task.agent?.memory,
1012
1071
  permissions: task.agent?.permissions,
1013
1072
  triggers: task.agent?.triggers,
1014
- skills: task.agent?.skills,
1015
1073
  }),
1016
1074
  '',
1017
1075
  'Thread comments:',
@@ -1384,7 +1442,11 @@ async function runOneRuntimeTaskSlot(runtime, config, machineKey, registration,
1384
1442
  }
1385
1443
  }
1386
1444
 
1387
- const body = normalizeTaskCompletion(runtimeTask, completion)
1445
+ const body = await attachRuntimeAssetsToCompletion(
1446
+ normalizeTaskCompletion(runtimeTask, completion),
1447
+ runtimeTask,
1448
+ executionContext
1449
+ )
1388
1450
  const result = await runWithDaemonRetry('complete runtime task', () => (
1389
1451
  deps.requestJson('/api/runtime/tasks/complete', {
1390
1452
  method: 'POST',