@swarmclawai/swarmclaw 0.7.0 → 0.7.2

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.
Files changed (119) hide show
  1. package/README.md +85 -139
  2. package/package.json +1 -1
  3. package/src/app/api/agents/[id]/thread/route.ts +1 -2
  4. package/src/app/api/agents/route.ts +1 -1
  5. package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
  6. package/src/app/api/{sessions → chats}/[id]/main-loop/route.ts +2 -2
  7. package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
  8. package/src/app/api/{sessions → chats}/[id]/route.ts +4 -52
  9. package/src/app/api/{sessions → chats}/route.ts +5 -7
  10. package/src/app/api/plugins/route.ts +3 -0
  11. package/src/app/api/plugins/settings/route.ts +35 -0
  12. package/src/app/api/usage/route.ts +30 -0
  13. package/src/cli/index.js +35 -33
  14. package/src/cli/index.ts +40 -39
  15. package/src/cli/spec.js +29 -27
  16. package/src/components/agents/agent-card.tsx +1 -1
  17. package/src/components/agents/agent-chat-list.tsx +3 -3
  18. package/src/components/agents/agent-list.tsx +8 -13
  19. package/src/components/agents/agent-sheet.tsx +2 -2
  20. package/src/components/agents/cron-job-form.tsx +3 -3
  21. package/src/components/agents/inspector-panel.tsx +2 -2
  22. package/src/components/auth/setup-wizard.tsx +5 -38
  23. package/src/components/chat/chat-area.tsx +10 -14
  24. package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +3 -3
  25. package/src/components/chat/chat-header.tsx +156 -73
  26. package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +4 -5
  27. package/src/components/chat/chat-tool-toggles.tsx +26 -17
  28. package/src/components/chat/checkpoint-timeline.tsx +4 -4
  29. package/src/components/chat/message-bubble.tsx +4 -1
  30. package/src/components/chat/message-list.tsx +2 -2
  31. package/src/components/{sessions/new-session-sheet.tsx → chat/new-chat-sheet.tsx} +6 -6
  32. package/src/components/chat/session-debug-panel.tsx +1 -1
  33. package/src/components/chat/tool-request-banner.tsx +3 -3
  34. package/src/components/chatrooms/agent-hover-card.tsx +3 -3
  35. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +2 -2
  36. package/src/components/connectors/connector-sheet.tsx +1 -1
  37. package/src/components/home/home-view.tsx +1 -1
  38. package/src/components/layout/app-layout.tsx +23 -2
  39. package/src/components/plugins/plugin-list.tsx +475 -254
  40. package/src/components/plugins/plugin-sheet.tsx +124 -10
  41. package/src/components/settings/gateway-connection-panel.tsx +1 -1
  42. package/src/components/shared/command-palette.tsx +0 -1
  43. package/src/components/shared/settings/section-heartbeat.tsx +1 -1
  44. package/src/components/shared/settings/section-providers.tsx +1 -1
  45. package/src/components/shared/settings/settings-page.tsx +1 -12
  46. package/src/components/usage/metrics-dashboard.tsx +73 -0
  47. package/src/components/webhooks/webhook-sheet.tsx +1 -1
  48. package/src/lib/chat.ts +1 -1
  49. package/src/lib/{sessions.ts → chats.ts} +28 -18
  50. package/src/lib/providers/claude-cli.ts +1 -1
  51. package/src/lib/server/approvals.ts +4 -4
  52. package/src/lib/server/capability-router.ts +10 -8
  53. package/src/lib/server/chat-execution.ts +36 -105
  54. package/src/lib/server/chatroom-helpers.ts +3 -3
  55. package/src/lib/server/connectors/manager.ts +4 -4
  56. package/src/lib/server/cost.ts +34 -1
  57. package/src/lib/server/daemon-state.ts +2 -2
  58. package/src/lib/server/heartbeat-service.ts +1 -1
  59. package/src/lib/server/main-agent-loop.ts +25 -160
  60. package/src/lib/server/main-session.ts +6 -13
  61. package/src/lib/server/orchestrator-lg.ts +3 -3
  62. package/src/lib/server/orchestrator.ts +5 -5
  63. package/src/lib/server/plugins.ts +112 -4
  64. package/src/lib/server/provider-health.ts +5 -3
  65. package/src/lib/server/queue.ts +12 -10
  66. package/src/lib/server/session-run-manager.test.ts +9 -6
  67. package/src/lib/server/session-run-manager.ts +1 -3
  68. package/src/lib/server/session-tools/calendar.ts +376 -0
  69. package/src/lib/server/session-tools/canvas.ts +1 -1
  70. package/src/lib/server/session-tools/chatroom.ts +4 -2
  71. package/src/lib/server/session-tools/connector.ts +5 -2
  72. package/src/lib/server/session-tools/context.ts +7 -3
  73. package/src/lib/server/session-tools/crud.ts +14 -6
  74. package/src/lib/server/session-tools/delegate.ts +95 -8
  75. package/src/lib/server/session-tools/discovery.ts +2 -2
  76. package/src/lib/server/session-tools/edit_file.ts +4 -2
  77. package/src/lib/server/session-tools/email.ts +322 -0
  78. package/src/lib/server/session-tools/file.ts +5 -2
  79. package/src/lib/server/session-tools/git.ts +1 -1
  80. package/src/lib/server/session-tools/http.ts +1 -1
  81. package/src/lib/server/session-tools/image-gen.ts +382 -0
  82. package/src/lib/server/session-tools/index.ts +74 -49
  83. package/src/lib/server/session-tools/memory.ts +139 -2
  84. package/src/lib/server/session-tools/monitor.ts +1 -1
  85. package/src/lib/server/session-tools/openclaw-nodes.ts +1 -1
  86. package/src/lib/server/session-tools/openclaw-workspace.ts +1 -1
  87. package/src/lib/server/session-tools/platform.ts +6 -3
  88. package/src/lib/server/session-tools/plugin-creator.ts +3 -3
  89. package/src/lib/server/session-tools/replicate.ts +303 -0
  90. package/src/lib/server/session-tools/sample-ui.ts +1 -1
  91. package/src/lib/server/session-tools/sandbox.ts +4 -2
  92. package/src/lib/server/session-tools/schedule.ts +4 -2
  93. package/src/lib/server/session-tools/session-info.ts +7 -4
  94. package/src/lib/server/session-tools/shell.ts +5 -2
  95. package/src/lib/server/session-tools/subagent.ts +2 -2
  96. package/src/lib/server/session-tools/wallet.ts +29 -2
  97. package/src/lib/server/session-tools/web.ts +51 -6
  98. package/src/lib/server/storage.ts +29 -9
  99. package/src/lib/server/stream-agent-chat.ts +72 -249
  100. package/src/lib/server/tool-aliases.ts +26 -15
  101. package/src/lib/server/tool-capability-policy.test.ts +9 -9
  102. package/src/lib/server/tool-capability-policy.ts +32 -27
  103. package/src/lib/tool-definitions.ts +4 -0
  104. package/src/lib/validation/schemas.ts +3 -1
  105. package/src/stores/use-app-store.ts +5 -5
  106. package/src/stores/use-chat-store.ts +7 -7
  107. package/src/types/index.ts +65 -3
  108. /package/src/app/api/{sessions → chats}/[id]/browser/route.ts +0 -0
  109. /package/src/app/api/{sessions → chats}/[id]/chat/route.ts +0 -0
  110. /package/src/app/api/{sessions → chats}/[id]/clear/route.ts +0 -0
  111. /package/src/app/api/{sessions → chats}/[id]/deploy/route.ts +0 -0
  112. /package/src/app/api/{sessions → chats}/[id]/devserver/route.ts +0 -0
  113. /package/src/app/api/{sessions → chats}/[id]/edit-resend/route.ts +0 -0
  114. /package/src/app/api/{sessions → chats}/[id]/fork/route.ts +0 -0
  115. /package/src/app/api/{sessions → chats}/[id]/mailbox/route.ts +0 -0
  116. /package/src/app/api/{sessions → chats}/[id]/messages/route.ts +0 -0
  117. /package/src/app/api/{sessions → chats}/[id]/retry/route.ts +0 -0
  118. /package/src/app/api/{sessions → chats}/[id]/stop/route.ts +0 -0
  119. /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
@@ -1,5 +1,6 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import { getPluginManager } from '@/lib/server/plugins'
3
+ import { notify } from '@/lib/server/ws-hub'
3
4
 
4
5
  // Ensure all builtin plugins are registered by importing their modules
5
6
  import '@/lib/server/session-tools/shell'
@@ -44,6 +45,7 @@ export async function POST(req: Request) {
44
45
 
45
46
  const manager = getPluginManager()
46
47
  manager.setEnabled(filename, enabled)
48
+ notify('plugins')
47
49
 
48
50
  return NextResponse.json({ ok: true })
49
51
  }
@@ -59,6 +61,7 @@ export async function DELETE(req: Request) {
59
61
  if (!deleted) {
60
62
  return NextResponse.json({ error: 'Cannot delete built-in or non-existent plugin' }, { status: 400 })
61
63
  }
64
+ notify('plugins')
62
65
  return NextResponse.json({ ok: true })
63
66
  }
64
67
 
@@ -0,0 +1,35 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { loadSettings, saveSettings } from '@/lib/server/storage'
3
+
4
+ export const dynamic = 'force-dynamic'
5
+
6
+ /** GET /api/plugins/settings?pluginId=X — read per-plugin settings */
7
+ export async function GET(req: Request) {
8
+ const { searchParams } = new URL(req.url)
9
+ const pluginId = searchParams.get('pluginId')
10
+ if (!pluginId) {
11
+ return NextResponse.json({ error: 'pluginId required' }, { status: 400 })
12
+ }
13
+
14
+ const settings = loadSettings()
15
+ const pluginSettings = (settings.pluginSettings as Record<string, Record<string, unknown>> | undefined) ?? {}
16
+ return NextResponse.json(pluginSettings[pluginId] ?? {})
17
+ }
18
+
19
+ /** PUT /api/plugins/settings?pluginId=X — write per-plugin settings */
20
+ export async function PUT(req: Request) {
21
+ const { searchParams } = new URL(req.url)
22
+ const pluginId = searchParams.get('pluginId')
23
+ if (!pluginId) {
24
+ return NextResponse.json({ error: 'pluginId required' }, { status: 400 })
25
+ }
26
+
27
+ const body = await req.json() as Record<string, unknown>
28
+ const settings = loadSettings()
29
+ const pluginSettings = (settings.pluginSettings as Record<string, Record<string, unknown>> | undefined) ?? {}
30
+ pluginSettings[pluginId] = body
31
+ settings.pluginSettings = pluginSettings
32
+ saveSettings(settings)
33
+
34
+ return NextResponse.json({ ok: true })
35
+ }
@@ -50,6 +50,7 @@ export async function GET(req: Request) {
50
50
  let totalCost = 0
51
51
  const byAgent: Record<string, { name: string; cost: number; tokens: number; count: number }> = {}
52
52
  const byProvider: Record<string, { tokens: number; cost: number }> = {}
53
+ const byPlugin: Record<string, { definitionTokens: number; invocationTokens: number; invocations: number; estimatedCost: number }> = {}
53
54
  const bucketMap: Record<string, { tokens: number; cost: number }> = {}
54
55
 
55
56
  for (const r of records) {
@@ -75,6 +76,26 @@ export async function GET(req: Request) {
75
76
  byAgent[agentId].tokens += tokens
76
77
  byAgent[agentId].count += 1
77
78
 
79
+ // by plugin — definition costs (context overhead per LLM call)
80
+ if (Array.isArray(r.pluginDefinitionCosts)) {
81
+ for (const dc of r.pluginDefinitionCosts) {
82
+ if (!dc.pluginId) continue
83
+ if (!byPlugin[dc.pluginId]) byPlugin[dc.pluginId] = { definitionTokens: 0, invocationTokens: 0, invocations: 0, estimatedCost: 0 }
84
+ byPlugin[dc.pluginId].definitionTokens += dc.estimatedTokens || 0
85
+ }
86
+ }
87
+
88
+ // by plugin — invocation costs (actual tool calls)
89
+ if (Array.isArray(r.pluginInvocations)) {
90
+ for (const inv of r.pluginInvocations) {
91
+ if (!inv.pluginId) continue
92
+ if (!byPlugin[inv.pluginId]) byPlugin[inv.pluginId] = { definitionTokens: 0, invocationTokens: 0, invocations: 0, estimatedCost: 0 }
93
+ const p = byPlugin[inv.pluginId]
94
+ p.invocationTokens += (inv.inputTokens || 0) + (inv.outputTokens || 0)
95
+ p.invocations += 1
96
+ }
97
+ }
98
+
78
99
  // time series bucketing
79
100
  const bk = bucketKey(r.timestamp || now, range)
80
101
  if (!bucketMap[bk]) bucketMap[bk] = { tokens: 0, cost: 0 }
@@ -82,6 +103,14 @@ export async function GET(req: Request) {
82
103
  bucketMap[bk].cost += cost
83
104
  }
84
105
 
106
+ // Estimate per-plugin cost using the average input token rate from total usage
107
+ if (totalTokens > 0 && totalCost > 0) {
108
+ const avgCostPerToken = totalCost / totalTokens
109
+ for (const p of Object.values(byPlugin)) {
110
+ p.estimatedCost = Math.round((p.definitionTokens + p.invocationTokens) * avgCostPerToken * 10000) / 10000
111
+ }
112
+ }
113
+
85
114
  // Sort time series
86
115
  const timeSeries = Object.entries(bucketMap)
87
116
  .sort(([a], [b]) => a.localeCompare(b))
@@ -144,6 +173,7 @@ export async function GET(req: Request) {
144
173
  totalCost: Math.round(totalCost * 10000) / 10000,
145
174
  byAgent,
146
175
  byProvider,
176
+ byPlugin,
147
177
  timeSeries,
148
178
  providerHealth,
149
179
  })
package/src/cli/index.js CHANGED
@@ -345,6 +345,8 @@ const COMMAND_GROUPS = [
345
345
  cmd('update', 'PATCH', '/plugins', 'Update a plugin (use --query id=plugin.js or --query all=true)'),
346
346
  cmd('install', 'POST', '/plugins/install', 'Install plugin from URL', { expectsJsonBody: true }),
347
347
  cmd('marketplace', 'GET', '/plugins/marketplace', 'Get marketplace catalog'),
348
+ cmd('settings-get', 'GET', '/plugins/settings', 'Get plugin settings (use --query pluginId=plugin_name)'),
349
+ cmd('settings-set', 'PUT', '/plugins/settings', 'Set plugin settings (use --query pluginId=plugin_name and --data JSON)', { expectsJsonBody: true }),
348
350
  cmd('ui', 'GET', '/plugins/ui', 'List plugin UI extensions (use --query type=sidebar|header|chat_actions|connectors)'),
349
351
  ],
350
352
  },
@@ -369,7 +371,7 @@ const COMMAND_GROUPS = [
369
371
  name: 'search',
370
372
  description: 'Global search across app resources',
371
373
  commands: [
372
- cmd('query', 'GET', '/search', 'Search agents/tasks/sessions/schedules/webhooks/skills (use --query q=term)'),
374
+ cmd('query', 'GET', '/search', 'Search agents/tasks/chats/schedules/webhooks/skills (use --query q=term)'),
373
375
  ],
374
376
  },
375
377
  {
@@ -404,56 +406,56 @@ const COMMAND_GROUPS = [
404
406
  ],
405
407
  },
406
408
  {
407
- name: 'sessions',
408
- description: 'Manage chat sessions and runtime controls',
409
+ name: 'chats',
410
+ description: 'Manage agent chats and runtime controls',
409
411
  commands: [
410
- cmd('list', 'GET', '/sessions', 'List sessions'),
411
- cmd('get', 'GET', '/sessions/:id', 'Get session by id', { virtual: true, clientGetRoute: '/sessions' }),
412
- cmd('create', 'POST', '/sessions', 'Create session', { expectsJsonBody: true }),
413
- cmd('update', 'PUT', '/sessions/:id', 'Update session', { expectsJsonBody: true }),
414
- cmd('delete', 'DELETE', '/sessions/:id', 'Delete session'),
415
- cmd('delete-many', 'DELETE', '/sessions', 'Delete multiple sessions (body: {"ids":[...]})', { expectsJsonBody: true }),
416
- cmd('heartbeat-disable-all', 'POST', '/sessions/heartbeat', 'Disable all session heartbeats and cancel queued heartbeat runs', {
412
+ cmd('list', 'GET', '/chats', 'List chats'),
413
+ cmd('get', 'GET', '/chats/:id', 'Get chat by id', { virtual: true, clientGetRoute: '/chats' }),
414
+ cmd('create', 'POST', '/chats', 'Create chat', { expectsJsonBody: true }),
415
+ cmd('update', 'PUT', '/chats/:id', 'Update chat', { expectsJsonBody: true }),
416
+ cmd('delete', 'DELETE', '/chats/:id', 'Delete chat'),
417
+ cmd('delete-many', 'DELETE', '/chats', 'Delete multiple chats (body: {"ids":[...]})', { expectsJsonBody: true }),
418
+ cmd('heartbeat-disable-all', 'POST', '/chats/heartbeat', 'Disable all chat heartbeats and cancel queued heartbeat runs', {
417
419
  expectsJsonBody: true,
418
420
  defaultBody: { action: 'disable_all' },
419
421
  }),
420
- cmd('messages', 'GET', '/sessions/:id/messages', 'Get session messages'),
421
- cmd('messages-update', 'PUT', '/sessions/:id/messages', 'Update session message metadata (e.g. bookmark)', { expectsJsonBody: true }),
422
- cmd('messages-send', 'POST', '/sessions/:id/messages', 'Append a user/system message to a session', { expectsJsonBody: true }),
423
- cmd('messages-delete', 'DELETE', '/sessions/:id/messages', 'Delete a message from a session', { expectsJsonBody: true }),
424
- cmd('fork', 'POST', '/sessions/:id/fork', 'Fork session from a specific message index', { expectsJsonBody: true }),
425
- cmd('edit-resend', 'POST', '/sessions/:id/edit-resend', 'Edit and resend from a specific message index', { expectsJsonBody: true }),
426
- cmd('main-loop', 'GET', '/sessions/:id/main-loop', 'Get main mission loop state'),
427
- cmd('main-loop-action', 'POST', '/sessions/:id/main-loop', 'Control main mission loop (pause/resume/set_goal/set_mode/clear_events/nudge)', {
422
+ cmd('messages', 'GET', '/chats/:id/messages', 'Get chat messages'),
423
+ cmd('messages-update', 'PUT', '/chats/:id/messages', 'Update chat message metadata (e.g. bookmark)', { expectsJsonBody: true }),
424
+ cmd('messages-send', 'POST', '/chats/:id/messages', 'Append a user/system message to a chat', { expectsJsonBody: true }),
425
+ cmd('messages-delete', 'DELETE', '/chats/:id/messages', 'Delete a message from a chat', { expectsJsonBody: true }),
426
+ cmd('fork', 'POST', '/chats/:id/fork', 'Fork chat from a specific message index', { expectsJsonBody: true }),
427
+ cmd('edit-resend', 'POST', '/chats/:id/edit-resend', 'Edit and resend from a specific message index', { expectsJsonBody: true }),
428
+ cmd('main-loop', 'GET', '/chats/:id/main-loop', 'Get main mission loop state'),
429
+ cmd('main-loop-action', 'POST', '/chats/:id/main-loop', 'Control main mission loop (pause/resume/set_goal/set_mode/clear_events/nudge)', {
428
430
  expectsJsonBody: true,
429
431
  }),
430
- cmd('chat', 'POST', '/sessions/:id/chat', 'Send chat message (streaming)', {
432
+ cmd('chat', 'POST', '/chats/:id/chat', 'Send chat message (streaming)', {
431
433
  expectsJsonBody: true,
432
434
  responseType: 'sse',
433
435
  }),
434
- cmd('stop', 'POST', '/sessions/:id/stop', 'Stop session run(s)'),
435
- cmd('clear', 'POST', '/sessions/:id/clear', 'Clear session messages'),
436
- cmd('browser-status', 'GET', '/sessions/:id/browser', 'Check browser status'),
437
- cmd('browser-close', 'DELETE', '/sessions/:id/browser', 'Close browser session'),
438
- cmd('mailbox', 'GET', '/sessions/:id/mailbox', 'List session mailbox envelopes'),
439
- cmd('mailbox-action', 'POST', '/sessions/:id/mailbox', 'Send/ack/clear mailbox envelopes', { expectsJsonBody: true }),
440
- cmd('retry', 'POST', '/sessions/:id/retry', 'Retry last assistant message'),
441
- cmd('deploy', 'POST', '/sessions/:id/deploy', 'Deploy current session branch', { expectsJsonBody: true }),
442
- cmd('devserver', 'POST', '/sessions/:id/devserver', 'Dev server action via JSON body', { expectsJsonBody: true }),
443
- cmd('devserver-start', 'POST', '/sessions/:id/devserver', 'Start session dev server', {
436
+ cmd('stop', 'POST', '/chats/:id/stop', 'Stop chat run(s)'),
437
+ cmd('clear', 'POST', '/chats/:id/clear', 'Clear chat messages'),
438
+ cmd('browser-status', 'GET', '/chats/:id/browser', 'Check browser status'),
439
+ cmd('browser-close', 'DELETE', '/chats/:id/browser', 'Close browser'),
440
+ cmd('mailbox', 'GET', '/chats/:id/mailbox', 'List chat mailbox envelopes'),
441
+ cmd('mailbox-action', 'POST', '/chats/:id/mailbox', 'Send/ack/clear mailbox envelopes', { expectsJsonBody: true }),
442
+ cmd('retry', 'POST', '/chats/:id/retry', 'Retry last assistant message'),
443
+ cmd('deploy', 'POST', '/chats/:id/deploy', 'Deploy current chat branch', { expectsJsonBody: true }),
444
+ cmd('devserver', 'POST', '/chats/:id/devserver', 'Dev server action via JSON body', { expectsJsonBody: true }),
445
+ cmd('devserver-start', 'POST', '/chats/:id/devserver', 'Start chat dev server', {
444
446
  expectsJsonBody: true,
445
447
  defaultBody: { action: 'start' },
446
448
  }),
447
- cmd('devserver-stop', 'POST', '/sessions/:id/devserver', 'Stop session dev server', {
449
+ cmd('devserver-stop', 'POST', '/chats/:id/devserver', 'Stop chat dev server', {
448
450
  expectsJsonBody: true,
449
451
  defaultBody: { action: 'stop' },
450
452
  }),
451
- cmd('devserver-status', 'POST', '/sessions/:id/devserver', 'Check session dev server status', {
453
+ cmd('devserver-status', 'POST', '/chats/:id/devserver', 'Check chat dev server status', {
452
454
  expectsJsonBody: true,
453
455
  defaultBody: { action: 'status' },
454
456
  }),
455
- cmd('checkpoints', 'GET', '/sessions/:id/checkpoints', 'List checkpoint history for a session'),
456
- cmd('restore', 'POST', '/sessions/:id/restore', 'Restore session to a previous checkpoint', { expectsJsonBody: true }),
457
+ cmd('checkpoints', 'GET', '/chats/:id/checkpoints', 'List checkpoint history for a chat'),
458
+ cmd('restore', 'POST', '/chats/:id/restore', 'Restore chat to a previous checkpoint', { expectsJsonBody: true }),
457
459
  ],
458
460
  },
459
461
  {
package/src/cli/index.ts CHANGED
@@ -657,27 +657,28 @@ export function buildProgram(): Command {
657
657
  await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'GET', `/runs/${encodeURIComponent(id)}`))
658
658
  })
659
659
 
660
- const sessions = program.command('sessions').description('Manage sessions')
660
+ const chats = program.command('chats').description('Manage chats')
661
+ program.command('sessions').description('Manage chats (alias)').action(() => chats.help())
661
662
 
662
- sessions
663
+ chats
663
664
  .command('list')
664
- .description('List sessions')
665
+ .description('List chats')
665
666
  .action(async function () {
666
- await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'GET', '/sessions'))
667
+ await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'GET', '/chats'))
667
668
  })
668
669
 
669
- sessions
670
+ chats
670
671
  .command('get')
671
- .description('Get session by id')
672
- .argument('<id>', 'Session id')
672
+ .description('Get chat by id')
673
+ .argument('<id>', 'Chat id')
673
674
  .action(async function (id: string) {
674
- await runWithHandler(this as Command, (ctx) => resolveByIdFromCollection(ctx, '/sessions', id))
675
+ await runWithHandler(this as Command, (ctx) => resolveByIdFromCollection(ctx, '/chats', id))
675
676
  })
676
677
 
677
- sessions
678
+ chats
678
679
  .command('create')
679
- .description('Create session')
680
- .option('--name <name>', 'Session name', 'New Session')
680
+ .description('Create chat')
681
+ .option('--name <name>', 'Chat name', 'New Chat')
681
682
  .option('--user <user>', 'User name')
682
683
  .option('--cwd <cwd>', 'Working directory')
683
684
  .option('--provider <provider>', 'Provider id')
@@ -706,7 +707,7 @@ export function buildProgram(): Command {
706
707
  if (opts.heartbeatIntervalSec && (!Number.isFinite(heartbeatIntervalSec) || heartbeatIntervalSec! < 0)) {
707
708
  throw new Error(`Invalid --heartbeat-interval-sec value: ${opts.heartbeatIntervalSec}`)
708
709
  }
709
- await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'POST', '/sessions', compactObject({
710
+ await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'POST', '/chats', compactObject({
710
711
  name: opts.name,
711
712
  user: opts.user,
712
713
  cwd: opts.cwd,
@@ -720,14 +721,14 @@ export function buildProgram(): Command {
720
721
  })))
721
722
  })
722
723
 
723
- sessions
724
+ chats
724
725
  .command('update')
725
- .description('Update session')
726
- .argument('<id>', 'Session id')
727
- .option('--name <name>', 'Session name')
726
+ .description('Update chat')
727
+ .argument('<id>', 'Chat id')
728
+ .option('--name <name>', 'Chat name')
728
729
  .option('--cwd <cwd>', 'Working directory')
729
730
  .option('--agent-id <agentId>', 'Agent id')
730
- .option('--tools <json>', 'Tools JSON array, e.g. ["shell","memory"]')
731
+ .option('--plugins <json>', 'Plugins JSON array, e.g. ["shell","memory"]')
731
732
  .option('--heartbeat-enabled <heartbeatEnabled>', 'Heartbeat enabled (true|false)')
732
733
  .option('--heartbeat-interval-sec <heartbeatIntervalSec>', 'Heartbeat interval seconds')
733
734
  .action(async function (
@@ -736,7 +737,7 @@ export function buildProgram(): Command {
736
737
  name?: string
737
738
  cwd?: string
738
739
  agentId?: string
739
- tools?: string
740
+ plugins?: string
740
741
  heartbeatEnabled?: string
741
742
  heartbeatIntervalSec?: string
742
743
  },
@@ -748,40 +749,40 @@ export function buildProgram(): Command {
748
749
  if (opts.heartbeatIntervalSec && (!Number.isFinite(heartbeatIntervalSec) || heartbeatIntervalSec! < 0)) {
749
750
  throw new Error(`Invalid --heartbeat-interval-sec value: ${opts.heartbeatIntervalSec}`)
750
751
  }
751
- const tools = parseJsonValue(opts.tools, '--tools')
752
- if (tools !== undefined && !Array.isArray(tools)) {
753
- throw new Error('--tools must be a JSON array')
752
+ const plugins = parseJsonValue(opts.plugins, '--plugins')
753
+ if (plugins !== undefined && !Array.isArray(plugins)) {
754
+ throw new Error('--plugins must be a JSON array')
754
755
  }
755
- await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'PUT', `/sessions/${encodeURIComponent(id)}`, compactObject({
756
+ await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'PUT', `/chats/${encodeURIComponent(id)}`, compactObject({
756
757
  name: opts.name,
757
758
  cwd: opts.cwd,
758
759
  agentId: opts.agentId,
759
- tools,
760
+ plugins,
760
761
  heartbeatEnabled: typeof opts.heartbeatEnabled === 'string' ? heartbeatEnabled : undefined,
761
762
  heartbeatIntervalSec,
762
763
  })))
763
764
  })
764
765
 
765
- sessions
766
+ chats
766
767
  .command('delete')
767
- .description('Delete session')
768
- .argument('<id>', 'Session id')
768
+ .description('Delete chat')
769
+ .argument('<id>', 'Chat id')
769
770
  .action(async function (id: string) {
770
- await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'DELETE', `/sessions/${encodeURIComponent(id)}`))
771
+ await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'DELETE', `/chats/${encodeURIComponent(id)}`))
771
772
  })
772
773
 
773
- sessions
774
+ chats
774
775
  .command('history')
775
- .description('Get session message history')
776
- .argument('<id>', 'Session id')
776
+ .description('Get chat message history')
777
+ .argument('<id>', 'Chat id')
777
778
  .action(async function (id: string) {
778
- await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'GET', `/sessions/${encodeURIComponent(id)}/messages`))
779
+ await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'GET', `/chats/${encodeURIComponent(id)}/messages`))
779
780
  })
780
781
 
781
- sessions
782
+ chats
782
783
  .command('mailbox')
783
- .description('List session mailbox envelopes')
784
- .argument('<id>', 'Session id')
784
+ .description('List chat mailbox envelopes')
785
+ .argument('<id>', 'Chat id')
785
786
  .option('--limit <limit>', 'Max envelopes to return (default: 50)')
786
787
  .option('--include-acked', 'Include acknowledged envelopes')
787
788
  .action(async function (
@@ -799,16 +800,16 @@ export function buildProgram(): Command {
799
800
  const params = new URLSearchParams()
800
801
  if (typeof limit === 'number') params.set('limit', String(limit))
801
802
  if (opts.includeAcked) params.set('includeAcked', '1')
802
- return apiRequest(ctx, 'GET', `/sessions/${encodeURIComponent(id)}/mailbox`, undefined, params)
803
+ return apiRequest(ctx, 'GET', `/chats/${encodeURIComponent(id)}/mailbox`, undefined, params)
803
804
  })
804
805
  })
805
806
 
806
- sessions
807
+ chats
807
808
  .command('stop')
808
- .description('Stop running work for a session')
809
- .argument('<id>', 'Session id')
809
+ .description('Stop running work for a chat')
810
+ .argument('<id>', 'Chat id')
810
811
  .action(async function (id: string) {
811
- await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'POST', `/sessions/${encodeURIComponent(id)}/stop`))
812
+ await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'POST', `/chats/${encodeURIComponent(id)}/stop`))
812
813
  })
813
814
 
814
815
  const memory = program.command('memory').description('Manage memory')
package/src/cli/spec.js CHANGED
@@ -228,6 +228,8 @@ const COMMAND_GROUPS = {
228
228
  update: { description: 'Enable/disable plugin (body: {"filename":"x.js","enabled":true})', method: 'POST', path: '/plugins' },
229
229
  marketplace: { description: 'Get plugin marketplace registry', method: 'GET', path: '/plugins/marketplace' },
230
230
  install: { description: 'Install plugin by URL', method: 'POST', path: '/plugins/install' },
231
+ 'settings-get': { description: 'Read plugin settings (supports --query pluginId=...)', method: 'GET', path: '/plugins/settings' },
232
+ 'settings-set': { description: 'Write plugin settings (supports --query pluginId=... and --data JSON)', method: 'PUT', path: '/plugins/settings' },
231
233
  },
232
234
  },
233
235
  providers: {
@@ -249,7 +251,7 @@ const COMMAND_GROUPS = {
249
251
  search: {
250
252
  description: 'Global search across app resources',
251
253
  commands: {
252
- query: { description: 'Search agents/tasks/sessions/schedules/webhooks/skills (supports --query q=term)', method: 'GET', path: '/search' },
254
+ query: { description: 'Search agents/tasks/chats/schedules/webhooks/skills (supports --query q=term)', method: 'GET', path: '/search' },
253
255
  },
254
256
  },
255
257
  schedules: {
@@ -273,33 +275,33 @@ const COMMAND_GROUPS = {
273
275
  delete: { description: 'Delete secret', method: 'DELETE', path: '/secrets/:id', params: ['id'] },
274
276
  },
275
277
  },
276
- sessions: {
277
- description: 'Interactive chat sessions',
278
+ chats: {
279
+ description: 'Agent chats',
278
280
  commands: {
279
- list: { description: 'List sessions', method: 'GET', path: '/sessions' },
280
- create: { description: 'Create session', method: 'POST', path: '/sessions' },
281
- get: { description: 'Get session by id (from list)', virtualGet: true, collectionPath: '/sessions', params: ['id'] },
282
- update: { description: 'Update session fields', method: 'PUT', path: '/sessions/:id', params: ['id'] },
283
- delete: { description: 'Delete one session', method: 'DELETE', path: '/sessions/:id', params: ['id'] },
284
- 'delete-many': { description: 'Delete multiple sessions (body: {"ids":[...]})', method: 'DELETE', path: '/sessions' },
285
- 'heartbeat-disable-all': { description: 'Disable all session heartbeats and cancel queued heartbeat runs', method: 'POST', path: '/sessions/heartbeat' },
286
- messages: { description: 'Get session message history', method: 'GET', path: '/sessions/:id/messages', params: ['id'] },
287
- 'messages-update': { description: 'Update session message metadata (e.g. bookmark)', method: 'PUT', path: '/sessions/:id/messages', params: ['id'] },
288
- 'messages-send': { description: 'Append a user/system message to a session', method: 'POST', path: '/sessions/:id/messages', params: ['id'] },
289
- 'messages-delete': { description: 'Delete a message from a session', method: 'DELETE', path: '/sessions/:id/messages', params: ['id'] },
290
- fork: { description: 'Fork session from a specific message index', method: 'POST', path: '/sessions/:id/fork', params: ['id'] },
291
- 'edit-resend': { description: 'Edit and resend from a specific message index', method: 'POST', path: '/sessions/:id/edit-resend', params: ['id'] },
292
- 'main-loop': { description: 'Get main mission loop state for a session', method: 'GET', path: '/sessions/:id/main-loop', params: ['id'] },
293
- 'main-loop-action': { description: 'Control main mission loop (pause/resume/set_goal/set_mode/clear_events/nudge)', method: 'POST', path: '/sessions/:id/main-loop', params: ['id'] },
294
- chat: { description: 'Send chat message (SSE stream)', method: 'POST', path: '/sessions/:id/chat', params: ['id'], stream: true, waitable: true },
295
- stop: { description: 'Cancel active/running session work', method: 'POST', path: '/sessions/:id/stop', params: ['id'] },
296
- clear: { description: 'Clear session history', method: 'POST', path: '/sessions/:id/clear', params: ['id'] },
297
- mailbox: { description: 'List mailbox envelopes for a session', method: 'GET', path: '/sessions/:id/mailbox', params: ['id'] },
298
- 'mailbox-action': { description: 'Send/ack/clear mailbox envelopes', method: 'POST', path: '/sessions/:id/mailbox', params: ['id'] },
299
- deploy: { description: 'Deploy session workspace git changes', method: 'POST', path: '/sessions/:id/deploy', params: ['id'] },
300
- devserver: { description: 'Start/stop/status dev server (body: {"action":"start|stop|status"})', method: 'POST', path: '/sessions/:id/devserver', params: ['id'] },
301
- browser: { description: 'Check browser runtime for session', method: 'GET', path: '/sessions/:id/browser', params: ['id'] },
302
- 'browser-clear': { description: 'Close browser runtime for session', method: 'DELETE', path: '/sessions/:id/browser', params: ['id'] },
281
+ list: { description: 'List chats', method: 'GET', path: '/chats' },
282
+ create: { description: 'Create chat', method: 'POST', path: '/chats' },
283
+ get: { description: 'Get chat by id (from list)', virtualGet: true, collectionPath: '/chats', params: ['id'] },
284
+ update: { description: 'Update chat fields', method: 'PUT', path: '/chats/:id', params: ['id'] },
285
+ delete: { description: 'Delete one chat', method: 'DELETE', path: '/chats/:id', params: ['id'] },
286
+ 'delete-many': { description: 'Delete multiple chats (body: {"ids":[...]})', method: 'DELETE', path: '/chats' },
287
+ 'heartbeat-disable-all': { description: 'Disable all chat heartbeats and cancel queued heartbeat runs', method: 'POST', path: '/chats/heartbeat' },
288
+ messages: { description: 'Get chat message history', method: 'GET', path: '/chats/:id/messages', params: ['id'] },
289
+ 'messages-update': { description: 'Update chat message metadata (e.g. bookmark)', method: 'PUT', path: '/chats/:id/messages', params: ['id'] },
290
+ 'messages-send': { description: 'Append a user/system message to a chat', method: 'POST', path: '/chats/:id/messages', params: ['id'] },
291
+ 'messages-delete': { description: 'Delete a message from a chat', method: 'DELETE', path: '/chats/:id/messages', params: ['id'] },
292
+ fork: { description: 'Fork chat from a specific message index', method: 'POST', path: '/chats/:id/fork', params: ['id'] },
293
+ 'edit-resend': { description: 'Edit and resend from a specific message index', method: 'POST', path: '/chats/:id/edit-resend', params: ['id'] },
294
+ 'main-loop': { description: 'Get main mission loop state for a chat', method: 'GET', path: '/chats/:id/main-loop', params: ['id'] },
295
+ 'main-loop-action': { description: 'Control main mission loop (pause/resume/set_goal/set_mode/clear_events/nudge)', method: 'POST', path: '/chats/:id/main-loop', params: ['id'] },
296
+ chat: { description: 'Send chat message (SSE stream)', method: 'POST', path: '/chats/:id/chat', params: ['id'], stream: true, waitable: true },
297
+ stop: { description: 'Cancel active/running chat work', method: 'POST', path: '/chats/:id/stop', params: ['id'] },
298
+ clear: { description: 'Clear chat history', method: 'POST', path: '/chats/:id/clear', params: ['id'] },
299
+ mailbox: { description: 'List mailbox envelopes for a chat', method: 'GET', path: '/chats/:id/mailbox', params: ['id'] },
300
+ 'mailbox-action': { description: 'Send/ack/clear mailbox envelopes', method: 'POST', path: '/chats/:id/mailbox', params: ['id'] },
301
+ deploy: { description: 'Deploy chat workspace git changes', method: 'POST', path: '/chats/:id/deploy', params: ['id'] },
302
+ devserver: { description: 'Start/stop/status dev server (body: {"action":"start|stop|status"})', method: 'POST', path: '/chats/:id/devserver', params: ['id'] },
303
+ browser: { description: 'Check browser runtime for chat', method: 'GET', path: '/chats/:id/browser', params: ['id'] },
304
+ 'browser-clear': { description: 'Close browser runtime for chat', method: 'DELETE', path: '/chats/:id/browser', params: ['id'] },
303
305
  },
304
306
  },
305
307
  settings: {
@@ -220,7 +220,7 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
220
220
  <div className="text-[12px] text-text-3/70 mt-1.5 truncate">{agent.description}</div>
221
221
  <div className="flex items-center gap-2 mt-1.5">
222
222
  <span className="text-[11px] text-text-3/60 font-mono">{agent.model || agent.provider}</span>
223
- {agent.tools?.includes('browser') && (
223
+ {agent.plugins?.includes('browser') && (
224
224
  <span className="text-[10px] font-600 uppercase tracking-wider text-sky-400/70 bg-sky-400/[0.08] px-1.5 py-0.5 rounded-[5px]">
225
225
  browser
226
226
  </span>
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } fr
4
4
  import { useAppStore } from '@/stores/use-app-store'
5
5
  import { useChatStore } from '@/stores/use-chat-store'
6
6
  import { useChatroomStore } from '@/stores/use-chatroom-store'
7
- import { fetchMessages } from '@/lib/sessions'
7
+ import { fetchMessages } from '@/lib/chats'
8
8
  import { api } from '@/lib/api-client'
9
9
  import { ConfirmDialog } from '@/components/shared/confirm-dialog'
10
10
  import type { Agent, Session } from '@/types'
@@ -58,7 +58,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
58
58
  .filter(Boolean) as string[]
59
59
  if (!sessionIds.length) { toast.error('No chats to delete'); return }
60
60
  try {
61
- await api('DELETE', '/sessions', { ids: sessionIds })
61
+ await api('DELETE', '/chats', { ids: sessionIds })
62
62
  await loadSessions()
63
63
  toast.success(`Deleted ${sessionIds.length} chat(s)`)
64
64
  setBulkMode(false)
@@ -262,7 +262,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
262
262
  const threadSession = agent.threadSessionId ? sessions[agent.threadSessionId] as Session | undefined : undefined
263
263
  const lastMsg = threadSession?.messages?.at(-1)
264
264
  const isActive = currentAgentId === agent.id
265
- const heartbeatOn = agent.heartbeatEnabled === true && (agent.tools?.length ?? 0) > 0
265
+ const heartbeatOn = agent.heartbeatEnabled === true && (agent.plugins?.length ?? 0) > 0
266
266
  const recentlyActive = (threadSession?.lastActiveAt ?? 0) > Date.now() - 30 * 60 * 1000
267
267
  const isWorking = runningAgentIds.has(agent.id) || (threadSession?.active ?? false) || heartbeatOn || recentlyActive || chatroomActiveAgentIds.has(agent.id)
268
268
  const isTyping = streamingSessionId === agent.threadSessionId
@@ -2,7 +2,6 @@
2
2
 
3
3
  import { useEffect, useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react'
4
4
  import { useAppStore } from '@/stores/use-app-store'
5
- import { api } from '@/lib/api-client'
6
5
  import { AgentCard } from './agent-card'
7
6
  import { TrashList } from './trash-list'
8
7
  import { useApprovalStore } from '@/stores/use-approval-store'
@@ -17,8 +16,6 @@ export function AgentList({ inSidebar }: Props) {
17
16
  const agents = useAppStore((s) => s.agents)
18
17
  const loadAgents = useAppStore((s) => s.loadAgents)
19
18
  const sessions = useAppStore((s) => s.sessions)
20
- const currentUser = useAppStore((s) => s.currentUser)
21
- const loadSessions = useAppStore((s) => s.loadSessions)
22
19
  const setAgentSheetOpen = useAppStore((s) => s.setAgentSheetOpen)
23
20
  const activeProjectFilter = useAppStore((s) => s.activeProjectFilter)
24
21
  const showTrash = useAppStore((s) => s.showTrash)
@@ -37,19 +34,17 @@ export function AgentList({ inSidebar }: Props) {
37
34
  const currentSession = currentSessionId ? sessions[currentSessionId] : null
38
35
  const selectedAgentId = currentSession?.agentId
39
36
 
40
- const mainSession = useMemo(() =>
41
- Object.values(sessions).find((s: any) => s.name === '__main__' && s.user === currentUser),
42
- [sessions, currentUser]
43
- )
44
- const defaultAgentId = mainSession?.agentId || 'default'
37
+ const appSettings = useAppStore((s) => s.appSettings)
38
+ const updateSettings = useAppStore((s) => s.updateSettings)
39
+ const defaultAgentId = (appSettings.defaultAgentId && agents[appSettings.defaultAgentId])
40
+ ? appSettings.defaultAgentId
41
+ : Object.values(agents)[0]?.id || 'default'
45
42
 
46
43
  const handleSetDefault = useCallback(async (agentId: string) => {
47
- if (!mainSession) return
48
44
  try {
49
- await api('PUT', `/sessions/${mainSession.id}`, { agentId })
50
- await loadSessions()
45
+ await updateSettings({ defaultAgentId: agentId })
51
46
  } catch { /* ignore */ }
52
- }, [mainSession, loadSessions])
47
+ }, [updateSettings])
53
48
 
54
49
  const [loaded, setLoaded] = useState(Object.keys(agents).length > 0)
55
50
  useEffect(() => { loadAgents().then(() => setLoaded(true)) }, [])
@@ -75,7 +70,7 @@ export function AgentList({ inSidebar }: Props) {
75
70
  const ids = new Set<string>()
76
71
  const recentThreshold = now - 30 * 60 * 1000
77
72
  for (const a of Object.values(agents)) {
78
- if (a.heartbeatEnabled === true && (a.tools?.length ?? 0) > 0) { ids.add(a.id); continue }
73
+ if (a.heartbeatEnabled === true && (a.plugins?.length ?? 0) > 0) { ids.add(a.id); continue }
79
74
  // Check if any session for this agent was active in the last 30 minutes
80
75
  for (const s of Object.values(sessions)) {
81
76
  if (s.agentId === a.id && (s.lastActiveAt ?? 0) > recentThreshold) { ids.add(a.id); break }
@@ -183,7 +183,7 @@ export function AgentSheet() {
183
183
  setApiEndpoint(editing.apiEndpoint || null)
184
184
  setIsOrchestrator(editing.isOrchestrator || false)
185
185
  setAgentAgentIds(editing.subAgentIds || [])
186
- setTools(editing.tools || [])
186
+ setTools(editing.plugins || [])
187
187
  setSkills(editing.skills || [])
188
188
  setSkillIds(editing.skillIds || [])
189
189
  setMcpServerIds(editing.mcpServerIds || [])
@@ -782,7 +782,7 @@ export function AgentSheet() {
782
782
  </div>
783
783
  </div>
784
784
  )}
785
- <p className="text-[11px] text-text-3/70 mt-1.5">Periodic check-in runs on idle sessions using this agent. Processes pending events and monitors status.</p>
785
+ <p className="text-[11px] text-text-3/70 mt-1.5">Periodic check-in runs on idle chats using this agent. Processes pending events and monitors status.</p>
786
786
  </div>
787
787
 
788
788
  {/* Spend Limits */}
@@ -94,10 +94,10 @@ export function CronJobForm({ agentId, onSaved, onCancel }: Props) {
94
94
  </select>
95
95
  </div>
96
96
  <div>
97
- <label className="block text-[11px] font-600 uppercase tracking-wider text-text-3/50 mb-1">Session</label>
97
+ <label className="block text-[11px] font-600 uppercase tracking-wider text-text-3/50 mb-1">Chat Target</label>
98
98
  <select value={sessionTarget} onChange={(e) => setSessionTarget(e.target.value as typeof sessionTarget)} className={inputClass}>
99
- <option value="main">Main session</option>
100
- <option value="isolated">Isolated session</option>
99
+ <option value="main">Main chat</option>
100
+ <option value="isolated">Isolated chat</option>
101
101
  </select>
102
102
  </div>
103
103
  </div>
@@ -171,11 +171,11 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
171
171
  </div>
172
172
  </div>
173
173
  )}
174
- {agent.tools && agent.tools.length > 0 && (
174
+ {agent.plugins && agent.plugins.length > 0 && (
175
175
  <div>
176
176
  <label className="block text-[11px] font-600 uppercase tracking-wider text-text-3/50 mb-1">Plugins</label>
177
177
  <div className="flex flex-wrap gap-1">
178
- {agent.tools.map((tool) => (
178
+ {agent.plugins.map((tool) => (
179
179
  <span key={tool} className="px-2 py-0.5 rounded-[6px] text-[11px] font-600 bg-sky-400/[0.08] text-sky-400/70">
180
180
  {tool}
181
181
  </span>