Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/349-agent-inbox.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
An **Inbox** row at the top of the sidebar opens one list of your own Bots' work: **Needs you** (tool approvals, held messages, tasks waiting for you or failed), **In progress** (running turns and tasks) and **Done** (the last 7 days). Each card has one clear status and an **Open** button to its chat; tool approvals from a group chat can be answered right there with the same buttons as in the chat ([#349](https://github.com/ThinkFlowLab/FlyBot/issues/349)).
3,960 changes: 2,177 additions & 1,783 deletions core/client.js

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions core/src/client/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,31 @@ const ZH = {
'Update FlyBot core {version} first.': '先更新 FlyBot 核心 {version}。',
'Open updates': '打开更新',

// Agent inbox (#349)
Inbox: '收件箱',
'Inbox, {count} need you': '收件箱,{count} 项需要你',
'Close inbox': '关闭收件箱',
'Your Bots': '你的 Bot',
'Needs you': '需要你',
'In progress': '进行中',
'Nothing needs you right now.': '现在没有需要你处理的事。',
'Nothing is running.': '没有正在进行的事。',
'Nothing finished in the last 7 days.': '最近 7 天没有完成的事。',
'Waiting to send': '等待发送',
'Needs your answer': '等你回答',
'Result ready': '结果已就绪',
Failed: '失败',
Cancelled: '已取消',
'A tool call': '一次工具调用',
'1 message waits for your approval': '1 条消息等你批准',
'{count} messages wait for your approval': '{count} 条消息等你批准',
'Approve it in the chat': '请在对话里批准',
'Untitled task': '未命名任务',
'for {time}': '已 {time}',
'just started': '刚开始',
'Open {name}': '打开 {name}',
'Allowed once for {name}': '已为 {name} 允许一次',
'Rejected for {name}': '已拒绝 {name} 的请求',
// Command palette
Messages: '消息',
Commands: '命令',
Expand Down
329 changes: 329 additions & 0 deletions core/src/client/inbox.js

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion core/src/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { GROUP_SETTINGS_CSS } from './group-settings.js'
import { FOLDER_CSS } from './folder-menu.js'
import { USAGE_CSS } from './usage-page.js'
import { SCHEDULE_CSS } from './schedules.js'
import { INBOX_CSS } from './inbox.js'
import { STORAGE, migrateStorage } from './storage.js'
import { friendLineNews } from './friend-lines.js'
import { groupsFor, readMode, surfaceGroups, writeMode } from './surface.js'
Expand Down Expand Up @@ -489,14 +490,17 @@ export function apply(ctx) {
closeSettings: () => { ui.set(state => (state.settings || state.remoteBotSettings ? { ...state, settings: null, remoteBotSettings: null } : state)) },
openMemory: (botId) => { ui.set(state => ({ ...state, memory: botId, menu: null, palette: null })) },
closeMemory: () => { ui.set(state => (state.memory ? { ...state, memory: null } : state)) },
// The Agent inbox (#349) opens over the conversation; Open on a card closes it first.
openInbox: () => { ui.set(state => ({ ...state, inbox: true, menu: null, palette: null, newChat: null })) },
closeInbox: () => { ui.set(state => (state.inbox ? { ...state, inbox: null } : state)) },
openConnectors: () => { ui.set(state => ({ ...state, settings: { page: 'connectors', id: null }, menu: null, palette: null, newChat: null })) },
// Agent mode hands the surface back to the shell; everything open on the Bot
// surface closes first so nothing stale reopens on the way back.
setSurface: (mode) => {
if (mode !== 'bot' && mode !== 'agent') return
if (surface.getSnapshot().mode === mode) return
if (mode === 'agent') {
ui.set(state => ({ ...state, details: null, view: 'details', settings: null, remoteBotSettings: null, palette: null, newChat: null, menu: null, voice: null, exchange: null, memory: null, toast: null }))
ui.set(state => ({ ...state, details: null, view: 'details', settings: null, remoteBotSettings: null, palette: null, newChat: null, menu: null, voice: null, exchange: null, memory: null, inbox: null, toast: null }))
}
writeMode(mode)
surface.set(state => ({ ...state, mode }))
Expand Down Expand Up @@ -595,6 +599,7 @@ export function apply(ctx) {
approveCapability: name => after(call('capability-approve', { name })),
setBotConnector: (botId, connector, enabled, scope = 'owner') => after(call('bot-connectors', { botId, connector, enabled, scope })),
setBotCapability: (botId, name, attached) => after(call('bot-capability', { botId, name, attached })),
tasks: () => call('tasks'),
schedules: () => call('schedules'),
scheduleCreate: task => call('schedule-create', task),
scheduleUpdate: task => call('schedule-update', task),
Expand Down Expand Up @@ -836,6 +841,7 @@ export function apply(ctx) {
installCompiledStyles(MODEL_CSS + FOLDER_CSS + BOT_SETTINGS_CSS + GROUP_SETTINGS_CSS + REMOTE_BOT_SETTINGS_CSS, 'models'),
installCompiledStyles(USAGE_CSS, 'usage'),
installCompiledStyles(SCHEDULE_CSS, 'schedules'),
installCompiledStyles(INBOX_CSS, 'inbox'),
installCompiledStyles(FEEDBACK_CSS, 'feedback'),
installCompiledStyles(ABOUT_CSS, 'about'),
installCompiledStyles(FLYBOT_CSS, 'flybot'),
Expand Down
5 changes: 4 additions & 1 deletion core/src/client/overlays.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ContextMenu } from './context-menu.js'
import { PANEL_LINGER_MS, BotPanel } from './browser-pane.js'
import { MemoryDialog } from './memory-panel.js'
import { RemoteBotSettingsSheet } from './remote-bot-settings.js'
import { InboxDialog } from './inbox.js'
import { useLinger } from './motion.js'

// How long a closing dialog or menu stays for its exit (see their [data-leaving] CSS).
Expand Down Expand Up @@ -41,6 +42,7 @@ export function Overlays({ useRoster, useUi, useSessions, useSessionStatus, useA
const [remoteBotSettings, remoteLeaving] = useLinger(remoteBotId, DIALOG_EXIT_MS)
const [memory, memoryLeaving] = useLinger(ui.memory && roster.byId[ui.memory] ? ui.memory : null, DIALOG_EXIT_MS)
const [menu, menuLeaving] = useLinger(ui.menu, MENU_EXIT_MS)
const [inbox, inboxLeaving] = useLinger(ui.inbox ?? null, DIALOG_EXIT_MS)
return h(Fragment, null,
pane ? createPortal(h(BotPanel, { pane, leaving: open === null, roster, actions, useSessions, useSessionStatus, useActivity, useWork, useBrowserTabs, useBrowserPages }), document.body) : null,
ui.newChat ? createPortal(h(NewChat, { key: ui.newChat, mode: ui.newChat, roster, actions }), document.body) : null,
Expand All @@ -50,5 +52,6 @@ export function Overlays({ useRoster, useUi, useSessions, useSessionStatus, useA
menu ? createPortal(h(ContextMenu, { key: `${menu.kind ?? ''}:${menu.id}:${menu.x}:${menu.y}`, menu, roster, actions, leaving: menuLeaving }), document.body) : null,
settings ? createPortal(h(SettingsDialog, { page: settings.page, id: settings.id, roster, actions, useUpdate, leaving: settingsLeaving }), document.body) : null,
remoteBotSettings ? createPortal(h(RemoteBotSettingsSheet, { key: remoteBotSettings, bot: roster.byId[remoteBotSettings], roster, actions, leaving: remoteLeaving }), document.body) : null,
memory && roster.byId[memory] ? createPortal(h(MemoryDialog, { key: memory, bot: roster.byId[memory], actions, leaving: memoryLeaving }), document.body) : null)
memory && roster.byId[memory] ? createPortal(h(MemoryDialog, { key: memory, bot: roster.byId[memory], actions, leaving: memoryLeaving }), document.body) : null,
inbox ? createPortal(h(InboxDialog, { roster, useWork, useSessions, actions, leaving: inboxLeaving }), document.body) : null)
}
2 changes: 2 additions & 0 deletions core/src/client/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { springEasing, SPRING_SHAPE } from './motion.js'
import { hostText, t } from './i18n.js'
import { cleanPeerName, ownerNameOf, remoteBotTitle, sharedRoomTitle, isListedRemoteChat } from '../shared/remote-label.js'
import { showsDshRow } from './dsh-offer.js'
import { InboxRow } from './inbox.js'

// -------------------------------------------------------------------------
// Sidebar
Expand Down Expand Up @@ -383,6 +384,7 @@ export function TeamSidebar(props) {
h(UpdateButton, { useUpdate: props.useUpdate, actions }),
h('button', { type: 'button', className: 'bt-round', 'aria-label': t('New…'), title: t('New…'), onClick: () => actions.openNewChat('new') }, h(PlusIcon))),
wide && roster.readOnly ? h('div', { className: 'bt-hint bt-read-only', role: 'status' }, hostText(roster.readOnly)) : null,
h(InboxRow, { wide, useRoster, useWork: props.useWork, useUi, actions }),
h('div', { ref: listRef, className: 'bt-list', role: 'region', 'aria-label': t('Bot list') },
tree ? h('div', { className: 'bt-tree', role: 'group', 'aria-label': t('Bot tree') }, tree.map(node => h(TreeNode, { key: node.bot.id, node, rowProps, markedUnread, collapsed, toggle, actions: structure?.nodeActions })))
: main ? h(BotRow, { ...rowProps, bot: main, marked: markedUnread[main.id] === true }) : roster.ready ? null : h('div', { className: 'bt-hint' }, t('Setting up your Main Bot…')),
Expand Down
Loading
Loading