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
13 changes: 12 additions & 1 deletion ds-bot/src/host/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,20 @@ export function install(rt) {
rt.deleteBot = async (id, ...rest) => {
// Fires after removal; the payload keeps the Bot's last record, fields included.
const last = view(rt.botOf(id))
const remoteChat = rt.botOf(id)?.remote !== undefined
const remote = rt.botOf(id)?.remote
const remoteChat = remote !== undefined
const result = await deleteBot(id, ...rest)
if (!remoteChat) fire('bot.removed', { ...last, botId: id, bot: last })
// Deleting a friend's Bot's chat clears it; the friend's Bot is still there, so it gets a
// fresh, empty chat at once. Otherwise its row waits ("opening its chat…") until the
// plugin next publishes its roster, which may be hours or the next restart.
else if (typeof remote?.handle === 'string' && typeof remote.plugin === 'string') {
const published = (state.flybot?.remote?.[remote.plugin] ?? []).find(bot => bot.handle === remote.handle)
if (published !== undefined) {
await rt.remoteChats?.open({ handle: published.handle, name: published.name, home: published.home, homeName: published.homeName, plugin: remote.plugin })
.catch(error => warn('flybot: chat for %s not reopened: %s', remote.handle, error))
}
}
return result
}
}
Expand Down
25 changes: 25 additions & 0 deletions ds-bot/test/flybot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,28 @@ test('tools (#4): a tool visibleTo hides is taken out of that Bot\'s tool list,
await new Promise(resolve => setTimeout(resolve, 20))
assert.deepEqual(masks.at(-1), ['c', 'lifted'])
})

test('deleting a friend\'s Bot\'s chat reopens a fresh one at once, so its row never waits on "opening its chat…"', async (t) => {
const { ctx, fb, call, stop } = await start()
t.after(stop)
// The relay route serves the friend's Bot, as remote-chat.js's adapter does in DSH.
ctx.llm.listProviders = () => [{ id: 'flybot-remote', name: 'Friends\' Bots' }]
ctx.llm.listModels = async () => [{ id: '@helper.friend.home', name: '助手' }]
let sessions = 0
ctx.sessionController.create = async () => ({ sessionId: `remote-${++sessions}` })
const social = fb.scope('social')
social.remote.publish([{ handle: '@helper.friend.home', name: '助手', home: '@friend.home' }])
const until = async (check) => { for (let i = 0; i < 100 && !check(); i++) await new Promise(resolve => setTimeout(resolve, 10)) }
await until(() => social.remote.chats.of('@helper.friend.home'))
const first = social.remote.chats.of('@helper.friend.home')
assert.ok(first)
const deleted = await call('delete-bot', { id: first })
assert.equal(deleted.ok, true)
// No new publish: the plugin's roster is unchanged and cached.
await until(() => social.remote.chats.of('@helper.friend.home'))
const again = social.remote.chats.list().find(chat => chat.handle === '@helper.friend.home')
assert.ok(again, 'the chat is back without a new publish')
assert.notEqual(again.id, first)
assert.equal(again.name, '助手')
assert.equal(again.plugin, 'social')
})
Loading